在項目的進展中使用了Gallery控件展示相冊,由于Gallery控件已經被谷歌棄用,所以建議大家考慮使用新的控件來代替,及時跟上潮流。不過本篇的重點不是討論被棄用的Gallery控件,而是來討論怎么使用Gallery控件實現類似ListView控件的刷新和加載功能,因為項目里面使用的是PullToRefresh的庫,但是該庫好像并不支持Gallery的刷新和加載功能,所以只得自己參考別人的寫法來實現該功能了。本文提供一種實現思路,希望可以幫到大家。(源碼附在文后,歡迎大家下載,不要分!!)
先看下效果圖(用的是在線GIF生成工具):
![]()
下面開始正文。首先講一下實現的思路:Gallery是橫向滑動的控件,我們類似ListView實現刷新一樣,當滑動到第0項時候,開始刷新操作,滑動到最后一項的時候進行分頁加載的操作。
順著這個思路,我們開始編碼了:首先是一頭一尾兩個界面,刷新界面和加載更多的界面,兩個界面布局類似,就只貼一個布局文件了。
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <PRogressBar style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="124dp" android:id="@+id/progressBarLoadMore" /> <TextView android:text="@string/loadMore" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_below="@+id/progressBarLoadMore" android:layout_marginTop="30dp" android:textSize="24sp" android:layout_centerHorizontal="true" android:id="@+id/textView2" /></RelativeLayout>寫完之后大概長這個樣子,當然你可以根據自己的喜好去定制
原來的Gallery控件肯定不能實現我們想要的效果,所以下面我們來定制自己的Gallery,對原來的Galley稍稍動一些手腳改造一下。接著上代碼:
public class MyGallery extends Gallery implements android.widget.AdapterView.OnItemSelectedListener{ private IGalleryEventListener mIGalleryEventListener; // 監聽Gallery滑動 private View refreshView ; // 刷新的緩沖界面 private View loadMoreView ; // 加載更多緩沖的界面 private int startIndex = 0 ; // 記錄首項的索引 private int endIndex = 0 ; // 記錄最后一項的索引 public MyGallery(Context context) { super(context); this.setOnItemSelectedListener(this); // 設置監聽器 } public MyGallery(Context context, AttributeSet attrs) { super(context, attrs); this.setOnItemSelectedListener(this); // 設置監聽器 } public MyGallery(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.setOnItemSelectedListener(this); // 設置監聽器 } /** * 對外提供設置監聽器的方法 * @param iGalleryEventListener Gallery的滑動監聽器 */ public void setGalleryEventListerner(IGalleryEventListener iGalleryEventListener){ this.mIGalleryEventListener = iGalleryEventListener; } /** * 刷新或加載結束的回調方法 */ public void onCompleted(){ if(refreshView != null && refreshView.isShown()){ refreshView.setVisibility(View.GONE); // 刷新頁面不可見 this.setSelection(startIndex+1); // 選中刷新頁面的后一項 } if(loadMoreView != null && loadMoreView.isShown()){ loadMoreView.setVisibility(View.GONE); // 加載更多頁面不可見 if(this.getChildAt(endIndex) != null ) this.setSelection(endIndex) ; // 有數據就選中剛剛加載的一項 else this.setSelection(endIndex-1); // 否則就選中加載更多頁面的前面一項 } } @Override public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { startIndex = 0 ; // 首項 endIndex = adapterView.getCount() - 1 ; // 最后一項 if(position == startIndex ){ // 表示滑動到了最前面一頁,刷新操作 refreshView = view ; // 刷新頁面保存 if(!refreshView.isShown()) refreshView.setVisibility(View.VISIBLE); mIGalleryEventListener.onRefresh(); // 回調刷新的方法 } if(position == endIndex){ // 表示滑動到了最后一頁,加載操作 loadMoreView = view ; if(!loadMoreView.isShown()) loadMoreView.setVisibility(View.VISIBLE); mIGalleryEventListener.onLoadMore(); // 回調加載的方法 } } @Override public void onNothingSelected(AdapterView<?> adapterView) { }}注釋已經寫得很清楚了,紅色部分是我們自己定義的一個接口,有兩個待實現的方法(加載更多和刷新),紅色部分的方法給當前的Gallery提供設置這個接口的方法,后面要用到。藍色部分是結束時候的回調方法,紫色部分是當前的Gallery實現了OnItemSelectedListener接口的一些處理,當滑動到最前面一頁的時候讓刷新界面出現,當滑動到最后一頁的時候讓加載更多的界面出現。public interface IGalleryEventListener { public void onLoadMore(); // 加載更多的回調函數 public void onRefresh(); // 刷新的回調函數}上面是監聽的接口,包含兩個待實現的方法,后面再要使用的地方實現該接口即可。下面寫一下Gallery的適配器public class MyGalleryAdapter extends BaseAdapter { private Context mContext ; // 運行的上下文 private List<PictureBean> mPictureList ; // 實體類數據集合 private LayoutInflater mInfalter ; // 界面渲染器 private View refreshView, loadMoreView; // 一頭一尾 public MyGalleryAdapter(){ } /** * 構造方法 * @param context 上下文 * @param pictureList 實體類數據集合 */ public MyGalleryAdapter(Context context , List<PictureBean> pictureList){ this.mContext = context ; this.mPictureList = pictureList ; } @Override public int getCount() { return mPictureList.size() + 2; // 這里多加了兩個是因為我們多加了兩個界面,分別是加載和刷新的界面,在數據集合中一頭一尾 } @Override public PictureBean getItem(int position) { if(position==0 || position== getCount()-1) return null; return mPictureList.get(position-1) ; } @Override public long getItemId(int i) { return i; } @Override public View getView(int position, View view, ViewGroup viewGroup) { mInfalter = LayoutInflater.from(mContext); if(position == 0 ){ // 第一頁 refreshView = mInfalter.inflate(R.layout.refresh_layout ,null); refreshView.setVisibility(View.GONE); // 默認設置刷新頁面不可見 return refreshView; // 返回刷新頁面 }else if(position == this.getCount() - 1){ // 最后一頁 loadMoreView = mInfalter.inflate(R.layout.loadmore_layout ,null); loadMoreView.setVisibility(View.GONE); // 默認設置加載更多不可見 return loadMoreView; // 返回加載更多頁面 }else{ if(view == null) view = mInfalter.inflate(R.layout.content_layout, null); ImageView imageView = ViewHolder.get(view,R.id.image); TextView describeTv = ViewHolder.get(view,R.id.describeTv); imageView.setImageResource(getItem(position).getPictureResId()); // 設置相片資源ID describeTv.setText(getItem(position).getPictureName()); // 設置相片名稱 return view ; // 返回內容頁面 } }}上面代碼注意紅色加粗的部分,在原來的數據集合長度上在加上2,表示一頭一尾各加了一個布局占位。藍色部分也很好理解,初始的時候讓一頭一尾的兩個界面都隱藏起來,該顯示的時候才讓其顯示。
這里有一個R.layout.content_layout的布局,如下,包含一個ImageView和一個TextView
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" android:gravity="center"> <TextView android:id="@+id/describeTv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/image" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:text="TextView" android:textSize="24sp" /> <ImageView android:id="@+id/image" android:layout_width="300dp" android:layout_height="360dp" android:src="@mipmap/ic_launcher" android:layout_centerInParent="true" /></RelativeLayout>下面開始看MainActivity的布局界面
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.cjt.galleryrefreshdemo.MainActivity"> <com.cjt.galleryrefreshdemo.view.MyGallery android:id="@+id/myGallery" android:layout_width="match_parent" android:layout_height="match_parent" android:spacing="15dp" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:layout_marginRight="17dp" android:layout_marginEnd="17dp"> </com.cjt.galleryrefreshdemo.view.MyGallery></RelativeLayout>這里使用的是我們剛剛自定義的Gallery布局,那么MainActivity要使用刷新和加載的功能,就不得不實現我們剛剛定義的接口了,代碼如下public class MainActivity extends AppCompatActivity implements IGalleryEventListener{ private MyGallery myGallery ; // 自定義Gallery控件 private MyGalleryAdapter myGalleryAdapter ; // 適配器 private List<PictureBean> pictureList = new ArrayList<>(); // 數據集合,使用前要先轉換為List的子類 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myGallery = (MyGallery) findViewById(R.id.myGallery); /// 獲取界面自定義的Gallery控件 myGallery.setGalleryEventListerner(this); // 為Gallery設置滑動監聽器 // 準備數據 PictureBean bean ; for (int i = 1; i <= 7 ; i++) { bean = new PictureBean(); // 下面一句是獲取mipmap中對應的圖片名稱的資源ID int picResId = this.getResources().getIdentifier("pic"+i,"mipmap",getPackageName()); bean.setPictureResId(picResId); bean.setPictureName("照片--"+i); pictureList.add(bean); } // 設置適配器 myGalleryAdapter = new MyGalleryAdapter(this,pictureList) ; myGallery.setAdapter(myGalleryAdapter); myGallery.setSelection(1); // 默認選中第一項,跳過第0項 } @Override public void onLoadMore() { // 在這里處理加載更多的事件 taskThread(0); } @Override public void onRefresh() { // 在這里處理刷新的事件 taskThread(1); } /** * 一般新開一個線程用于加載或刷新操作 * @param type 這里的type用于區分是刷新還是加載操作 */ private void taskThread(final int type){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Log.e("CJT","這里可以新建加載或刷新的任務"); getLoad(type); // 這里是模擬加載更多的任務 } }).start(); } // 模擬獲取數據的方法,這里一般是網絡請求加載數據 private void getLoad(int type){ if(type == 0){ // 加載操作 mHandler.sendEmptyMessage(0x01); // 加載完畢之后,發送消息,更新界面 // 這里模擬加載更多數據 // 準備數據 PictureBean bean ; for (int i = 1; i <= 4 ; i++) { bean = new PictureBean(); int picResId = this.getResources().getIdentifier("m"+i,"mipmap",getPackageName()); bean.setPictureResId(picResId); bean.setPictureName("美女--"+i); pictureList.add(bean); } }else{ mHandler.sendEmptyMessage(0x02); // 刷新完畢之后,發送消息,更新界面 } } // 一般使用Handler來更新界面 private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == 0x01){ // 更新界面 if(myGalleryAdapter != null ) myGalleryAdapter.notifyDataSetChanged(); }else if(msg.what == 0x02){ // 更新界面 } myGallery.onCompleted(); // 同時調用加載結束的回調函數,停止轉圈圈 } };}首先實現了我們自定義的接口,并且重寫了兩個方法(刷新和加載),在刷新和加載中我們開啟了一個新的線程,模擬在后臺實現的網絡請求過程,加入線程休眠Thread.sleep(2000)主要是讓刷新界面或者加載界面都能出來露個臉,就是讓圈圈出來轉一下。getLoad()方法就是模擬我們的后臺請求數據,我這里使用一個int類型用來區分是刷新請求還是加載更多的請求,當然你們可以根據自己的需要去增加相應的請求的參數,比如分頁加載需要頁碼(pageNum)等。推薦使用Handler來更新界面,如果是加載更多的話,只需要調用Adapter的notifyDataSetChanged()方法,如果是刷新的話根據業務需求來更新,比如我是將數據集合清空,然后在填充一次,這樣避免了數據重復。最后別忘了回調加載結束的方法,不然那個刷新或加載更多的圈圈會一直不停的轉、不停的轉、不停的轉……。這里有一個測試用的實體類,也一并貼上來,顯得文章長一些…………
public class PictureBean { private int pictureResId ; // 圖片資源的ID private String pictureName ; // 圖片的名稱描述 public int getPictureResId() { return pictureResId; } public void setPictureResId(int pictureResId) { this.pictureResId = pictureResId; } public String getPictureName() { return pictureName; } public void setPictureName(String pictureName) { this.pictureName = pictureName; } @Override public String toString() { return "PictureBean{" + "pictureResId=" + pictureResId + ", pictureName='" + pictureName + '/'' + '}'; }}好像也沒見得有多長。總結一下:其實實現可滑動控件的刷新或加載也不難,定義好接口以及相應的待實現的方法,在要使用的地方實現該接口,并填充方法,在方法里面做刷新或加載的操作。另外刷新或加載完了之后,推薦使用Handler來更新界面,至于轉圈圈的界面,控制好時機,該出現的時候讓他出現,不該出現的時候堅決將他干掉就行了。
參考: 感謝網上的資料,下次記得把鏈接和名字一并奉上,這次就低調些
。
想要源碼的可以去這兩個地方,不要分,拿走不謝。我也是個菜鳥,希望跟大家共同探討人生,哈哈哈
。
Git地址:https://github.com/1989Jiangtao/GalleryRefresh
代碼下載:http://download.csdn.net/detail/u010898329/9750269
新聞熱點
疑難解答