RecyclerView刷新加载库-SRecyclerView

来源:互联网 发布:多媒体教学软件价格 编辑:程序博客网 时间:2024/06/05 17:36

前言


最近在使用前段时间 “业余时间" 项目中封装的 RecyclerView 时,发现了好几个问题,比如:刷新头部和加载尾部耦合性太高,要是以后想换个刷新头部或加载尾部,必然改动很大,不利于扩展,刷新手势处理和加载更多的逻辑也有点小问题,于是乎,换了一种思路重新封装了一下,核心思想就是:该控件的一些配置,比如刷新头部和加载尾部,刷新头的高度,动画时间等等,都方便扩展和重设置,而且可以很方便的去重写一个刷新头,并且满足某个刷新列表的特殊性要求。封装完成后干脆重命名为SRecyclerView,并上传到JCenter,方便以后更新和维护

功能

主要功能有以下几点
  1. 下拉刷新,滑到底部加载(也支持GridLayoutManager)
  2. 支持添加多个头部和尾部(也支持GridLayoutManager)
  3. 支持代码设置一个刷新头部和加载尾部(某个列表有特殊的刷新头部和加载尾部的需求)
  4. 支持自定义刷新头部和加载尾部,手势等逻辑已处理,只需写刷新界面逻辑即可
  5. 可以全局配置刷新头部和加载尾部
  6. 支持设置LinearLayoutManager的分割线,以及纵向时分割线的左右距离
  7. 支持设置一个EmptyView
  8. 支持Item的点击事件
  9. 附带一个简易的适配器,大大减少适配器的代码
  10. 默认设置为纵向的LinearLayoutManager

                  

刷新和加载扩展


