Android5.0新特性:RecyclerView实现上拉加载更多
RecyclerView是Android5.0以后推出的新控件,相比于ListView可定制性更大,大有取代ListView之势。下面这篇博客主要来实现RecyclerView的上拉加载更多功能。
基本思路是让RecyclerView的Adapter加载两种布局,第一个布局来显示主界面,第二个布局来显示上拉加载时的提示信息,让RecyclerView监听是否滑动到最后一个item,如果是,则调用上拉刷新的逻辑,拉取远程数据,并显示第二个布局。等加载完毕时,刷新
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>
显示效果:

主要逻辑放在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

