RecyclerView是Android5.0之後推出的新控件,相比於ListView可定製性更大,大有取代ListView之勢。下面這篇博客主要來實現RecyclerView的上拉加載更多功能。java
基本思路是讓RecyclerView的Adapter加載兩種佈局,第一個佈局來顯示主界面,第二個佈局來顯示上拉加載時的提示信息,讓RecyclerView監聽是否滑動到最後一個item,若是是,則調用上拉刷新的邏輯,拉取遠程數據,並顯示第二個佈局。等加載完畢時,刷新android
Adapter,並隱藏第二個佈局。下面分析代碼。數組
要加載兩種不一樣的佈局,Adapter要重寫getItemViewType方法。服務器
@Override public int getItemViewType(int position) { if (position == dataList.size()) return 1; else return 0; }
onCreateViewHolder根據viewtype加載ViewHolder.
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; if (viewType == 0) { view = LayoutInflater.from(context).inflate(R.layout.grid_redu_item, parent, false); return new NormalHolder(view); } else { view = LayoutInflater.from(context).inflate(R.layout.sample_common_list_footer, parent, false); mFooterHolder = new FooterHolder(view); return mFooterHolder; } }
注意getItemCount長度要加1。
@Override public int getItemCount() { return dataList.size() + 1; }
NormalHolder
1 class NormalHolder extends RecyclerView.ViewHolder { 2 ImageView img; 3 TextView name; 4 5 public NormalHolder(View itemView) { 6 super(itemView); 7 img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic); 8 name = (TextView) itemView.findViewById(R.id.homepage_grid_name); 9 } 10 11 public void setData(int position) { 12 imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img); 13 name.setText(dataList.get(position)); 14 } 15 }
FooterHolder
1 public class FooterHolder extends RecyclerView.ViewHolder { 2 View mLoadingViewstubstub; 3 View mEndViewstub; 4 View mNetworkErrorViewstub; 5 6 public FooterHolder(View itemView) { 7 super(itemView); 8 mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub); 9 mEndViewstub = itemView.findViewById(R.id.end_viewstub); 10 mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub); 11 } 12 13 //根據傳過來的status控制哪一個狀態可見 14 public void setData(LoadingFooter.FooterState status) { 15 Log.d("TAG", "reduAdapter" + status + ""); 16 switch (status) { 17 case Normal: 18 setAllGone(); 19 break; 20 case Loading: 21 setAllGone(); 22 mLoadingViewstubstub.setVisibility(View.VISIBLE); 23 break; 24 case TheEnd: 25 setAllGone(); 26 mEndViewstub.setVisibility(View.VISIBLE); 27 break; 28 case NetWorkError: 29 setAllGone(); 30 mNetworkErrorViewstub.setVisibility(View.VISIBLE); 31 break; 32 default: 33 break; 34 } 35 36 } 37 38 //所有不可見 39 void setAllGone() { 40 if (mLoadingViewstubstub != null) { 41 mLoadingViewstubstub.setVisibility(View.GONE); 42 } 43 if (mEndViewstub != null) { 44 mEndViewstub.setVisibility(View.GONE); 45 } 46 if (mNetworkErrorViewstub != null) { 47 mNetworkErrorViewstub.setVisibility(View.GONE); 48 } 49 } 50 51 }
FooterHolder佈局:網絡
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/loading_view" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:orientation="vertical" tools:layout_height="wrap_content"> <include android:id="@+id/loading_viewstub" layout="@layout/sample_common_list_footer_loading" android:layout_width="match_parent" android:layout_height="40dp" /> <include android:id="@+id/end_viewstub" layout="@layout/sample_common_list_footer_end" android:layout_width="match_parent" android:layout_height="40dp" /> <include android:id="@+id/network_error_viewstub" layout="@layout/sample_common_list_footer_network_error" android:layout_width="match_parent" android:layout_height="40dp" /> </LinearLayout>
顯示效果:app
主要邏輯放在Fragment裏。
首先咱們要綁定爲RecyclerView綁定一個監聽器,監聽RecyclerView是否滑到了底部。
Listener代碼:
package com.yctime.truelove.LoadMore; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; import com.yctime.truelove.ImageLoader.UILPauseOnScrollListener; /** * Created by xjx * <p/> * 繼承自RecyclerView.OnScrollListener,一:能夠監聽到是否滑動到頁面最低部。二:滑動時中止加載圖片 */ public class EndlessRecyclerOnScrollListener extends UILPauseOnScrollListener { /** * 當前RecyclerView類型 */ protected LayoutManagerType layoutManagerType; /** * 最後一個的位置 */ private int[] lastPositions; /** * 最後一個可見的item的位置 */ private int lastVisibleItemPosition; /** * 當前滑動的狀態 */ private int currentScrollState = 0; @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManagerType == null) { if (layoutManager instanceof LinearLayoutManager) { layoutManagerType = LayoutManagerType.LinearLayout; } else if (layoutManager instanceof GridLayoutManager) { layoutManagerType = LayoutManagerType.GridLayout; } else if (layoutManager instanceof StaggeredGridLayoutManager) { layoutManagerType = LayoutManagerType.StaggeredGridLayout; } else { throw new RuntimeException( "Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); } } switch (layoutManagerType) { case LinearLayout: lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case GridLayout: lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); break; case StaggeredGridLayout: StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; if (lastPositions == null) { lastPositions = new int[staggeredGridLayoutManager.getSpanCount()]; } staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions); lastVisibleItemPosition = findMax(lastPositions); break; } } @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); currentScrollState = newState; RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); int visibleItemCount = layoutManager.getChildCount(); int totalItemCount = layoutManager.getItemCount(); if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) { onLoadNextPage(recyclerView); } } /** * 取數組中最大值 * * @param lastPositions * @return */ private int findMax(int[] lastPositions) { int max = lastPositions[0]; for (int value : lastPositions) { if (value > max) { max = value; } } return max; } public void onLoadNextPage(final View view) { } public static enum LayoutManagerType { LinearLayout, StaggeredGridLayout, GridLayout } }
下面是Fragment完整代碼:
1 package com.yctime.truelove.fragment; 2 3 4 import android.support.v4.app.Fragment; 5 import android.support.v7.widget.DefaultItemAnimator; 6 import android.support.v7.widget.GridLayoutManager; 7 import android.support.v7.widget.LinearLayoutManager; 8 import android.support.v7.widget.RecyclerView; 9 import android.util.Log; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 13 import com.yctime.truelove.LoadMore.EndlessRecyclerOnScrollListener; 14 import com.yctime.truelove.LoadMore.LoadingFooter; 15 import com.yctime.truelove.MainActivity; 16 import com.yctime.truelove.Utils.NetworkUtils; 17 import com.yctime.truelove.login.R; 18 19 import java.util.ArrayList; 20 21 22 /** 23 * A simple {@link Fragment} subclass. 24 */ 25 public class HomeReDuFragment extends BaseFragment { 26 27 private RecyclerView mRecyclerView; 28 private GridAdapter_Redu gridReDuAdapter; 29 // 服務器端一共多少條數據 30 private static final int TOTAL_COUNTER = 50; 31 // 每一頁展現多少條數據 32 private static final int REQUEST_COUNT = 12; 33 // 已經獲取到多少條數據了 34 private int mCurrentCounter = 0; 35 //模擬的數據源 36 private ArrayList<String> dataList; 37 38 39 protected LoadingFooter.FooterState mState = LoadingFooter.FooterState.Normal; 40 41 protected void setState(LoadingFooter.FooterState mState) { 42 this.mState = mState; 43 ((MainActivity) mContext).runOnUiThread(new Runnable() { 44 @Override 45 public void run() { 46 changeAdaperState(); 47 } 48 }); 49 } 50 51 //改變底部bottom的樣式 52 protected void changeAdaperState() { 53 if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) { 54 gridReDuAdapter.mFooterHolder.setData(mState); 55 } 56 } 57 58 public HomeReDuFragment() { 59 } 60 61 62 @Override 63 protected View initView() { 64 View mView = LayoutInflater.from(mContext).inflate(R.layout.homepage_viewpager_item_redu, null); 65 mRecyclerView = (RecyclerView) mView.findViewById(R.id.home_page_recyclerview); 66 return mView; 67 } 68 69 @Override 70 protected void initData() { 71 initGridView(); 72 } 73 74 75 private View initGridView() { 76 mRecyclerView.setHasFixedSize(true); 77 //滑動暫停加載網絡圖片,並且能夠監聽recycler是否滑動到底部 78 mRecyclerView.addOnScrollListener(mOnScrollListener); 79 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); 80 gridReDuAdapter = new GridAdapter_Redu(mContext); 81 gridReDuAdapter.addAll(getRemoteData()); 82 mRecyclerView.setAdapter(gridReDuAdapter); 83 GridLayoutManager layoutManager = new GridLayoutManager(mContext, 3); 84 layoutManager.setOrientation(LinearLayoutManager.VERTICAL); 85 layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 86 @Override 87 public int getSpanSize(int position) { 88 //若是是最後一個item,則設置佔據3列,不然佔據1列 89 boolean isFooter = position == gridReDuAdapter.getItemCount() - 1; 90 return isFooter ? 3 : 1; 91 } 92 }); 93 mRecyclerView.setLayoutManager(layoutManager); 94 return mRecyclerView; 95 } 96 97 private EndlessRecyclerOnScrollListener mOnScrollListener = new EndlessRecyclerOnScrollListener() { 98 @Override 99 public void onLoadNextPage(View view) { 100 super.onLoadNextPage(view); 101 102 if (mState == LoadingFooter.FooterState.Loading) { 103 Log.d("@TAG", "the state is Loading, just wait.."); 104 return; 105 } 106 107 if (mCurrentCounter < TOTAL_COUNTER) { 108 // loading more 109 requestData(); 110 Log.d("TAG", "請求數據"); 111 } else { 112 //the end 113 setState(LoadingFooter.FooterState.TheEnd); 114 } 115 } 116 }; 117 118 119 /** 120 * 模擬請求網絡 121 */ 122 private void requestData() { 123 setState(LoadingFooter.FooterState.Loading); 124 new Thread() { 125 @Override 126 public void run() { 127 super.run(); 128 try { 129 Thread.sleep(1000); 130 } catch (InterruptedException e) { 131 e.printStackTrace(); 132 } 133 if (NetworkUtils.isNetAvailable(mContext)) { 134 //模擬請求遠程數據 135 gridReDuAdapter.addAll(getRemoteData()); 136 //加載完畢時 137 setState(LoadingFooter.FooterState.Normal); 138 Log.d("TAG", mCurrentCounter + ""); 139 } else { 140 //模擬一下網絡請求失敗的狀況 141 setState(LoadingFooter.FooterState.NetWorkError); 142 } 143 } 144 }.start(); 145 } 146 147 //模擬請求數據 148 private ArrayList<String> getRemoteData() { 149 if (dataList == null) 150 dataList = new ArrayList<>(); 151 //每次都清空一下 152 dataList.clear(); 153 //要減去adapter最後一頁 154 for (int i = 0; i < REQUEST_COUNT; i++) { 155 if (dataList.size() + mCurrentCounter >= TOTAL_COUNTER) { 156 break; 157 } 158 dataList.add("帳號" + (mCurrentCounter + i)); 159 } 160 mCurrentCounter += dataList.size(); 161 return dataList; 162 } 163 164 165 }
須要注意的是這個方法:
//改變底部bottom的樣式 protected void changeAdaperState() { if (gridReDuAdapter != null && gridReDuAdapter.mFooterHolder != null) { gridReDuAdapter.mFooterHolder.setData(mState); } }
Adapter的應用調用Adapter裏面的方法,來切換Adaper的樣式。
Adapter完整代碼:
1 package com.yctime.truelove.fragment; 2 3 import android.content.Context; 4 import android.content.Intent; 5 import android.support.v7.widget.RecyclerView; 6 import android.util.Log; 7 import android.view.LayoutInflater; 8 import android.view.View; 9 import android.view.ViewGroup; 10 import android.widget.ImageView; 11 import android.widget.TextView; 12 13 import com.yctime.truelove.ImageLoader.MyImageLoader; 14 import com.yctime.truelove.ImageLoader.UILImageLoader; 15 import com.yctime.truelove.LoadMore.LoadingFooter; 16 import com.yctime.truelove.drawer.MyZoneActivity; 17 import com.yctime.truelove.login.R; 18 19 import java.util.ArrayList; 20 21 22 public class GridAdapter_Redu extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 23 24 25 public String[] imgUrls = { 26 "http://img5.duitang.com/uploads/item/201402/22/20140222113830_X3ddd.jpeg", 27 "http://v1.qzone.cc/avatar/201403/01/10/36/531147afa4197738.jpg!200x200.jpg", 28 "http://g.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=e4d7ed147af40ad115b1cfe7621c3de9/b7fd5266d016092445b47837d50735fae6cd340d.jpg", 29 "http://img5q.duitang.com/uploads/item/201502/19/20150219182507_vGVaK.jpeg", 30 "http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212190118646.jpg", 31 "http://img5.duitang.com/uploads/item/201412/08/20141208221323_YVJFk.png", 32 "http://cdn.duitang.com/uploads/item/201408/02/20140802222651_GWuU2.png", 33 "http://ent.dzwww.com/yulezhuanti/mtcbg/201510/W020151027467479100669.jpg", 34 "http://p1.qqyou.com/touxiang/uploadpic/2013-3/10/2013031009323656495.jpg", 35 "http://p1.qqyou.com/touxiang/uploadpic/2013-3/12/2013031212295986807.jpg", 36 "http://f.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=10742594d739b6004d9b07b1d9601912/9f2f070828381f30ec9eabdeab014c086f06f0c5.jpg", 37 "http://a.hiphotos.baidu.com/zhidao/wh%3D600%2C800/sign=5bda8a18a71ea8d38a777c02a73a1c76/5882b2b7d0a20cf4598dc37c77094b36acaf9977.jpg", 38 "http://a1.att.hudong.com/36/98/300001051406133039983418031.jpg" 39 }; 40 public Context context; 41 MyImageLoader imageLoader = new UILImageLoader(); 42 private ArrayList<String> dataList = new ArrayList<>(); 43 44 45 private final int NORMALLAYOUT = 0; 46 private final int FOOTERLAYOUT = 1; 47 public FooterHolder mFooterHolder; 48 49 public GridAdapter_Redu(Context context) { 50 this.context = context; 51 } 52 53 public void addAll(ArrayList<String> list) { 54 int lastIndex = this.dataList.size(); 55 if (this.dataList.addAll(list)) { 56 notifyItemRangeInserted(lastIndex, list.size()); 57 } 58 } 59 60 @Override 61 public int getItemViewType(int position) { 62 if (position == dataList.size()) 63 return FOOTERLAYOUT; 64 else 65 return NORMALLAYOUT; 66 } 67 68 69 @Override 70 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 71 View view; 72 if (viewType == NORMALLAYOUT) { 73 view = LayoutInflater.from(context).inflate(R.layout.grid_redu_item, parent, false); 74 return new NormalHolder(view); 75 } else { 76 view = LayoutInflater.from(context).inflate(R.layout.sample_common_list_footer, parent, false); 77 mFooterHolder = new FooterHolder(view); 78 return mFooterHolder; 79 } 80 } 81 82 @Override 83 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 84 if (holder instanceof NormalHolder) { 85 //點擊事件 86 holder.itemView.setOnClickListener(new View.OnClickListener() { 87 @Override 88 public void onClick(View v) { 89 Intent intent = new Intent(context, MyZoneActivity.class); 90 context.startActivity(intent); 91 } 92 }); 93 ((NormalHolder) holder).setData(position); 94 } 95 } 96 97 @Override 98 public int getItemCount() { 99 return dataList.size() + 1; 100 } 101 102 103 class NormalHolder extends RecyclerView.ViewHolder { 104 ImageView img; 105 TextView name; 106 107 public NormalHolder(View itemView) { 108 super(itemView); 109 img = (ImageView) itemView.findViewById(R.id.homepage_grid_picpic); 110 name = (TextView) itemView.findViewById(R.id.homepage_grid_name); 111 } 112 113 public void setData(int position) { 114 imageLoader.displayImage(context, imgUrls[position % imgUrls.length], img); 115 name.setText(dataList.get(position)); 116 } 117 } 118 119 public class FooterHolder extends RecyclerView.ViewHolder { 120 View mLoadingViewstubstub; 121 View mEndViewstub; 122 View mNetworkErrorViewstub; 123 124 public FooterHolder(View itemView) { 125 super(itemView); 126 mLoadingViewstubstub = itemView.findViewById(R.id.loading_viewstub); 127 mEndViewstub = itemView.findViewById(R.id.end_viewstub); 128 mNetworkErrorViewstub = itemView.findViewById(R.id.network_error_viewstub); 129 } 130 131 //根據傳過來的status控制哪一個狀態可見 132 public void setData(LoadingFooter.FooterState status) { 133 Log.d("TAG", "reduAdapter" + status + ""); 134 switch (status) { 135 case Normal: 136 setAllGone(); 137 break; 138 case Loading: 139 setAllGone(); 140 mLoadingViewstubstub.setVisibility(View.VISIBLE); 141 break; 142 case TheEnd: 143 setAllGone(); 144 mEndViewstub.setVisibility(View.VISIBLE); 145 break; 146 case NetWorkError: 147 setAllGone(); 148 mNetworkErrorViewstub.setVisibility(View.VISIBLE); 149 break; 150 default: 151 break; 152 } 153 154 } 155 156 //所有不可見 157 void setAllGone() { 158 if (mLoadingViewstubstub != null) { 159 mLoadingViewstubstub.setVisibility(View.GONE); 160 } 161 if (mEndViewstub != null) { 162 mEndViewstub.setVisibility(View.GONE); 163 } 164 if (mNetworkErrorViewstub != null) { 165 mNetworkErrorViewstub.setVisibility(View.GONE); 166 } 167 } 168 169 } 170 171 }
運行效果:
