RecycleView配合SwipeRefreshLayout实现轻量级上拉刷新下拉加载,外加牛X的Adapter

来源:互联网 发布:java中设计模式详解 编辑:程序博客网 时间:2024/05/22 00:33

看过我这边文章的RecyclerView.Adapter的优化与封装的真的想说声抱歉,其实不需要继承BaseBean,只需要继承Object就可以了,而且更灵活了,当时不知道咋了,脑袋抽风了,多次了这一举。


好了,今天来一个更牛逼的神器RefreshLayout

效果图:

这里写图片描述

  • 实现了上拉刷新和下拉加载功能
  • 封装好的BaseAdapter和BaseViewHolder,将adapter交给Activity来管理
  • 实现了单击事件和长单击事件

类结构图:

这里写图片描述


这个demo我把之前的BaseAdapter和BaseViewHolder的封装都重新整理了一边,然后把BaseAdapter抽象给Activity来管理,因为我发现我在之前的博文中封装的BaseAdapter,然后让其他的adapter去继承他去操作,这个adapter根本就不需要多少的操作,而且有点冗余,好多的adapter,烦,所以我打算把BaseAdapter的管理交给Activity去实现,这样,整个项目只需要一个BaseAdapter和BaseViewHolder去操作就行了,真的是万能的适配器啊,项目瞬间清爽了起来。好了,今天先来说说实现思路:


因为下拉加载SwipeRefreshLayout大家都知道的,官方的,没什么好讲,但是在处理上拉加载的时候这个下拉在后面还需要去处理,所以,先来讲上拉加载更多。

上拉加载更多需要哪些实现呢?

  • 计算所有的RecycleView的item数量(即layoutManager.getItemCount())
  • 获取当前页面底部可见的item的position位置(即LayoutManager.findLastVisibleItemPosition())
  • 判断这个可见的position是否大于等于RecycleView的item数量减1(因为position是从0开始的)
  • 我们还需要一个getChildCount辅助方法,获取当前整个屏幕显示的item数量,目的是判断有无item,来处理item不满一屏幕的时候处理。
  • 当数据不满一屏幕的时候,SwipeRefreshLayout下拉的时候会触发我在onScrollStateChanged里面做的判断,然而这个判断是满足上拉加载更多的,会出现上拉加载刷新和下拉加载更多同时调用,然后看见数据突然的闪一下恢复到原来的数据,为了解决数据不满一屏的操作的bug,我在onTouch里面做了一个手势的处理,如果是向下拉,则给上拉加载更多的判断设置个flag为false,不让他去处理,亲测完美解决。

上代码,来看看RefreshRecycleView.java

