前言:利用SwipeRefreshLayout和RecyclerView来实现下拉刷新和加载更多,有很多的例子,但普遍都存在一个问题,当总的数据不够一屏时,FooterView也显示了,如果直接隐藏FooterView,加载更多时FooterView又不显示了,捣鼓了一阵子后,总算完美的解决了,所以记录一下,同时为了方便使用,进行了一些简单的封装。
直接进入主题,关于SwipeRefreshLayout和RecyclerView的一些基本介绍,就不在这里累述,不了解的童鞋,上网查一下吧!
一、封装自己的RefreshLayout控件
为什么要封装?为了使用方便;更为了Activity简单(MVP模式);因为Adapter的数据源类型不确定,所以采用泛型来实现。
java类和xml布局如下图
仔细的童鞋,已经发现了java类继承的是LinearLayout(xml根节点),为什么不用直接继承SwipeRefreshLayout(xml根节点)呢?其实一开始也是直接已SwipeRefreshLayout为根节点的,直接以SwipeRefreshLayout为根节点,运行后发现下拉刷新的颜色无法修改(swipeRefreshLayout.setColorSchemeColors(int color)无效),而且SwipeRefreshLayout.setRefreshing(false)方法也无效,导致无法释放下拉刷新,界面卡住。
要实现下拉刷新和加载更多,需要SwipeRefreshLayout监听OnRefreshListener和RecyclerView监听OnScrollListener,下拉刷新比较简单,如图
难点在于RecyclerView监听OnScrollListener,重写onScrollStateChanged和onScrolled方法,方法具体实现如下图
代码简单,逻辑也不复杂,图一主要实现“加载更多”,根据判断RecyclerView是否滑倒底部了;图二主要实现“不足一屏数据”的显示逻辑,数据少于一屏时,修改状态值为“初始化”,当状态为“到底”时,不进行操作,一共设计了4个状态(1-初始状态,全隐藏;2-正在加载,3-加载完成,4-加载到底,指没有更多数据了),后文具体交代;
因为把Adapter也封装到了RefreshLayout中,所以需要对外提供刷新和加载更多的方法,如图
到此处RefreshLayout.java就差不结束了,自定义控件相对来说还是比较简单;
二、封装Adapter(实现FooterView)
Adapter部分,其实主要就是给RecyclerView添加一个FooterView,首先定义一个泛型的BaseAbsAdapter抽象类(比较简单,也是为了项目方便使用),代码如下
public abstract class BaseAbsAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { protected List<T> mList; protected Context mContext; public BaseAbsAdapter(Context context) { mContext = context; mList = new ArrayList<>(); } /** * 下拉刷新 * @param list 列表 */ public abstract void onRefresh(List<T> list); /** * 加载更多 * @param list 列表 */ public abstract void onLoadMore(List<T> list); /** * 创建View * @param parent 父View * @param layout XML布局 * @return View */ protected View inflate(ViewGroup parent, int layout) { return LayoutInflater.from(mContext).inflate(layout, parent, false); } }
然后封装实现FooterView的WrapperAdapter类(这里何网络上的大部分实现功能都差不多,就不多累述,这里出现了上面说的4中状态,目前实现比较简单,就是对FooterView的操作,直接上代码)
class WrapperAdapter<T> extends BaseAbsAdapter<T> { private BaseAbsAdapter<T> mAdapter;// 适配器 private int mState = KeyUtils.LOAD_INIT;// 状态 WrapperAdapter(Context context, BaseAbsAdapter<T> adapter) { super(context); mAdapter = adapter; } /** * 设置状态 * @param state 状态 */ void onState(int state) { if (state == mState) return; mState = state; notifyDataSetChanged(); } @Override public void onRefresh(List<T> list) { mAdapter.onRefresh(list); notifyDataSetChanged(); } @Override public void onLoadMore(List<T> list) { mAdapter.onLoadMore(list); notifyDataSetChanged(); } /** * 获取当前状态 * @return 状态(初始、正在、完成、到底) */ int state() { return mState; } @Override public int getItemViewType(int position) { return position + 1 == getItemCount() ? KeyUtils.WRAPPER_FOOT : KeyUtils.WRAPPER_ITEM; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (KeyUtils.WRAPPER_ITEM == viewType)// 非FooterHolder return mAdapter.onCreateViewHolder(parent, viewType); return new FooterHolder(inflate(parent, R.layout.view_um_footer)); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { if (KeyUtils.WRAPPER_FOOT != getItemViewType(position)) {// 非Footer mAdapter.onBindViewHolder(holder, position); return; } ((WrapperAdapter.FooterHolder) holder).showView(mState); } @Override public int getItemCount() { return mAdapter.getItemCount() + 1; } /** * Footer的Holder */ private class FooterHolder extends ViewHolder { private LinearLayout[] mLayouts; private FooterHolder(View view) { super(view); mLayouts = new LinearLayout[] { view.findViewById(R.id.ll_wrapper_load), view.findViewById(R.id.ll_wrapper_end) }; } /** * 显示视图 * @param state 状态 */ private void showView(int state) { switch (state) { case KeyUtils.LOAD_INIT:// 初始状态 mLayouts[0].setVisibility(View.GONE); mLayouts[1].setVisibility(View.GONE); break; case KeyUtils.LOAD_ING:// 正在加载 mLayouts[0].setVisibility(View.VISIBLE); mLayouts[1].setVisibility(View.GONE); break; case KeyUtils.LOAD_OVER:// 加载完成 mLayouts[0].setVisibility(View.VISIBLE); mLayouts[1].setVisibility(View.GONE); break; case KeyUtils.LOAD_END:// 加载到底 mLayouts[0].setVisibility(View.GONE); mLayouts[1].setVisibility(View.VISIBLE); break; } } } @Override public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); final LayoutManager lm = recyclerView.getLayoutManager(); if (lm == null || !(lm instanceof GridLayoutManager)) return; final GridLayoutManager glm = (GridLayoutManager) lm; glm.setSpanSizeLookup(new SpanSizeLookup() { @Override public int getSpanSize(int position) { // GridView时,Footer占据的是整个横排,其他Item只占据1个单元格 return getItemViewType(position) == KeyUtils.WRAPPER_FOOT ? glm.getSpanCount() : 1; } }); } }
<?xml version="1.0" encoding="utf-8"?><!-- 加载更多的FooterView布局 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:id="@+id/ll_wrapper_load" android:layout_width="match_parent" android:layout_height="@dimen/l_60" android:gravity="center" android:orientation="horizontal"> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminateDrawable="@drawable/view_wrapper_progress" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/l_15" android:text="@string/sm_wrapper_load" android:textColor="@color/color_d_7" android:textSize="@dimen/s_16" /> </LinearLayout> <LinearLayout android:id="@+id/ll_wrapper_end" android:layout_width="match_parent" android:layout_height="@dimen/l_60" android:layout_marginLeft="@dimen/l_15" android:layout_marginRight="@dimen/l_15" android:gravity="center" android:orientation="horizontal"> <View android:layout_width="0dp" android:layout_height="1dp" android:layout_weight="1" android:alpha="0.5" android:background="@color/color_d_7" android:gravity="start" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:padding="@dimen/l_15" android:text="@string/sm_wrapper_end" android:textColor="@color/color_d_7" android:textSize="@dimen/s_16" /> <View android:layout_width="0dp" android:layout_height="1dp" android:layout_weight="1" android:alpha="0.5" android:background="@color/color_d_7" android:gravity="end" /> </LinearLayout> </LinearLayout>
三、RefreshLayout和WrapperAdapter都封装好了,接下来就是使用了
1、看一下Activity的布局文件,几行代码,简单至极
<?xml version="1.0" encoding="utf-8"?> <qc.lot.lib.refresh.RefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rl_um_refresh" android:layout_width="match_parent" android:layout_height="match_parent" />
2、看一下Activity的使用(添加回调、设置颜色、填充适配器)
回调需要重写的方法(上图前2个方法),这里有一个InitiateImpl类,同时Activity也实现了InitiateIF接口(上图中后2个方法),这里是用MVP模式搭建的框架,InitiateImpl类相当于Presenter层,InitiateIF接口是连接Presenter和View层的纽带;
InitiateImpl类实现数据逻辑,这里模拟了一下耗时操作:
3、填充适配器的地方,有一个InitateAdapter类,该类继承了BaseAbsAdapter,主要负责数据在RecyclerView中的填充,代码如下:
class InitiateAdapter extends BaseAbsAdapter<String> { InitiateAdapter(Context context) { super(context); } @Override public void onRefresh(List<String> list) { mList.clear(); mList.addAll(list == null ? mList : list); } @Override public void onLoadMore(List<String> list) { mList.addAll(list == null ? new ArrayList<String>() : list); } @Override public int getItemCount() { return mList.size(); } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new InitiateViewHolder(inflate(parent, R.layout.item_um_initiate)); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ((InitiateViewHolder) holder).show(mList.get(position)); } private class InitiateViewHolder extends RecyclerView.ViewHolder { private TextView mTvName; private InitiateViewHolder(View view) { super(view); mTvName = view.findViewById(R.id.tv_initiate_name); } private void show(String value) { mTvName.setText(value); } } }
四、总结
下拉刷新和加载更多都实现了,回顾一下MVP模式、数据不足一页时,FooterView的处理逻辑,眸然回首,是不是发现很简单。
来源:CSDN
作者:YinRH2014
链接:https://blog.csdn.net/YinRH2014/article/details/81032236