在开发中常常使用到刷新分页,这里实现一个 RecyclerView 的简单的刷新分页操作,RecyclerView 的刷新分页会了,相信 ListView 、ExpandListView 分组列表的刷新分页肯定不在话下,那就一起来看一下具体是如何实现的。
实现思路
-
加载更多数据使用到 RecyclerView 加载多种布局,根据 ViewType 判断加载数据 Item 还是加载 FooterItem ;
-
通过线程模拟加载数据;
-
为 RecyclerView 添加滚动事件来监听用户的滑动操作;
-
根据用户滑动状态以及具体情况开始加载数据
-
通知数据更新。
遇到的第一个问题肯定就是如何获得屏幕上第一个可见的 Item 呢?为了能够在数据加载中动态判断什么时候加载数据,需要知道屏幕上显示的第一个可见的 Item 的位置,当然了这里使用的是布局管理器是 LinearLayoutManager ,这样查找屏幕上第一个可见的 Item 就显得容易多了,下面介绍一些 LinearLayoutManager 的四个方法:
findFirstVisibleItemPosition()
获得屏幕上第一个可见 Item 的 position,只要该 Item 有一部分可见,那么返回的 position 就是该Item 的 position。
findFirstCompletelyVisibleItemPosition()
获得屏幕上第一个完整可见的 Item 的 position,只要该 Item 有一部分不可见,那么返回的 position 就是该 Item 对应的下一个能显示完整的 Item 的position。
findLastVisibleItemPosition()
获得屏幕上最后一个可见 Item 的 position,只要该 Item 有一部分可见,那么返回的 position 就是该Item 的 position。
findLastCompletelyVisibleItemPosition()
获得屏幕上最后一个完整可见的 Item 的 position,只要该 Item 有一部分不可见,那么返回的 position 就是该 Item 对应的上一个能显示完整的 Item 的position。
准备数据
这里使用线程模拟网络数据,直接看代码,具体如下:
1 /**
2 * 初始化数据
3 * @return
4 */
5 public void initData(){
6 for (int i=0;i<30;i++){
7 arrayList.add("第"+i+"条数据");
8 }
9 }
10
11 /**
12 * 线程模拟加载数据
13 */
14 class LoadDataThread extends Thread{
15 @Override
16 public void run() {
17 initData();
18 try {
19 Thread.sleep(2000);
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 //通知主线程更新数据
24 Message message = handler.obtainMessage();
25 message.what = UPDATE_DATA;
26 message.obj = arrayList;
27 handler.sendMessage(message);
28 }
29 }
代码参考
一直觉得写博客如果没有源码链接,最好把代码补全,希望每一个读者都能有所收获。
主要布局
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 tools:context="com.manu.mrecyclerview.MainActivity">
8 <android.support.v7.widget.RecyclerView
9 android:id="@+id/rv"
10 android:layout_width="match_parent"
11 android:layout_height="match_parent">
12 </android.support.v7.widget.RecyclerView>
13</LinearLayout>
Item 布局
RecyclerView 每一数据项的布局,具体如下:
1/**item.xml**/
2<?xml version="1.0" encoding="utf-8"?>
3<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:orientation="vertical"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:padding="5dp">
8 <TextView
9 android:id="@+id/tv_recycle"
10 android:layout_width="match_parent"
11 android:layout_height="wrap_content"
12 android:gravity="center_horizontal"
13 android:text="data"
14 android:background="#cac3c3"
15 android:padding="10dp"
16 android:textSize="20sp"/>
17</LinearLayout>
Footer 布局
Footer 布局作为加载更多的布局,具体如下:
1/**item_footer.xml**/
2<?xml version="1.0" encoding="utf-8"?>
3<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:orientation="horizontal"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:gravity="center_horizontal">
8 <ProgressBar
9 style="?android:attr/progressBarStyleSmall"
10 android:layout_width="wrap_content"
11 android:layout_height="wrap_content"
12 android:id="@+id/progressBar" />
13 <TextView
14 android:text="正在努力加载中,请稍后..."
15 android:layout_width="wrap_content"
16 android:layout_height="wrap_content"
17 android:id="@+id/textView" />
18</LinearLayout>
Adapter
这里使用了 RecyclerView 根据不同的 ViewType 加载多种布局的用法,使用时根据不同的布局创建不同的 ViewHolder , 然后根据不同的 Viewholder 为对应的 Item 添加数据,注意 getItemViewType() 方法的用法,Adapter 代码参考如下:
1/**
2 * Created by jzman on 2017/6/04
3 * RecycleView的Adapter
4 */
5public class RvAdapter1 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements
6 View.OnClickListener{
7 private static final int ITEM_FOOTER = 0x1;
8 private static final int ITEM_DATA = 0x2;
9 private Context mContext;
10 private RecyclerView recyclerView;
11 private ArrayList<String> mList;
12
13 public RvAdapter1() {}
14
15 public RvAdapter1(Context mContext, ArrayList<String> mList) {
16 this.mContext = mContext;
17 this.mList = mList;
18 }
19
20 public void setmList(ArrayList<String> mList) {
21 this.mList = mList;
22 }
23
24 /**
25 * 用于创建ViewHolder
26 * @param parent
27 * @param viewTypez
28 * @return
29 */
30 @Override
31 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
32 View view ;
33 RecyclerView.ViewHolder vh = null;
34 switch (viewType){
35 case ITEM_DATA:
36 view = LayoutInflater.from(mContext).inflate(R.layout.item,null);
37 view.setOnClickListener(this);
38 vh = new DataViewHolder(view);
39 //使用代码设置宽高(xml布局设置无效时)
40 view.setLayoutParams(new ViewGroup.LayoutParams(
41 ViewGroup.LayoutParams.MATCH_PARENT,
42 ViewGroup.LayoutParams.WRAP_CONTENT));
43 break;
44 case ITEM_FOOTER:
45 view = LayoutInflater.from(mContext).inflate(R.layout.item_footer,null);
46 //使用代码设置宽高(xml布局设置无效时)
47 vh = new FooterViewHolder(view);
48 view.setLayoutParams(new ViewGroup.LayoutParams(
49 ViewGroup.LayoutParams.MATCH_PARENT,
50 ViewGroup.LayoutParams.WRAP_CONTENT));
51 break;
52 }
53 return vh;
54 }
55
56 /**
57 * 获取Item的View类型
58 * @param position
59 * @return
60 */
61 @Override
62 public int getItemViewType(int position) {
63 //根据 Item 的 position 返回不同的 Viewtype
64 if (position == (getItemCount())-1){
65 return ITEM_FOOTER;
66 }else{
67 return ITEM_DATA;
68 }
69 }
70
71 /**
72 * 绑定数据
73 * @param holder
74 * @param position
75 */
76 @Override
77 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
78 if (holder instanceof DataViewHolder){
79 DataViewHolder dataViewHolder = (DataViewHolder) holder;
80 dataViewHolder.tv_data.setText(mList.get(position));
81 }else if (holder instanceof FooterViewHolder){
82
83 }
84 }
85
86 /**
87 * 选项总数
88 * @return
89 */
90 @Override
91 public int getItemCount() {
92 return mList.size()+1;
93 }
94
95 @Override
96 public void onClick(View view) {
97 //根据RecyclerView获得当前View的位置
98 int position = recyclerView.getChildAdapterPosition(view);
99 //程序执行到此,会去执行具体实现的onItemClick()方法
100 if (onItemClickListener!=null){
101 onItemClickListener.onItemClick(recyclerView,view,position,mList.get(position));
102 }
103 }
104
105 /**
106 * 创建ViewHolder
107 */
108 public static class DataViewHolder extends RecyclerView.ViewHolder{
109 TextView tv_data;
110 public DataViewHolder(View itemView) {
111 super(itemView);
112 tv_data = (TextView) itemView.findViewById(R.id.tv_recycle);
113 }
114 }
115
116 /**
117 * 创建footer的ViewHolder
118 */
119 public static class FooterViewHolder extends RecyclerView.ViewHolder{
120 public FooterViewHolder(View itemView) {
121 super(itemView);
122 }
123 }
124
125 private OnItemClickListener onItemClickListener;
126 public void setOnItemClickListener(OnItemClickListener onItemClickListener){
127 this.onItemClickListener = onItemClickListener;
128 }
129
130 /**
131 * 定义RecyclerView选项单击事件的回调接口
132 */
133 public interface OnItemClickListener{
134 //参数(父组件,当前单击的View,单击的View的位置,数据)
135 void onItemClick(RecyclerView parent,View view, int position, String data);
136 }
137 /**
138 * 将RecycleView附加到Adapter上
139 */
140 @Override
141 public void onAttachedToRecyclerView(RecyclerView recyclerView) {
142 super.onAttachedToRecyclerView(recyclerView);
143 this.recyclerView= recyclerView;
144 }
145 /**
146 * 将RecycleView从Adapter解除
147 */
148 @Override
149 public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
150 super.onDetachedFromRecyclerView(recyclerView);
151 this.recyclerView = null;
152 }
153}
MainActivity
这里主要注意 rv.addOnScrollListener(new OnScrollListener() …里面的具体实现,MainActivity 代码参考如下:
1/**
2 * Created by jzman on 2017/6/04 0013.
3 */
4public class MainActivity extends AppCompatActivity {
5 private static final int UPDATE_DATA = 0x3;
6
7 private RecyclerView rv;
8 RvAdapter1 adapter;
9
10 private ArrayList<String> arrayList = new ArrayList<>();
11 //加载更多数据时最后一项的索引
12 private int lastLoadDataItemPosition;
13
14 @Override
15 protected void onCreate(Bundle savedInstanceState) {
16 super.onCreate(savedInstanceState);
17 setContentView(R.layout.activity_main);
18 rv = (RecyclerView) findViewById(R.id.rv);
19
20 //设置布局管理器
21 rv.setLayoutManager(new LinearLayoutManager(this));//线性
22// rv.setLayoutManager(new GridLayoutManager(this,4));//线性
23// rv.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));//线性
24 initData();
25 adapter = new RvAdapter1(this,arrayList);
26 adapter.setOnItemClickListener(new RvAdapter1.OnItemClickListener() {
27 @Override
28 public void onItemClick(RecyclerView parent, View view, int position, String data) {
29 Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
30 }
31 });
32
33 rv.addOnScrollListener(new OnScrollListener() {
34 @Override
35 public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
36 if (newState == SCROLL_STATE_IDLE &&
37 lastLoadDataItemPosition == adapter.getItemCount()){
38 new LoadDataThread().start();
39 }
40 }
41
42 @Override
43 public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
44
45 LayoutManager layoutManager = recyclerView.getLayoutManager();
46 if (layoutManager instanceof LinearLayoutManager){
47 LinearLayoutManager manager = (LinearLayoutManager) layoutManager;
48 int firstVisibleItem = manager.findFirstVisibleItemPosition();
49 int l = manager.findLastCompletelyVisibleItemPosition();
50 lastLoadDataItemPosition = firstVisibleItem+(l-firstVisibleItem)+1;
51 }
52 }
53 });
54
55 rv.setAdapter(adapter);
56 }
57
58 /**
59 * 初始化数据
60 * @return
61 */
62 public void initData(){
63 for (int i=0;i<25;i++){
64 arrayList.add("第"+i+"条数据");
65 }
66 }
67
68 /**
69 * 线程模拟加载数据
70 */
71 class LoadDataThread extends Thread{
72 @Override
73 public void run() {
74 initData();
75 try {
76 Thread.sleep(2000);
77 } catch (InterruptedException e) {
78 e.printStackTrace();
79 }
80 Message message = handler.obtainMessage();
81 message.what = UPDATE_DATA;
82 message.obj = arrayList;
83 handler.sendMessage(message);
84 }
85 }
86
87 private Handler handler = new Handler(){
88
89 @Override
90 public void handleMessage(Message msg) {
91 super.handleMessage(msg);
92 switch (msg.what){
93 case UPDATE_DATA:
94 arrayList = (ArrayList<String>) msg.obj;
95 adapter.setmList(arrayList);
96 adapter.notifyDataSetChanged();
97 break;
98 }
99 }
100 };
101}
显示效果
RecyclerView 的刷新分页