/** * Created by wangqi on 2016/11/8. */public class RefreshRecycleView extends RecyclerView {    public RefreshRecycleView(Context context) {        super(context);        initView(context);    }    public RefreshRecycleView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public RefreshRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView(context);    }    private ProgressDialog progressDialog;    private void initView(Context context) {        progressDialog = new ProgressDialog(context);        progressDialog.setMessage("加载更多...");    }    @Override    public void onScrollStateChanged(int state) {        super.onScrollStateChanged(state);        //获取在各个不同的LayoutManager中可见的position位置        if (state == RecyclerView.SCROLL_STATE_IDLE && loadMoreListner != null && loadingMoreEnabled) {            LayoutManager layoutManager = getLayoutManager();            int lastVisibleItemPosition;            if (layoutManager instanceof GridLayoutManager) {                lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();            } else if (layoutManager instanceof StaggeredGridLayoutManager) {                int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];                ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);                lastVisibleItemPosition = findMax(into);            } else {                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();            }            //触发加载更多事件            if (flag && loadingMoreEnabled && layoutManager.getChildCount() > 0 && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() >= layoutManager.getChildCount()) {                progressDialog.show();                loadMoreListner.onLoad();            }        }    }    public boolean flag;    float startY = 0;    float moveY = 0;    /**     *     * @param e     * @return  处理数据不满一屏的手势判断     */    @Override    public boolean onTouchEvent(MotionEvent e) {        switch (e.getAction()) {            case MotionEvent.ACTION_DOWN:                startY = e.getRawY();                break;            case MotionEvent.ACTION_MOVE:                moveY = e.getRawY();                if (moveY > startY)                    flag = false;                else                    flag = true;                startY = moveY;                break;            case MotionEvent.ACTION_UP:                break;        }        return super.onTouchEvent(e);    }    public void setLoadMoreComplete() {        progressDialog.dismiss();    }    //设置不给上拉加载    public void setLoadingMoreEnabled(boolean loadingMoreEnabled) {        this.loadingMoreEnabled = loadingMoreEnabled;    }    private int findMax(int[] lastPositions) {        int max = lastPositions[0];        for (int value : lastPositions) {            if (value > max) {                max = value;            }        }        return max;    }    boolean loadingMoreEnabled = true;    LoadMoreListner loadMoreListner;    public void setLoadMoreListner(LoadMoreListner loadMoreListner) {        this.loadMoreListner = loadMoreListner;    }    public interface LoadMoreListner {        void onLoad();    }}

代码特别简单,

  • 在滑动事件里面去判断当前上否是滑动状态,然后判断上拉加载更多是否可用,当前的回调是否已经注册。
  • 获取RecycleView在不同LayoutManager的时候返回的底部可见的position的位置
  • layoutManager.getChildCount()是获取当前可见界面的item数量
  • lastVisibleItemPosition获取item可见的最底部的position位置
  • layoutManager.getItemCount()是获取整个RecycleView的item的总数
  • 然后对当前进行判断,
  • falg是处理onTouch返回的数据不满一屏幕的判断,
  • 其他的判断大家看看就懂的,不难
  • 然后显示dialog,回调load接口,给别人去处理

好了,这时候来看看包装RefreshRecycleView的类RefreshLayout.java了

看看xml就懂了

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/refresh_swip"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.e8net.myapplication.RefreshRecycleView        android:id="@+id/refresh_recycler"        android:layout_width="match_parent"        android:layout_height="match_parent" /></android.support.v4.widget.SwipeRefreshLayout>

RefreshLayout只是对RecycleView的一种包装,专门处理上拉刷新和下拉加载更多,将所有事件的处理通过这个辅助类传递给Activity的调用者,外者只需要调用,不需要管理实现原理,简洁了代码。来看看代码

/** * Created by wangqi on 2016/11/8. */public class RefreshLayout extends LinearLayout {    RefreshRecycleView refreshRecycleView;    SwipeRefreshLayout swipeRefreshLayout;    public RefreshLayout(Context context) {        super(context);        initView(context);    }    public RefreshLayout(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context);    }    private void initView(final Context context) {        View v = LayoutInflater.from(context).inflate(R.layout.refresh_view, this, false);        refreshRecycleView = (RefreshRecycleView) v.findViewById(R.id.refresh_recycler);        swipeRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.refresh_swip);        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);        this.addView(v, params);        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                onRefreshListner.refresh();            }        });        //加载更多        refreshRecycleView.setLoadMoreListner(new RefreshRecycleView.LoadMoreListner() {            @Override            public void onLoad() {                onRefreshListner.loadMore();            }        });    }    public void setLoadingMoreEnabled(boolean flag) {        refreshRecycleView.setLoadingMoreEnabled(flag);    }    public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {        refreshRecycleView.setLayoutManager(layoutManager);    }    public void setAdapter(RecyclerView.Adapter<BaseViewHolder> adapter) {        refreshRecycleView.setAdapter(adapter);    }    public void setRefreshComplete() {        swipeRefreshLayout.setRefreshing(false);    }    public void setLoadMoreComplete() {        refreshRecycleView.setLoadMoreComplete();    }    private RefreshListner onRefreshListner;    public void setOnRefreshListner(RefreshListner onRefreshListner) {        this.onRefreshListner = onRefreshListner;    }    public interface RefreshListner {        void refresh();        void loadMore();    }}
  • 初始化xml,将view界面inflater到RefreshLayout
  • 给下拉刷新和上拉加载进行注册
  • 然后设置下拉刷新完成和上拉加载完成,这个大家应该都懂的,全是包装
  • 然后注册个接口,将下拉和上拉的触发交个回调者来处理

这时候包装都结束了,最后来看看Activity来调用看看

  @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        refreshLayout = (RefreshLayout) findViewById(R.id.refresh_layout);        list = new ArrayList<>();        addList();        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);        refreshLayout.setLayoutManager(layoutManager);        myRecycleAdapter = new BaseRecycleAdapter(this, R.layout.recycle_item, list);        refreshLayout.setAdapter(myRecycleAdapter);        refreshLayout.setOnRefreshListner(new RefreshLayout.RefreshListner() {            @Override            public void refresh() {                refreshLayout.postDelayed(new Runnable() {                    @Override                    public void run() {                        list.clear();                        addList();                        myRecycleAdapter.notifyDataSetChanged();                        refreshLayout.setRefreshComplete();                    }                }, 2000);            }            @Override            public void loadMore() {                refreshLayout.postDelayed(new Runnable() {                    @Override                    public void run() {                        addList();                        myRecycleAdapter.notifyDataSetChanged();                        refreshLayout.setLoadMoreComplete();                    }                }, 2000);            }        });      }         public void addList() {        for (int i = 0; i < 10; i++)            list.add(""); }

下拉和上拉就这么搞定了,哈哈,不对不对,你这个adapter还没讲呢,对吼,那赶紧的跟上步伐


说说我这个牛X的adapter,所有项目的RecycleView都只需要这一个adapter就可以搞定了,如果item需要复杂的处理,可以拓展BaseViewHolder方法,主要思路就是,将数据交给adapter,adapter不做处理,只是对数据的size进行排版item,数据的设置回调给Activity去实现,Activity拿到数据去BaseViewHolder里面去设置界面显示数据,adapter里面实现了单击和长单击事件,和我之前那篇博文一样的思路,那来看看代码吧

BaseRecycleAdapter.java

/** * Created by wangqi on 2016/7/16. */public class BaseRecycleAdapter extends RecyclerView.Adapter<BaseViewHolder> {    private int layoutId;    private List<? extends Object> data;    public Context context;    private OnItemClickListner onItemClickListner;//单击事件    private OnItemLongClickListner onItemLongClickListner;//长按单击事件    private boolean clickFlag = true;//单击事件和长单击事件的屏蔽标识    /**     * @param context  //上下文     * @param layoutId //布局id     * @param data     //数据源     */    public BaseRecycleAdapter(Context context, int layoutId, List<? extends Object> data) {        this.layoutId = layoutId;        this.data = data;        this.context = context;    }    @Override    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View v = LayoutInflater.from(context).inflate(layoutId, parent, false);        final BaseViewHolder holder = new BaseViewHolder(v, context);        //单击事件回调        v.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                if (onItemClickListner == null)                    return;                if (clickFlag) {                    onItemClickListner.onItemClickListner(v, holder.getLayoutPosition());                }                clickFlag = true;            }        });        //单击长按事件回调        v.setOnLongClickListener(new View.OnLongClickListener() {            @Override            public boolean onLongClick(View v) {                if (onItemLongClickListner == null)                    return false;                onItemLongClickListner.onItemLongClickListner(v, holder.getLayoutPosition());                clickFlag = false;                return false;            }        });        return holder;    }    @Override    public void onBindViewHolder(BaseViewHolder holder, int position) {        if (mCallBack != null)            mCallBack.convert(holder, data.get(position), position);    }    @Override    public int getItemCount() {        return data.size();    }    public void setOnItemClickListner(OnItemClickListner onItemClickListner) {        this.onItemClickListner = onItemClickListner;    }    public void setOnItemLongClickListner(OnItemLongClickListner onItemLongClickListner) {        this.onItemLongClickListner = onItemLongClickListner;    }    public interface OnItemClickListner {        void onItemClickListner(View v, int position);    }    public interface OnItemLongClickListner {        void onItemLongClickListner(View v, int position);    }    CallBack mCallBack;    public void setCallBack(CallBack CallBack) {        this.mCallBack = CallBack;    }    public interface CallBack {        <T extends Object> void convert(BaseViewHolder holder, T bean, int position);    }}

这一次adapter的修改,主要是对当初的抽象onBindViewHolder方法变成了接口回调,当初需要继承这个Base,然后实现这个convert方法来对数据进行操作,但这个时候感觉继承这个adapter的使用者,完全不需要那么多的操作,只是对数据操作了下,大功能基本上是在BaseViewHolder处理,这一次整改,将这个设置器交给Activity来处理,还有就是数据的泛型,上一遍博文让所有的Model都继承BaseBean,这个真是多此一举啊,哎,失败,这里我给改成Object,方便传递,其他的和我上篇博文一样,可以看看,文章开头给了链接地址。

然后来看看BaseViewHolder.java

/** * Created by wangqi on 2016/7/16. */public class BaseViewHolder extends RecyclerView.ViewHolder {    View convertView;    Context context;    public BaseViewHolder(View itemView, Context context) {        super(itemView);        this.convertView = itemView;        this.context = context;    }    public View getItemView() {        return convertView;    }    public void setText(int id, String text) {        TextView tx = (TextView) convertView.findViewById(id);        tx.setText(text);    }    public void setText(int id, String text, final OnClickListener onClickListener) {        TextView tx = (TextView) convertView.findViewById(id);        tx.setText(text);        tx.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                onClickListener.onClickListner(v);            }        });    }    public void setImageListner(int id, final OnClickListener onClickListener) {        ImageView img = (ImageView) convertView.findViewById(id);        img.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                onClickListener.onClickListner(v);            }        });    }    public void setImageResource(int id, int resouceId) {        ImageView img = (ImageView) convertView.findViewById(id);        img.setImageResource(resouceId);    }    public void setImageResource(int id, int resouceId, final OnClickListener onClickListener) {        ImageView img = (ImageView) convertView.findViewById(id);        img.setImageResource(resouceId);        img.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                onClickListener.onClickListner(v);            }        });    }    public interface OnClickListener {        void onClickListner(View v);    }}

