在开发中常常使用到刷新分页,这里实现一个 RecyclerView 的简单的刷新分页操作,RecyclerView 的刷新分页会了,相信 ListView 、ExpandListView 分组列表的刷新分页肯定不在话下,那就一起来看一下具体是如何实现的。

实现思路

  1. 加载更多数据使用到 RecyclerView 加载多种布局,根据 ViewType 判断加载数据 Item 还是加载 FooterItem ;
  2. 通过线程模拟加载数据;
  3. 为 RecyclerView 添加滚动事件来监听用户的滑动操作;
  4. 根据用户滑动状态以及具体情况开始加载数据
  5. 通知数据更新。

遇到的第一个问题肯定就是如何获得屏幕上第一个可见的 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}

显示效果

Android recycleview 分割线 recycleview分页_xml

RecyclerView 的刷新分页