为了方便扩展刷新头部和加载尾部,这里抽象出一个头部基类 AbsRefreshHeader 和尾部基类  AbsLoadFooter ,在基类中完成一些基础性的操作和逻辑,比如在头部基类 AbsRefreshHeader 中,处理了头部的手势拖拽操作和刷新逻辑,并预留出抽象方法,可以方便子类处理自己的刷新界面的更新,SRecyclerView 中默认实现了一个刷新头部,自己的自定义头部刷新完全可以仿照此实现, 头部基类 AbsRefreshHeader 的代码就不贴了,可以去看源码。我们可以在 init 方法中完成初始化自己的布局,refresh 方法会在整个刷新过程中被调用,按照四种不同的状态更新自己的UI界面,如果需要更改刷新高度(刷新临界值),刷新动画时间,刷新头部的Gravity,可以重写对应的方法,重新设置。
public class TestRefreshHeader extends AbsRefreshHeader {    private TextView refreshText;    private ClockView clockView;    public TestRefreshHeader(Context context) {        super(context);    }    public TestRefreshHeader(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public TestRefreshHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public void init() {        View view = LayoutInflater.from(getContext()).inflate(R.layout.refresh_view, this, false);        clockView = (ClockView) view.findViewById(R.id.v_refresh);        refreshText = (TextView) view.findViewById(R.id.tv_refresh);        addView(view);    }    /**     * 如果需要设置刷新动画时间,可以重写此方法     */    @Override    public int getRefreshDuration() {        return 300;    }    /**     * 如果需要设置头部的Gravity,可以重写此方法     *     * @return HEADER_CENTER,HEADER_BOTTOM     */    @Override    public int getRefreshGravity() {        return AbsRefreshHeader.HEADER_CENTER;//        return AbsRefreshHeader.HEADER_BOTTOM;    }    /**     * 如果需要设置刷新高度,也就是刷新临界值,可以重写此方法     */    @Override    public int getRefreshHeight() {        return dip2px(70);    }    private int dip2px(float value) {        final float scale = Resources.getSystem().getDisplayMetrics().density;        return (int) (value * scale + 0.5f);    }    /**     * SRecyclerView的onDetachedFromWindow被调用,可能SRecyclerView所在的界面要被销毁,     * 如果子类中有动画等未完成,可以重写此方法取消动画等耗时操作,避免造成内存泄露     */    @Override    public void srvDetachedFromWindow() {        if(clockView!=null){            clockView.resetClock();        }    }    @Override    public void refresh(int state, int height) {        switch (state) {            case NORMAL:                refreshText.setText("下拉刷新");                clockView.stopClockAnim();                break;            case REFRESH:                refreshText.setText("正在刷新...");                clockView.startClockAnim();                break;            case PREPARE_NORMAL:                refreshText.setText("下拉刷新");                clockView.setClockAngle(height);                break;            case PREPARE_REFRESH:                refreshText.setText("释放立即刷新");                clockView.setClockAngle(height);                break;        }    }}

同样的,如果默认的加载尾部功能或者样式不满足,也可以继承AbsLoadFooter打造项目专属的加载尾部,栗子如下:
public class TestLoadFooter extends AbsLoadFooter {    private View load, noMore;    public TestLoadFooter(Context context) {        super(context);    }    public TestLoadFooter(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public TestLoadFooter(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public void init() {        View loadView = LayoutInflater.from(getContext()).inflate(R.layout.srv_load_footer, this, false);        noMore = loadView.findViewById(R.id.tv_src_loadNoMore);        load = loadView.findViewById(R.id.v_srv_loading);        addView(loadView);    }    @Override    public void loadBegin() {        load.setVisibility(VISIBLE);        noMore.setVisibility(GONE);    }    @Override    public void loadEnd() {        load.setVisibility(GONE);        noMore.setVisibility(GONE);    }    /**     * 据说有一种需求是,没数据时,直接不显示无数据的UI,此时可设置高度为0,然后重写reset()方法,     * 当列表刷新时会重置加载尾部     */    @Override    public void loadingNoMoreData() {        ViewGroup.LayoutParams params = getLayoutParams();        params.height = 0;        setLayoutParams(params);    }    /**     * 刷新结束后如果需要重置加载尾部,可重写此方法重置LoadFooter     */    @Override    public void reset() {        ViewGroup.LayoutParams params = getLayoutParams();        params.height = dip2px(45);        setLayoutParams(params);    }    private int dip2px(float value) {        final float scale = Resources.getSystem().getDisplayMetrics().density;        return (int) (value * scale + 0.5f);    }}


全局配置


那自己实现的头部和尾部怎么一劳永逸的设置给 SRecyclerView 呢?这里我借鉴了 Glide 配置缓存路劲的做法(果然还是大神们厉害),和 Glide 类似,我们新建一个类并实现 SRecyclerViewModule 接口,然后在 AndroidManifest.xml 中配置一下即可,实现原理很简单,每次创建 SRecyclerView 时读取  AndroidManifest.xml 中是否有此配置,如果有此配置则使用,否则使用默认的配置。测试代码如下:
public class TestSRVModule implements SRecyclerViewModule {    @Override    public AbsRefreshHeader getRefreshHeader(Context context) {        return new TestRefreshHeader(context);    }    @Override    public AbsLoadFooter getLoadingFooter(Context context) {        return TestLoadFooter(context);    }    }
在 AndroidManifest.xml 中的配置,注意 value 必须为 SRecyclerViewModule
<meta-data            android:name="com.hzw.srecyclerviewproject.TestSRVModule"            android:value="SRecyclerViewModule" />
代码里也提供了设置一个刷新头部和加载尾部的方法,主要是为了满足某个列表有特殊刷新样式的需求,具体看github上的代码吧

使用

注意:SRecyclerView中已经导入了support design库(com.android.support:design:xxx),请勿重复导入

混淆
-keep public class * implements com.hzw.srecyclerview.SRecyclerViewModule
使用
gradle中添加: compile 'com.github.hzw:srecyclerview:github上的版本'
recyclerView.setLoadListener(new SRecyclerView.LoadListener() {            @Override            public void refresh() {                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        refreshData();                        recyclerView.refreshComplete();                    }                }, 2000);            }            @Override            public void loading() {                new Handler().postDelayed(new Runnable() {                    @Override                    public void run() {                        if (list.size() != 30) {                            loadData();                            recyclerView.loadingComplete();                        } else {                            recyclerView.loadNoMoreData();                        }                    }                }, 2000);            }        });        //item的点击事件        recyclerView.setItemClickListener(new SRecyclerView.ItemClickListener() {            @Override            public void click(View v, int position) {                Toast.makeText(getApplication(), "位置:  " + position, Toast.LENGTH_SHORT).show();            }        });        //可以设置一个EmptyView        recyclerView.setEmptyView(emptyView);        //可以手动设置一个刷新头部,应该在setAdapter方法之前调用,适用于某个列表需要特殊刷新头的场景        //SRecyclerView的头部设置有两种种方法:代码设置,全局配置。如果两种方法都没有设置,则适用默认自带的默认刷新头和加载尾        //recyclerView.setRefreshHeader(new TestRefreshHeader(this));        //recyclerView.setLoadingFooter(new TestLoadFooter(this));        //这里的适配器使用的一个简易的SRV适配器,同样也可以用于普通的RecyclerView,当然这里也可以用原生的适配器        recyclerView.setAdapter(new SRVAdapter(list));        //recyclerView.setAdapter(new InitAdapter(list, this));        //测试添加头部        View header = LayoutInflater.from(this).inflate(R.layout.header_test, recyclerView, false);        recyclerView.addHeader(header);        //测试添加尾部        View footer = LayoutInflater.from(this).inflate(R.layout.footer_test, recyclerView, false);        recyclerView.addFooter(footer);        //SRV的代码刷新,应该在setAdapter方法之后调用,true表示会有刷新动画,false无任何动画        //recyclerView.startRefresh(true);

自带的用户简化代码的适配器使用栗子如下,可以看到相比于原生的适配器写法,能减少不少代码,当然这个适配器也可以用于其它的RecyclerView
    /**     * 基于简易适配器的写法     */    private static class SRVAdapter extends BaseSRVAdapter<String> {        SRVAdapter(List<String> list) {            super(list, R.layout.item_test);        }        @Override        public void onBindView(SRVHolder holder, String data, int i) {            holder.setTextView(R.id.tv_item_test, data);//            holder.setTextView(0, "123")//                    .setTextView(1, "234");        }    }


END

至此我平常开发中能用到的功能和一些应用场景已尽可能实现,项目已上传到GitHub,library已提交到JCenter上,可以很方便的引用,后续有什么问题,我会及时更新处理。使用方法和注释我在测试代码中写的尽可能详细了,具体看代码吧!



代码:https://github.com/HzwSunshine/SRecyclerView