这里面的方法是需要开发者根据需求自己去定制添加方法的,我这里只是列举了一些常见的,大家可以自己扩展,大家可能看到我给的回调,如果你当前界面不需要设置监听,你完全可以直接调用设置数据源的方法,如果你需要item的子View触发事件,也可以调用有回调接口的方法,这个回调也是交给Activity去处理

来看看完整的MainActivity的代码

public class MainActivity extends AppCompatActivity {    private RefreshLayout refreshLayout;    List<String> list;    BaseRecycleAdapter myRecycleAdapter;    public void addList() {        for (int i = 0; i < 10; i++)            list.add("");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        refreshLayout = (RefreshLayout) findViewById(R.id.refresh_layout);        list = new ArrayList<>();        addList();        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);        refreshLayout.setLayoutManager(layoutManager);        myRecycleAdapter = new BaseRecycleAdapter(this, R.layout.recycle_item, list);        refreshLayout.setAdapter(myRecycleAdapter);        refreshLayout.setOnRefreshListner(new RefreshLayout.RefreshListner() {            @Override            public void refresh() {                refreshLayout.postDelayed(new Runnable() {                    @Override                    public void run() {                        list.clear();                        addList();                        myRecycleAdapter.notifyDataSetChanged();                        refreshLayout.setRefreshComplete();                    }                }, 2000);            }            @Override            public void loadMore() {                refreshLayout.postDelayed(new Runnable() {                    @Override                    public void run() {                        addList();                        myRecycleAdapter.notifyDataSetChanged();                        refreshLayout.setLoadMoreComplete();                    }                }, 2000);            }        });        myRecycleAdapter.setOnItemClickListner(new BaseRecycleAdapter.OnItemClickListner() {            @Override            public void onItemClickListner(View v, int position) {                Toast.makeText(MainActivity.this, "click---" + position, Toast.LENGTH_SHORT).show();            }        });        myRecycleAdapter.setOnItemLongClickListner(new BaseRecycleAdapter.OnItemLongClickListner() {            @Override            public void onItemLongClickListner(View v, int position) {                Toast.makeText(MainActivity.this, "longClick---" + position, Toast.LENGTH_SHORT).show();            }        });        myRecycleAdapter.setCallBack(new BaseRecycleAdapter.CallBack() {            @Override            public <T> void convert(BaseViewHolder holder, T bean, final int position) {                holder.setText(R.id.txt, "明天会更好" + position, new BaseViewHolder.OnClickListener() {                    @Override                    public void onClickListner(View v) {                        Toast.makeText(MainActivity.this, "明天会更好" + position, Toast.LENGTH_SHORT).show();                    }                });                holder.setImageListner(R.id.img, new BaseViewHolder.OnClickListener() {                    @Override                    public void onClickListner(View v) {                        Toast.makeText(MainActivity.this, "img" + position, Toast.LENGTH_SHORT).show();                    }                });            }        });    }}

看完后有没有为之一振的感觉呢,Activity不去管那些复杂的操作,只做一些简单结果处理,确实有那么点意思,adapter也只需要给xml布局和数据源,不再去管理复杂操作,想怎么玩就怎么玩,所有项目的RecycleView都可以用这一个adapter,Activity立马解决数据操作,神器,绝对的神器,为了实现效果早点装逼,代码质量还需要一些规范分类(关键是懒,哈哈),里面用了好多的接口回调,也是醉了,不过这种结果处理,真是屡试不爽啊,好咯,下午马上就要上实验课了,作业还一大堆,不会操作就只会下位来装逼指导学生,每天上课之前ppt放映到”系主任”几个字的那个页面,真是受够了——————————愤青少年


最后附上github项目链接来star吧

1 0
原创粉丝点击