Recylerview 加载更多功能实现(分页加载)
来源:互联网 发布:js获取手机屏幕宽度 编辑:程序博客网 时间:2024/05/17 22:12
2017.3.20更新
更新说明:之前看评论里的童鞋们给我反映问题,很感谢大家指出,针对出现的问题我进行了一些改进。同时将加载更多的这个功能从主体的adapter中分离出来,使用了装饰者模式,降低了代码的耦合,这样便于维护和修改。
装饰者模式是常用的Java设计模式之一,不熟悉的童鞋可以自行查阅资料,先了解了装饰者模式看以下内容会容易懂一点~这个设计模式在Android里也是广泛应用,最典型的就是Context的应用了。
……………………….我是分割线…………………………………………
Recyclerview是 Listview 的升级版本,在项目中使用较为广泛,官方也推荐我们使用 Recyclerview 来代替 Listview,在此就不多说 Recyclerview 的优势特点 balala了。。。
在实际项目中,列表通常是分页的,请求服务器也只会一次请求若干条,按需加载,这样比较节省流量,这样就有了我们很常见的上拉加载更多的功能,具体的实现效果如下图:
该activity的布局文件:
<?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/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="io.geek.myapplication.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout>
底部加载更多的view布局:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:gravity="center" android:layout_height="wrap_content"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:text="正在加载..." android:layout_width="wrap_content" android:layout_height="wrap_content"/></LinearLayout>
底部提示到底的view布局:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:padding="8dp" android:gravity="center" android:layout_height="wrap_content"> <TextView android:text="已经到底了" android:layout_width="wrap_content" android:layout_height="wrap_content"/></LinearLayout>
数据item的view:
<?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:orientation="horizontal" android:gravity="center" android:padding="8dp" android:layout_width="match_parent" android:layout_height="wrap_content"> <View android:background="@color/colorAccent" android:layout_width="50dp" android:layout_height="50dp"/> <TextView android:gravity="center" android:id="@+id/tv_content" tools:text="我是第一项" android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout>
接下来就是代码的编写了,开头就已经讲到过我们在这要用到装饰者模式,其实装饰者模式并没有多么复杂,可以理解成给一个类装饰点新功能,并且不会影响到这个类本来的功能。装饰者模式写法也很简单,分为以下几步套路:
1. 装饰者模式套路第一步:
创建基类(装饰类和被装饰类共同父类),首先写一个抽象类BaseAdapter,继承自RecyclerView.Adapter,这个Adapter中只封装了与数据源相关的字段和方法:
/** * 与数据源相关的字段和方法封装在父类中 */public abstract class BaseAdapter<T> extends RecyclerView.Adapter { protected List<T> dataSet = new ArrayList<>(); public void updateData(List dataSet) { this.dataSet.clear(); appendData(dataSet); } public void appendData(List dataSet) { if (dataSet != null && !dataSet.isEmpty()) { this.dataSet.addAll(dataSet); notifyDataSetChanged(); } } public List<T> getDataSet() { return dataSet; } @Override public int getItemCount() { return dataSet.size(); }}
2. 装饰者模式套路第二步
创建装饰类,然后开始编写加载更多的adapter的包装类,这个类继承自BaseAdpater,这个装饰类中只负责处理加载更多的逻辑,代码如下:
/** * 在这个装饰器中,只做与加载更多相关操作。public class LoadMoreAdapterWrapper extends BaseAdapter<String> { private BaseAdapter mAdapter; private static final int mPageSize = 10; private int mPagePosition = 0; private boolean hasMoreData = true; private OnLoad mOnLoad; public LoadMoreAdapterWrapper(BaseAdapter adapter, OnLoad onLoad) { mAdapter = adapter; mOnLoad = onLoad; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == R.layout.list_item_no_more) { View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); return new NoMoreItemVH(view); } else if (viewType == R.layout.list_item_loading) { View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); return new LoadingItemVH(view); } else { return mAdapter.onCreateViewHolder(parent, viewType); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof LoadingItemVH) { requestData(mPagePosition, mPageSize); } else if (holder instanceof NoMoreItemVH) { } else { mAdapter.onBindViewHolder(holder, position); } } private void requestData(int pagePosition, int pageSize) { //网络请求,如果是异步请求,则在成功之后的回调中添加数据,并且调用notifyDataSetChanged方法,hasMoreData为true //如果没有数据了,则hasMoreData为false,然后通知变化,更新recylerview if (mOnLoad != null) { mOnLoad.load(pagePosition, pageSize, new ILoadCallback() { @Override public void onSuccess() { notifyDataSetChanged(); mPagePosition = (mPagePosition + 1) * mPageSize; hasMoreData = true; } @Override public void onFailure() { hasMoreData = false; } }); } } @Override public int getItemViewType(int position) { if (position == getItemCount() - 1) { if (hasMoreData) { return R.layout.list_item_loading; } else { return R.layout.list_item_no_more; } } else { return mAdapter.getItemViewType(position); } } @Override public int getItemCount() { return mAdapter.getItemCount() + 1; } static class LoadingItemVH extends RecyclerView.ViewHolder { public LoadingItemVH(View itemView) { super(itemView); } } static class NoMoreItemVH extends RecyclerView.ViewHolder { public NoMoreItemVH(View itemView) { super(itemView); } }}public interface OnLoad { void load(int pagePosition, int pageSize, ILoadCallback callback);}public interface ILoadCallback { void onSuccess(); void onFailure();}
解析如下:
1. 该类中的传入的mAdapter就是需要被装饰的adapter,mOnLoad是自定义的一个接口,传入之后可以回调处理加载更多数据。
2. 首先重写getItemCount方法,因为在列表中最后始终会有一个加载更多或者是到底提示的view,所以item的数目始终是数据源的数目多一个。
3. 重写getItemType方法:首先判断position是否是最后一个,如果不是的话,则直接返回被装饰的mAdapter的getItemType;如果是的话判断hasMoreData变量,是true则返回加载更多的布局文件ID,反之返回到底的布局文件ID。这里的hasMoreData是我们自己定义的一个boolean字段,通过改变这个值就可以最后一个view应该是加载更多还是到底。
4. 重写onCreateViewHolder方法,在这个方法中通过判断viewType的类型返回不同的ViewHolder。
5. 重写onBindViewHolder方法,在这个方法中,判断holder的类型的不同做出不同的操作,如果holder是加载更多的holder,那么表示加载更多的view正在展示,这时候就应该做出加载更多的操作,这个操作放在了requestData方法中。
6. 在requestData方法中,触发了OnLoad接口中的load回调,传入的参数有当前的页码加载数据一页的大小,加载完成之后的回调接口,可以看到加载完成之后会进行不同的作,如果成功,则设置hasMoreData为true,并且通知数据发生改变,更新列表,改变前页码;如果失败的话,则把hasMoreData设置为false。
3. 装饰者模式套路第三步
创建被装饰类,同样这个类也需要继承自BaseAdapter:
/** * 被装饰类要和装饰类继承自同一父类 */public class MyAdapter extends BaseAdapter<String> { private Context mContext; public MyAdapter(Context context) { mContext = context; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(mContext).inflate(R.layout.list_item_mine, parent, false); return new MyViewHolder(v); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((MyViewHolder) holder).bind(getDataSet().get(position)); } static class MyViewHolder extends RecyclerView.ViewHolder { TextView mTextView; public MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(R.id.tv_content); } public void bind(CharSequence content) { mTextView.setText(content); } }}
4. 装饰者模式套路第四步
最后一步就是如何使用,这个adapter就是一个普通的没有上拉加载功能的adapter,如果要给它加上这个功能,只需要这么使用:
public class MainActivity extends AppCompatActivity { RecyclerView mRecyclerView; BaseAdapter mAdapter; int loadCount; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView) findViewById(R.id.recycler); //创建被装饰者类实例 final MyAdapter adapter = new MyAdapter(this); //创建装饰者实例,并传入被装饰者和回调接口 mAdapter = new LoadMoreAdapterWrapper(adapter, new OnLoad() { @Override public void load(int pagePosition, int pageSize, final ILoadCallback callback) { //此处模拟做网络操作,2s延迟,将拉取的数据更新到adpter中 new Handler().postDelayed(new Runnable() { @Override public void run() { List<String> dataSet = new ArrayList(); for (int i = 0; i < 10; i++) { dataSet.add("我是一条数据"); } //数据的处理最终还是交给被装饰的adapter来处理 adapter.appendData(dataSet); callback.onSuccess(); //模拟加载到没有更多数据的情况,触发onFailure if (loadCount++ == 3) { callback.onFailure(); } } }, 2000); } }); mRecyclerView.setAdapter(mAdapter); mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); }}
这样就完整实现了 Recylerview 的加载更多功能,使用装饰者模式使得整个代码结构更加清晰,在被装饰的adapter中就不用处理太多逻辑,专心处理数据展示即可,易于维护。
其中比较绕的地方可能就是数据加载的回调接口,不过仔细理一下还是没有多大问题,总体来说还是很简单的,如果还是有不明白的,可能你对 Recylerview 的使用方法还不够了解,可以自行学习。
有不当之处请大家批评指正,谢谢:)
- Recylerview 加载更多功能实现(分页加载)
- Recylerview 加载更多功能实现(分页加载)
- RecylerView实现下拉到底加载更多数据
- 加载更多功能实现
- 加载更多功能实现
- dropload.js实现下滑加载更多分页功能
- RecycleView上拉加载更多、实现分批分页功能
- RecyclerView实现分批分页、上拉加载更多功能
- UITableView实现加载更多功能
- 加载更多功能的实现
- ListView分页加载,加载更多
- 为RecyclerView添加分页加载(上拉加载更多)功能
- Android_listview分页加载更多
- js分页加载更多
- 实现Twitter自动加载更多功能
- UItableview中实现加载更多功能+++
- java实现页面加载更多功能
- 工作笔记之加载更多功能实现
- webpack初学者指南(Beginner’s guide to Webpack)
- 成员变量反射的综合案例
- 成员方法的反射
- 设计模式之工厂模式
- Day4-3.31
- Recylerview 加载更多功能实现(分页加载)
- 抽象类和接口的区别和联系【java】
- Qt浅谈之四十八窗口下方弹出提示信息
- 构造方法的反射应用
- JAVAEE学习笔记
- 历史上最伟大的12位程序员
- android音视频采集参考
- Java源码学习之Integer类(三)——好玩的位运算函数
- snort--byte_test,byte_jump用法