zoukankan      html  css  js  c++  java
  • 一个漂亮而强大的RecyclerView

    代码地址如下:
    http://www.demodashi.com/demo/13470.html

    简介

    主要提供了简单易用强大的RecyclerView库,包括自定义刷新加载效果、极简通用的万能适配器Adapter、万能分割线、多种分组效果、常见状态页面、item动画效果、添加多个header和footer、侧滑、拖拽、Sticky(黏性)效果、多item布局等,各模块之间灵活、解耦、通用、又能相互组合使用。

    功能

    • 优化RecyclerView功能
    • 支持刷新和加载更多效果,滑动到底部自动加载下页数据
    • 支持自定义刷新和加载更多效果
    • 支持添加多个header和footer,一行代码搞定
    • 支持动态加载各种Item类型的列表、网格、瀑布流
    • 支持极简通用的万能适配器Adapter,可以接受任意数据实体
    • 支持万能的分割线、设置高度(宽度)、颜色、自定义等
    • 支持点击和长按效果
    • 支持Item加载动画效果
    • 支持各种分组、随心定义分组头部、尾部、多类型内容item
    • 支持侧滑、拖拽
    • 支持Sticky(黏性)效果
    • 支持多item布局
    • 支持状态页面,加载中、空页面、错误页面、内容页面切换
    • 支持自定义万能adapter、holder

    演示










    XRecyclerView使用介绍

    XRecyclerView基于RecyclerView的封装,在原有功能的基础上支持刷新、加载更多、自定义刷新加载更多、添加头部、尾部等

    添加(单个/多个)HeaderViw、FooterView

    可以添加单个或者多个普通HeaderViw、FooterView,支持内容为LinearLayoutManager和GridLayoutManager。
    方式一:

    //添加一个头部view
    mRecyclerView.addHeaderView(new SampleHeader(this));
    //mRecyclerView.addHeaderView(new SampleHeader2(this));//添加第二个头部view
    ....
    //mRecyclerView.addHeaderView(new SampleHeaderN(this));//添加第n个头部view(多头部)
    
    //添加一个尾部view
    mRecyclerView.addFooterView(new SampleFooter(this));
    //mRecyclerView.addFooterView(new SampleFooter2(this));//添加第二个尾部view
    ....
    //mRecyclerView.addFooterView(new SampleFooterN(this));//添加第n个尾部view(多尾部)
    

    方式二:

    //添加一个头部view
    View headerView =   LayoutInflater.from(this).inflate(R.layout.layout_header, (ViewGroup)findViewById(android.R.id.content),false);
    mRecyclerView.addHeaderView(headerView);
    //mRecyclerView.addHeaderView(headerView2);//添加第二个头部view
    ...
    //mRecyclerView.addHeaderView(headerViewN);//添加第n个头部view(多头部)
    //点击事件
    headerView.findViewById(R.id.test_txt).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ...
        }
    });
    
    //添加一个尾部view
    View footerView = getLayoutInflater().inflate(R.layout.layout_footer, (ViewGroup) mRecyclerView.getParent(), false);
    mRecyclerView.addFooterView(footerView);
    //mRecyclerView.addFooterView(footerView2);//添加第二个尾部view
    ....
    //mRecyclerView.addFooterView(footerViewN);//添加第n个尾部view(多尾部)
    
    //点击事件
    footerView.findViewById(R.id.test_txt).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ...
        }
    });
    

    注:此功能只是添加普通的头部和尾部,与刷新和加载更多的头部和尾部是不一样的,切记!不会影响刷新头和加载更多尾部的显示问题

    移除HeaderViw、FooterView

    //移除添加的头部view
    mRecyclerView.removeHeaderView(headerView);
    //移除添加的尾部view
    mRecyclerView.removeFooterView(footerView);
    

    下拉刷新和加载更多样式

    //设置下拉刷新Progress的样式
    mRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader); //在ProgressStyle里面选择喜欢的样式
    //设置下拉刷新箭头
    mRecyclerView.setArrowImageView(R.drawable.iconfont_downgrey);
    //设置加载更多Progress的样式
    mRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallPulse);
    

    注:1.如果采用自定义下拉刷新setRefreshHeader()和加载更多setLoadingMoreFoote(),以上设置会不起作用,也无须设置。

    开启和禁止下拉刷新、加载更多功能

    //打开刷新和加载更多
    mRecyclerView.setPullRefreshEnabled(true);
    mRecyclerView.setLoadingMoreEnabled(true);
    

    注:默认是开启。

    设置底部加载文字提示

    mRecyclerView.setFootViewText("拼命加载中","已经全部");
    

    注:自定义加载更多View时,此设置不起作用,切记!

    下拉刷新完成

     mRecyclerView.refreshComplete();
    

    上拉加载更多完成

     mRecyclerView.loadMoreComplete();
    

    数据加载完成

     mRecyclerView.setNoMore(true);
    

    开启进入页面自动刷新

    mRecyclerView.setRefreshing(true);//没有更多数据
    
    

    例:在Acitvity中开启进入页面自动刷新,在onStart()中处理。

    @Override
    protected void onStart() {
        super.onStart();
        if(mRecyclerView !=null){
            mRecyclerView.setRefreshing(true);
        }
    }
    

    自定义下拉刷新View

    1. 自定义view继承BaseRefreshHeader;
    2. 调用setRefreshHeader(IRefreshHeader refreshHeader)即可。
     mRecyclerView.setRefreshHeader(new CustomRefreshHeader(this));
    

    示例:

    public class CustomRefreshHeader extends BaseRefreshHeader {
        private CustomAnimView mCustomAnimView;
        public CustomRefreshHeader2(Context context) {
            super(context);
        }
    
        public CustomRefreshHeader2(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public View getView() {
            //方式一  
            mCustomAnimView = new CustomAnimView(getContext());
            return  mCustomAnimView;
            //方式二
            //return LayoutInflater.from(getContext()).inflate(R.layout.clife_loading_header,null);
        }
        
        //根据状态改变View
        @Override
        public void setState(int state) {
            super.setState(state);
            //选择自定义需要处理的状态:STATE_NORMAL、STATE_RELEASE_TO_REFRESH、STATE_REFRESHING、STATE_DONE
            //以下是我自定义动画需要用到的状态判断,你可以根据自己需求选择。
            if (state == STATE_REFRESHING) {    // 显示进度
                //这里处理自己的逻辑、刷新中
                mCustomAnimView.startAnim();
            } else if (state == STATE_DONE) {
                //这里处理自己的逻辑、刷新完成
                mCustomAnimView.stopAnim();
            } else {
                mCustomAnimView.startAnim();
            }
        }
        
    
       /* @Override
        public void refreshComplete() {
            //有默认处理、可以覆盖该方法处理,刷新完成的动作
        }*/
    
        /*@Override
        public void smoothScrollTo(int destHeight) {
            //super.smoothScrollTo(destHeight);
            //有默认处理、可以覆盖该方法处理,顺滑改变高度
        }*/
        
        //更多方法,可以通过覆写实现,自定义
    }
    

    注:如果设置了自定义下拉刷新,setRefreshProgressStyle()会不起作用,无需配置,要么采用库里默认支持的样式设置,要么采用自定义View。

    自定义加载更多View

    1. 自定义view实现BaseMoreFooter接口;
    2. 调用setLoadingMoreFooter(IMoreFooter refreshHeader)即可。
     mRecyclerView.setLoadingMoreFooter(new CustomMoreFooter(this));
    

    示例:

    public class CustomMoreFooter extends BaseMoreFooter {
        private AnimationDrawable mAnimationDrawable;
        private LinearLayout allLayout;
        private TextView mTextView;
    
        public CustomMoreFooter(Context context) {
            super(context);
        }
    
        public CustomMoreFooter(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void initView() {
            super.initView();//有居中显示功能,如果不需要就去掉super.initView();
            allLayout = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.pull_to_refresh_clife, null);
            ImageView imageView = (ImageView) allLayout.findViewById(R.id.lodimg);
            imageView.setImageResource(R.drawable.icon_loading_animation);
            mAnimationDrawable = (AnimationDrawable) imageView.getDrawable();
            mTextView = (TextView) allLayout.findViewById(R.id.lodtext);
            addView(allLayout);
        }
    
        @Override
        public void setState(int state) {
            super.setState(state);
            //以下是我自定义动画需要用到的状态判断,你可以根据自己需求选择。
            //选择自定义需要处理的状态:STATE_LOADING、STATE_COMPLETE、STATE_NOMORE、STATE_NOMORE
            switch (state) {
                case STATE_LOADING:
                    this.setVisibility(View.VISIBLE);
                    mAnimationDrawable.start();
                    mTextView.setText("努力加载中...");
                    mTextView.setVisibility(VISIBLE);
                    break;
                case STATE_COMPLETE:
                    this.setVisibility(View.GONE);
                    mAnimationDrawable.stop();
                    mTextView.setText("加载完成");
                    break;
                case STATE_NOMORE:
                    mAnimationDrawable.stop();
                    mTextView.setText("没有更多");
                    this.setVisibility(View.GONE);
                    break;
            }
        }
    }
    

    注:如果设置了自定义下拉刷新,setRefreshProgressStyle()会不起作用,无需配置,要么采用库里默认支持的样式设置,要么采用自定义View。

    滑动和加载更多事件监听

    mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
        @Override
        public void onRefresh() {
            //下拉刷新
            ...
        }
    
        @Override
        public void onLoadMore() {
            //加载更多
            ...
        }
    });
    

    设置分割线

    分割线可以采用库中提供的自定义万能分割线HorizontalDividerItemDecoration和XHorizontalDividerItemDecoration,这里只做使用介绍,详细请看demo中关于分割线的示例。

    //例1
    recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this).build());
    //例2
    recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this)
            .paint(paint)
            .margin(15)
            .showLastDivider()
            .build());
    //例3 
    recyclerView.addItemDecoration(new com.zhouyou.recyclerview.divider.HorizontalDividerItemDecoration.Builder(this)
            //.drawable(R.drawable.divider_sample)//.9图
            .drawable(R.drawable.divider_shape)//shape文件
            .size(10)
            .build());
    //例4
    Paint paint = new Paint();
            paint.setStrokeWidth(5);
            paint.setColor(Color.BLUE);
            paint.setAntiAlias(true);
            paint.setPathEffect(new DashPathEffect(new float[]{15.0f, 15.0f}, 0));
    recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this)
                    .paint(paint)
                    .margin(15)
                    .startSkipCount(2)
                    .endSkipCount(2)
                    .build());
    .....
    //更多请看分割线讲解
    

    注:此分割线主要是针对LinearLayoutManager使用,对GridLayoutManager和StaggeredGridAdapter支持性比较差,针对这一类的分割线还需要自定义实现。

    设置侧滑菜单

    设置侧滑菜单需要用到自定义SwipeMenuRecyclerView,此view是继承自XRecyclerView,具备XRecyclerView的所有功能和属性。
    1.Xml布局引用SwipeMenuRecyclerView

     <com.zhouyou.recyclerview.swipemenu.SwipeMenuRecyclerView
            android:id="@+id/super_swipemenu_recycle_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    

    2.侧滑菜单对适配器没有特殊要求可以继承任意Adapter,但是对item的布局文件有要求,需要用SwipeMenuLayout。
    例如:adapter_menu_layout_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!--布局外层必须用SwipeMenuLayout-->
    <com.zhouyou.recyclerview.swipemenu.SwipeMenuLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                        android:layout_width="match_parent"
                                                        android:layout_height="wrap_content">
        <!--内容布局id必须用smContentView,不能自定义id名称,否则找不到-->
        <include
            android:id="@+id/smContentView"
            layout="@layout/item_swipe_menu_content" />
        <!--菜单布局id必须用smMenuView,不能自定义id名称,否则找不到-->
        <include
            android:id="@+id/smMenuView"
            layout="@layout/item_swipe_menu1" />
    
    </com.zhouyou.recyclerview.swipemenu.SwipeMenuLayout>
    

    注:其它关于SwipeMenuLayout更多使用方法,请参考demo示例。

    设置sticky悬停

    主要提供了两种悬停效果:一种是鸿洋大神的StickyNavLayout,另一种是用StickyNestedScrollView(继承自V4包NestedScrollView进行扩展)。为什么会把这两种悬停效果放到库中处理呢,因为XRecyclerView、StickyNavLayout/StickyNestedScrollView、Tablayout、ViewPager、HelperRecyclerViewAdapter等相互组合使用会存在冲突等问题,本库中都已经解决了存在的已知兼容问题,互相打通,另一方面也是收集常用功能对EasyXRecyclerView的完善。
    StickyNavLayout使用介绍:

    可以设置页面某个控件滑动悬停,底部内容区域支持 ScrollView ,ListView,RecyclerView。
    注意控件id的设置

    • top区域:id必须为 android:id="@+id/id_stickynavlayout_topview"
    • 悬浮区域:id必须为android:id="@+id/id_stickynavlayout_indicator"
    • 内容区域:id必须为android:id="@+id/id_stickynavlayout_viewpager"

    内容区域

    • 此方式内容区域必须需为ViewPager或者其子类
    • ViewPager的内容可以是Fragment,如果Fragment里面是ListView,RecycleView等需要设置其id为:android:id="@+id/id_stickynavlayout_innerscrollview"

    在xml中引入:

    <?xml version="1.0" encoding="utf-8"?>
    <com.zhouyou.recyclerview.sticky.StickyNavLayout
        android:id="@+id/id_stick"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:isStickNav="false">
        <!--头部布局 id必须设置为:id_stickynavlayout_topview-->
        <LinearLayout
            android:id="@id/id_stickynavlayout_topview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <!--写头部区域内容-->
            ...
        </LinearLayout>
    
        <!--需要悬停内容的布局 id必须设置为:id_stickynavlayout_indicator-->
        <LinearLayout
            android:id="@id/id_stickynavlayout_indicator"
              ...
            >
            <!--写悬停部区域内容-->
            ...
        </LinearLayout>
        <!--底部内容区域布局,ViewPager id必须设置为:id_stickynavlayout_viewpager-->
        <!--如果Viewpager里面装载的内容为ListView,RecycleView、XRecycleView等需要设置其id为id_stickynavlayout_innerscrollview-->
        <android.support.v4.view.ViewPager
            android:id="@id/id_stickynavlayout_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    </com.zhouyou.recyclerview.sticky.StickyNavLayout>
    

    注:更多使用功能请参考鸿洋大神的讲解。
    StickyNestedScrollView使用介绍:

    StickyNestedScrollView方式使用比较简单,可以支持设置多个悬停。只需要在需要悬停的控件上设置tag属性,tag属性可以设置为:sticky、sticky-nonconstant、sticky-hastransparency,一般直接使用 android:tag="sticky"
    在xml中引入:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">
        <com.zhouyou.recyclerview.sticky.StickyNestedScrollView
            android:id="@+id/scroll_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <!--只要将需要悬停的控件设置tag为:sticky就可以悬停-->
                <TextView
                     ...
                    android:tag="sticky"
                    android:text="A"/>
                <!--B没有设置tag就不会悬停-->
                <TextView
                ...
                android:text="B"/>
                <!--C设置tag就会和A一样可以悬停-->
                <TextView
                ...
                android:tag="sticky"
                android:text="B"/>
    
            </LinearLayout>
        </com.zhouyou.recyclerview.sticky.StickyNestedScrollView>
    </android.support.design.widget.CoordinatorLayout>
    

    StickyNestedScrollView支持的方法addOnViewStickyListener(OnViewStickyListener stickyListener)、removeOnViewStickyListener(OnViewStickyListener stickyListener)、clearOnViewStickyListener()、setShadowHeight(int height)、setShadowDrawable(Drawable shadowDrawable)。如果XRecyclerview适配器中的某个item需要悬停,只需要将item布局设置 android:tag="sticky"即可实现item悬停,但是这样会有一个问题,如果在xml中静态设置所有的item都会有悬停,可以在设配器中获取到item布局对象根据业务逻辑进行动态设置view.setTag("sticky");

    StickyNavLayout与StickyNestedScrollView对比

    1. StickyNavLayout使用起来相对于StickyNestedScrollView比较麻烦,特别是要注意Id的设置,StickyNestedScrollView使用更加便捷一些。
    2. StickyNavLayout支持单个悬停,StickyNestedScrollView支持多个悬停。
    3. StickyNestedScrollView不适合与带有刷新和加载更多的Recyclerview嵌套,嵌套后Recyclerview的加载更多会失效,而StickyNavLayout不会影响加载跟多功能。
    4. 如果内容比较多建议采用StickyNavLayout+Recyclerview来实现分页带有头部的列表悬停功能,StickyNestedScrollView+Recyclerview不能装载太多的数据特别是用于分页。因为StickyNestedScrollView原理是把Recyclerview的滑动事件都转移给自己来处理,这样Recyclerview的复用机制就会不起作用,数据过多容易OOM.单页面且数据量不多可以采用StickyNestedScrollView更简单。

    注:其它更多复杂的悬停使用场景请参考demo中关于Sticky部分的示例。

    完整示例

    所有的属性设置,都是根据自己的需要进行设置,不需要把所有属性都设置一遍,切记!切记!切记!

    XRecyclerView mRecyclerView = (com.zhouyou.recyclerview.XRecyclerView) this.findViewById(R.id.recyclerview);
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setAutoMeasureEnabled(true);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mRecyclerView.setLayoutManager(layoutManager);
    
    //刷新方式二选一:方式一or方式二 
    //方式一:采用库中提供的默认刷新样式
    mRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader); //设置下拉刷新Progress的样式,在ProgressStyle里面选择喜欢的样式
    mRecyclerView.setArrowImageView(R.drawable.iconfont_downgrey);//设置下拉刷新箭头
    mRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallPulse);  //设置加载更多Progress的样式
    mRecyclerView.setFootViewText("拼命加载中","已经全部");
    
    //方式二:完全采用自定义的刷新动画
    //mRecyclerView.setRefreshHeader(new CustomRefreshHeader(this));
    //mRecyclerView.setLoadingMoreFooter(new CustomLoadingMoreFooter(this));
    
    //打开刷新和加载更多
    //mRecyclerView.setPullRefreshEnabled(true);
    //mRecyclerView.setLoadingMoreEnabled(true);
    
    //添加一个头部view和尾部view
    View headerView =   LayoutInflater.from(this).inflate(R.layout.layout_header, (ViewGroup)findViewById(android.R.id.content),false);
    View footerView = getLayoutInflater().inflate(R.layout.layout_footer, (ViewGroup) mRecyclerView.getParent(), false);
    mRecyclerView.addHeaderView(headerView);
    mRecyclerView.addFooterView(footerView);
    
    //分割线设置
    mRecyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(this).build());
    
    //移除添加的头部view
    mRecyclerView.removeHeaderView(headerView);
    //移除添加的尾部view
    mRecyclerView.removeFooterView(footerView);
    mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
        @Override
        public void onRefresh() {
            //下拉刷新
            //mRecyclerView.refreshComplete();//刷新动画完成
        }
    
        @Override
        public void onLoadMore() {
            //加载更多
            //mRecyclerView.loadMoreComplete();//加载动画完成
            //mRecyclerView.setNoMore(true);//数据加载完成
        }
    });
    
    //装载数据
    MyAdapter mAdapter = new MyAdapter(this);
    mRecyclerView.setAdapter(mAdapter);//本库不成文规定,最好使用setAdapter写在装载数据之前
    mAdapter.setListAll(datas);//该动作放在setAdapter之后
    

    github源码地址:https://github.com/zhou-you/EasyXRecyclerView

    万能适配器Adapter功能介绍

    RecyclerView适配器的传统使用方法是继承RecyclerView.Adapter<RecyclerView.ViewHolder>相信大家都很清楚(不清楚的请先自行学习官方使用方式),这里为什么提供一种万能适配器呢?主要是方便快捷的开发,一种最佳偷懒姿势,打造属于RecyclerView的万能适配器Adapter和ViewHolder、以及更简单方便的数据绑定和操作。

    特性

    • 优化了Adapter代码,减少了代码量
    • 简单配置、无需重写额外方法
    • 添加Item事件
      • Item的点击事件
      • Item的长按事件
    • 添加列表加载动画
      • 一行代码轻松切换5种默认动画
    • 支持添加分组
      • 随心定义分组头部、尾部
      • 支持点击展开收起
      • 支持配置是否带组头或组尾(组头、组尾是否显示)
      • 子项为Grid(并支持Span不同)
      • 头、尾、子项支持多种类型
      • 支持分组带有Sticky黏性效果
      • 分组增加状态页面占位显示(加载中、错误页面、空页面、内容页面)
    • 自定义不同的item类型
    • 支持添加item拖拽
    • 支持自定义ViewHolder,让开发者随心所欲

    主要相关类介绍

    类名 功能 备注
    BaseRecyclerViewAdapter 万能适配器基类 继承该类该就可体验极简适配器用法、也可继承该类扩展功能。
    HelperRecyclerViewAdapter 提供便捷数据操作的万能适配器,继承BaseRecyclerViewAdapter 此类在万能适配的基础上增加了数据操作,可以直接继承该类实现适配器 (推荐用法)。
    HelperRecyclerViewAnimAdapter 提供带有动画效果的万能适配器,继承HelperRecyclerViewAdapter 如果不需要动画效果,请直接使用HelperRecyclerViewAdapter。
    HelpRecyclerViewDragAdapter 提供带有拖拽效果的万能适配器,继承HelperRecyclerViewAdapter 如果不需要拖拽效果,请直接使用HelperRecyclerViewAdapter。
    HelperStateRecyclerViewAdapter 提供带有状态页面的万能适配器,继承HelperRecyclerViewAdapter 继承该类该就可配置页面加载中、错误、空页面、内容页面之间自由切换。
    GroupedRecyclerViewAdapter 分组适配器 继承该类该可实现各种分组效果 。
    GroupedStateRecyclerViewAdapter 具有分组和支持状态页面切换的适配器,继承GroupedRecyclerViewAdapter 如果不需要状态页面,请直接使用GroupedRecyclerViewAdapter。
    BaseRecyclerViewHolder 万能适配Holder 可继承该类扩展功能。
    HelperRecyclerViewHolder 具有view快捷操作的Holder,继承BaseRecyclerViewHolder 此类在BaseRecyclerViewHolder基础上增加了view绑定的便捷操作(推荐用法)。

    注:
    1.主要推荐用法是 HelperRecyclerViewAdapter+HelperRecyclerViewHolder完美打造万能适配器,其它可根据具体需求自由选择适配器。
    2.类名HelpXXX开头和GroupedXXX相关的类用方法差别较大,请看提供的具体demo用法
    3.使用适配器时在Activity和Fragment等中不需要再创建一个集合用于存放数据,适配器里面已经默认内置装载数据的集合List<T>,否则会有两个集合占用内存,从网络返回的数据是集合可以直接丢进适配器里面。

    HelperRecyclerViewAdapter支持的数据操作接口介绍

    HelperRecyclerViewAdapter是继承于BaseRecyclerViewAdapter同时实现了DataHelper数据操作接口,在使用中会更加简单快捷,具体如下:

    方法 描述
    void add(int position, T data); 添加单个数据data到指定位置position
    addAll(int startPosition, List datas) 添加数据集合datas到指定位置position
    addItemToHead(T data) 添加单个数据data到列表头部
    addItemsToHead(List datas) 添加数据集datas到列表头部
    addItemToLast(T data) 添加单个数据data到列表尾部
    addItemsToLast(List datas) 添加数据集合datas到列表尾部
    alterObj(T oldData, T newData) 将集合中的数据oldDagta修改成新数据newData
    alterObj(int index, T data) 将index位置的数据修改成data
    ** clear()** 情空所有数据
    ** contains(T data)** 判断集合中是否包含数据data
    ** getData(int index)** 获取index位置的数据,return T
    isEnabled(int position) 判断position位置是否有效
    remove(T data) 移除集合中的数据data
    removeToIndex(int index) 移除结合中index位置的数据
    removeToIndex(int index) 移除结合中index位置的数据
    replaceAll(List datas) 替换适配器集合中所有的数据
    setListAll(List datas) 覆盖所有数据

    注:
    1.需要先调用setListAll(List<T> datas)或者从适配器构造方法里装载数据后,再允许操作其它方法。
    2.使用以上提供的方法,里面都有做空指针和索引越界判断,外部不管集合或者数据data是否为null都可传递.不会导致应用崩溃。
    3.使用以上所有方法有不用再调用notifyDataSetChanged()方法刷新适配器了,方法里面会自动帮你触发刷新通知。

    万能适配器使用介绍

    /**
     * <p>描述:自定义适配器(请大家重点参考此类的使用方式和讲解)</p>
     * 
     * 一下代码会提供四种设置数据的构造<br/>
     * 本代码模拟采用方式四来举例,其实也是方式二的一个变种,大部分适配器都是对应一个item布局。<br/>
     * 传统的写法请参考:OldMyAdapter.java<br/>
     * 作者: zhouyou<br>
     * 日期: 2016/10/27 16:24<br>
     * 版本: v2.0<br>
     */
    public class MyAdapter extends HelperRecyclerViewAdapter<TestBean> {
        //以下提供适配器的几种构造使用方式,请选择自己喜欢的【一种】运用方式就可以了
        /*
        //方式一 data layoutId都从外部传入,例如:new MyAdapter(mList,this,R.layout.item)
        //注意layoutIds是表示可变参数,支持传入多个布局,用于支持多item布局,例如:new MyAdapter(mList,this,R.layout.item,R.layout.item2)
        public MyAdapter(List<TestBean> data, Context context, int... layoutIds) {
            super(data, context, layoutIds);
        }
        //方式二 layoutIds从外部传入,例如:MyAdapter  adapter = new MyAdapter(this,R.layout.item),数据传递用adapter.setListAll(mList);
        //注意layoutIds是表示可变参数,支持传入多个布局,用于支持多item布局,例如:new MyAdapter(this,R.layout.item,R.layout.item2)
        public MyAdapter(Context context, int... layoutIds) {
            super(context, layoutIds);
        }
        
        //方式三 数据从外部传入,布局直接写在里面(推荐使用方式),好处把适配器相关的item布局都放在本来中一块管理
        public MyAdapter(List<TestBean> mList, Context context) {
            super(mList, context,R.layout.item);//单item布局
            //super(mList, context,R.layout.item,R.layout.item2);//多item布局
        }*/
    
        //方式四 布局直接通过在构造方法中设置(推荐使用方式),数据集合通过setListAll设置
        public MyAdapter(Context context) {
            super(context, R.layout.item);
        }
    
       //不需要自己再自定义viewHolder类了 库里定义有viewHolder基类HelperRecyclerViewHolder
        @Override
        protected void HelperBindData(HelperRecyclerViewHolder viewHolder, int position, TestBean item) {
            /****1.数据获取方式*****/
            //旧:传统的写法是从集合中获取再强转,如下:
            //TestBean testBean =(TestBean)datas.get(position);
            //新:baseadapter中提供的有获取当前postion位置对应的数据,直接调用就行了,也不用强转
            final TestBean testBean = getData(position);
    
            /****2.view赋值*****/
            //方式一:采用链式的设计的书写方式,一点到尾。(方式一)
            viewHolder.setText(R.id.text,testBean.getName())
                    .setImageResource(R.id.image, MakePicUtil.makePic(position))
                   /* .setVisible(R.id.text,true);//设置某个view是否可见*/
            .setOnClickListener(R.id.image, new View.OnClickListener() {//点击事件
                @Override
                public void onClick(View view) {
                    Toast.makeText(mContext, "我是子控件" + testBean.getName() + "请看我如何处理View点击事件的", Toast.LENGTH_LONG).show();
                }
            });
            //其它更多连写功能请查看viewHolder类中代码
            
            //方式二:不采用链式的方式,通过getView直接获取控件对象,不需要强转了,采用的是泛型
            TextView textView =viewHolder.getView(R.id.text2);
            textView.setText(testBean.getAge());
    
            /****3.其它更多使用方式,请自己探索*****/
            //举例如果想知道适配器中数据是否为空用isEmpty()就可以了,无需list.size()==0  list.isEmpty()等其它方式
            if(isEmpty()){
                
            }
        }
    
        /*******************以下两种item点击事件都可以,自己选择合适的方式**********************************/
        //方式一:此方式是另一种处理:绑定相关事件,例如点击长按等,默认空实现,如果你要使用需要覆写setListener()方法
        //方式二:绑定相关事件,例如点击长按等,默认空实现等我们一般会在适配器外部使用,
        // 例如: mAdapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener<TestBean>(){});
        
       /* @Override
        protected void setListener(HelperRecyclerViewHolder viewHolder, final int position, TestBean item) {
            viewHolder.getItemView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(mContext,"我是Item:"+position,Toast.LENGTH_SHORT).show();
                }
            });
        }*/
       
    }
    

    Activity/Fragment中引用:

    //用框架里面的adapter时不需要再建立全局集合存放数据了,数据都和adapter绑定了,里面自带泛型集合
    //如果你外面还建立一个集合,那相当于占用内存两份了。。
    List<TestBean> listData = new ArrayList<TestBean>();
    
    //方式四对应的初始化适配器   也可采用下面的构造方式创建对象  (自己选择)
    //这种方式一定要先setAdapter然后才setListAll()设置数据
    mAdapter = new MyAdapter(this);
    mRecyclerView.setAdapter(mAdapter);
    
    /****讲解*****/
    //1.使用setListAll(覆盖数据)后就不需要再调用notifyDataSetChanged()
    //2.如果是addAll()追加
    //3.自己会刷新
    //4.如果集合List<TestBean>是从网络请求中返回的,那么此处就不需要再多创建new ArrayList<TestBean>()操作,直接在网络请求你成功的地方调用mAdapter.setListAll(list);
    mAdapter.setListAll(listData);
    
    //方式一对应的初始化适配器
    //mAdapter = new MyAdapter(listData, this, R.layout.item);
    //方式二对应的初始化适配器
    //mAdapter = new MyAdapter(this, R.layout.item);
    

    多Item布局使用介绍

    /**
     * <p>描述:多item适配器</p>
     *
     * 作者: zhouyou<br>
     * 日期: 2016/10/31 16:24<br>
     * 版本: v2.0<br>
     */
    public class MultiItemAdapter extends com.zhouyou.recyclerview.adapter.HelperRecyclerViewAdapter<MultipleItemBean> {
    
        public MultiItemAdapter(Context context) {
            //设置多item布局adapter_multi_item1_layout,adapter_multi_item2_layout,adapter_multi_item3_layout
            super(context, R.layout.adapter_multi_item1_layout,R.layout.adapter_multi_item2_layout,R.layout.adapter_multi_item3_layout);
        }
    
        @Override
        protected void HelperBindData(HelperRecyclerViewHolder viewHolder, int position, MultipleItemBean item) {
            //方式一,对应checkLayout中的方式一
            if(item.getItemType() == 0){//adapter_multi_item1_layout布局对应的操作
                viewHolder.setText(R.id.name_tv,item.getName());
            } else if(item.getItemType() == 1){//adapter_multi_item2_layout布局对应的操作
                viewHolder.setText(R.id.name_tv,item.getName())
                        .setText(R.id.info_tv,item.getAge());
            }
    
            /*//方式二,对应checkLayout中的方式二
            int layoutType = getItemViewType(position);
            if(layoutType==R.layout.adapter_multi_item1_layout){
                viewHolder.setText(R.id.name_tv,item.getName());
            }else if(layoutType==R.layout.adapter_multi_item2_layout){
                viewHolder.setText(R.id.name_tv,item.getName())
                        .setText(R.id.info_tv,item.getAge());
            }*/
        }
    
        /*********多item布局使用方式***********/
        //如果要用多item布局,必须重写checkLayout()方法,来指定哪一条数据对应哪个item布局文件
        //不重写的时候返回默认是0,也就是只会加载第一个布局
        @Override
        public int checkLayout(MultipleItemBean item, int position) {
            //方式一:判断的类型直接写在model中
            return item.getItemType();
            //方式二:根据类型判断
            /*if(item instanceof A){
                return R.layout.adapter_multi_item1_layout;
            }else if(item instanceof B){
                return R.layout.adapter_multi_item2_layout;
            }else {
                return R.layout.adapter_multi_item3_layout;
            }*/
        }
    }
    
    

    添加Item事件

    //1.设置item事件监听
    //2.在适配器MyAdapter类中也可以设置点击事件,采用setListener()接口
    mAdapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener<TestBean>() {
        @Override
        public void onItemClick(View view, TestBean item, int position) {
            Toast.makeText(getApplicationContext(),"我是item "+position,Toast.LENGTH_LONG).show();
        }
    });
    
    //长按事件
    mAdapter.setOnItemLongClickListener(new BaseRecyclerViewAdapter.OnItemLongClickListener() {
        @Override
        public void onItemLongClick(View view, Object item, int position) {
            Toast.makeText(getApplicationContext(),"我是item长按 "+position,Toast.LENGTH_LONG).show();
        }
    });
    

    Item拖拽使用介绍

    适配器需要继承HelperRecyclerViewDragAdapter,同样具备所有万能适配器的功能。

    /**
     * <p>描述:item可以移动拖拽的适配器</p>
     *
     *
     * 作者: zhouyou<br>
     * 日期: 2016/10/31 16:24<br>
     * 版本: v2.0<br>
     */
    public class DragAdapter extends HelperRecyclerViewDragAdapter<String> {
        public DragAdapter(Context context) {
            super(context, R.layout.adapter_draggable_layout);
        }
    
        @Override
        protected void HelperBindData(HelperRecyclerViewHolder viewHolder, int position, String item) {
            viewHolder.setText(R.id.tv,item).setImageResource(R.id.image, MakePicUtil.makePic(position));
        }
    }
    

    Activity/Fragment中引用:

    //拖动监听事件
    OnItemDragListener listener = new OnItemDragListener() {
        @Override
        public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos) {
            //开始拖动
        }
    
        @Override
        public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {
            //拖动中
        }
    
        @Override
        public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {
            //拖动结束
        }
    };
    
    DragAdapter mAdapter = new DragAdapter(this);
    //适配器绑定item拖动监听
    ItemDragListener mItemDragAndSwipeCallback = new ItemDragListener(mAdapter);
    //触摸帮助类
    ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(mItemDragAndSwipeCallback);
    //绑定RecyclerView
    mItemTouchHelper.attachToRecyclerView(mRecyclerView);
    //设置允许拖动的方向
    mItemDragAndSwipeCallback.setDragMoveFlags(ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN);
    //设置监听
    mAdapter.enableDragItem(mItemTouchHelper);
    mAdapter.setOnItemDragListener(listener);
    
    //item点击事件
    mAdapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener<String>() {
        @Override
        public void onItemClick(View view, String item, int position) {
            Toast.makeText(DragActivity.this,item,Toast.LENGTH_SHORT).show();
        }
    });
    //设置适配器
    mRecyclerView.setAdapter(mAdapter);
    mAdapter.setListAll(mData);
    

    Item动画使用介绍

    适配器需要继承HelperRecyclerViewAnimAdapter,同样具备所有万能适配器的功能。

    /**
     * <p>描述:具有动画效果的适配器</p>
     *
     * 作者: zhouyou<br>
     * 日期: 2016/10/31 16:24<br>
     * 版本: v2.0<br>
     */
    public class AnimationAdapter extends HelperRecyclerViewAnimAdapter<String> {
    
        public AnimationAdapter(Context context) {
            super(context, R.layout.adapter_animation_layout);
        }
        @Override
        protected void HelperBindData(HelperRecyclerViewHolder viewHolder, int position, Object item) {
            
        }
    }
    

    Activity/Fragment中引用:

    AnimationAdapter mAdapter = new AnimationAdapter(this);
    mAdapter.setShowItemAnimationEveryTime(true);//是否每次都会执行动画,默认是false,该方便测试
    mAdapter.setItemAnimation(AnimationType.ALPHA);//设置item动画类型ALPHA、SCALE、SLIDE_FROM_LEFT、SLIDE_FROM_RIGHT、SLIDE_FROM_BOTTOM
    //mAdapter.setCustomItemAnimator(new CustomAnimator());//自定义实现动画
    //mAdapter.setItemAnimationDuration(2000);//设置动画执行时间
    //mAdapter.setItemAnimationInterpolator(new AccelerateInterpolator());//设置动画插值器
    
    superRecyclerView.setAdapter(mAdapter);
    mAdapter.setListAll(dataList);//设置数据
    

    状态页面适配器介绍

    状态页面适配器主要是解决列表页面具有加载中页面、空页面、错误页面、内容页面之间无缝切换的问题。通常的做法是用一个普通的占位页面和Recyclerview布局平级,通过显示和隐藏等来改变占位页面达到效果。这里尝试另一种方式来解决此问题,在适配器中实现具体的getEmptyView,getErrorView,getLoadingView,虽然多了几个方法,但是代码更整洁清晰了,也利于你想定制任意的页面状态,根据调用的方法动态监控类表的数据装载情况才显示出占位图,有列表内容显示的时候占位图不会被加载不会占用内存。

    /**
     * <p>描述:状态页面适配器</p>
     * 1.实现具体的getEmptyView,getErrorView,getLoadingView,虽然多了几个方法,但是代码更整洁清晰了,也利于你想定制任意的页面状态<br>
     * 2.如果你的每个页面状态都是一样的,不想每个页面都去实现getEmptyView,getErrorView,getLoadingView,可以自己再封装一个baseAdapter,处理公共的状态页面<br>
     * 作者: zhouyou<br>
     * 日期: 2018/1/19 17:51 <br>
     * 版本: v1.0<br>
     */
    public class EmptyStateAdapter extends HelperStateRecyclerViewAdapter<String> {
        public EmptyStateAdapter(List data, Context context, int... layoutId) {
            super(data, context, layoutId);
        }
    
        public EmptyStateAdapter(Context context) {
            super(context, R.layout.header_footer_item);
        }
    
        //不需要自己再自定义viewHolder类了 库里定义有viewHolder基类HelperRecyclerViewHolder
        @Override
        protected void HelperBindData(HelperRecyclerViewHolder viewHolder, int position, String item) {
            final String value = getData(position);
            viewHolder.setText(R.id.test_btn, value);
        }
    
        @Override
        public View getEmptyView(ViewGroup parent) {
            //空页面内容布局
            return mLInflater.inflate(R.layout.view_state_empty, parent, false);
        }
    
        @Override
        public View getErrorView(ViewGroup parent) {
            //错误页面布局
            return mLInflater.inflate(R.layout.view_state_error, parent, false);
        }
    
        @Override
        public View getLoadingView(ViewGroup parent) {
            //加载中页面布局
            return mLInflater.inflate(R.layout.view_state_loading, parent, false);
        }
    
        //onBindEmptyViewHolder、onBindErrorViewHolder、onBindLoadingViewHolder根据需要进行选择是否实现。
        @Override
        public void onBindEmptyViewHolder(HelperRecyclerViewHolder holder) {
            //修改空页面内容
            holder.setText(R.id.tv_empty_view, "我更新了空页面内容");
        }
    
        
       /* @Override
        public void onBindErrorViewHolder(HelperRecyclerViewHolder holder) {
            super.onBindErrorViewHolder(holder);
        }*/
    
       /* @Override
        public void onBindLoadingViewHolder(HelperRecyclerViewHolder holder) {
            super.onBindLoadingViewHolder(holder);
        }*/
    }
    

    在需要的地方,通过一些方法来控制页面的显示。

    mAdapter = new EmptyStateAdapter(this);
    mRecyclerView.setAdapter(mAdapter);
    
    //以下状态在需要的地方调用
    mAdapter.setState(HelperStateRecyclerViewAdapter.STATE_LOADING);//模拟加载中
    //mAdapter.setState(HelperStateRecyclerViewAdapter.STATE_ERROR);
    //mAdapter.setState(HelperStateRecyclerViewAdapter.STATE_EMPTY);
    //如果页面列表集合有内容调用mAdapter.setListAll(list);时会自动隐藏占位图
    mAdapter.setListAll(list);
    //如果设置了 mAdapter.setState()调用:mAdapter.clear();/mAdapter.remove()等都会自动切换到占位图
    
    
    //如果用网格Manager,必须用StateGridLayoutManager,否则Grid状态页面会不起作用
    StateGridLayoutManager layoutManager = new StateGridLayoutManager(this,3);
    //切记!切记!切记!
    //setLayoutManager方法一定要放在setAdapter之后,否则LayoutManager是GridLayoutManager的时候尾部脚显示错误
    mRecyclerView.setLayoutManager(layoutManager);
    

    注:
    1.如果用网格Manager,必须用StateGridLayoutManager,否则Grid状态页面会不起作用
    2.setLayoutManager方法一定要放在setAdapter之后,否则LayoutManager是GridLayoutManager的时候尾部显示错误,切记!切记!切记!

    分组显示

    主要采用GroupedRecyclerViewAdapter可以很方便的实现RecyclerView的分组显示,并且每个组都可以包含组头、组尾和子项;可以方便实现多种Type类型的列表,可以实现如QQ联系人的列表一样的列表展开收起功能,还可以实现头部悬浮吸顶功能等。
    示例:

    public class GroupedListAdapter extends GroupedRecyclerViewAdapter<GroupBean> {
        public GroupedListAdapter(Context context) {
            super(context);
        }
    
        public GroupedListAdapter(Context context, List<GroupBean> list) {
            super(context, list);
        }
    
        //返回组的数量
        @Override
        public int getGroupCount() {
            return getGroups().size();
        }
    
        //返回当前组的子项数量
        @Override
        public int getChildrenCount(int groupPosition) {
            return getGroup(groupPosition).getChildren().size();
        }
        //当前这个组是否有头部
        @Override
        public boolean hasHeader(int groupPosition) {
            return true;
        }
    
        //当前这个组是否有尾部
        @Override
        public boolean hasFooter(int groupPosition) {
            return true;
        }
    
        //返回头部的布局id。(如果hasHeader返回false,这个方法不会执行)
        @Override
        public int getHeaderLayout(int viewType) {
            return R.layout.adapter_header;
        }
    
        //返回尾部的布局id。(如果hasFooter返回false,这个方法不会执行)
        @Override
        public int getFooterLayout(int viewType) {
            return R.layout.adapter_footer;
        }
    
        //返回子项的布局id。
        @Override
        public int getChildLayout(int viewType) {
            return R.layout.adapter_child;
        }
    
        //绑定头部布局数据。(如果hasHeader返回false,这个方法不会执行)
        @Override
        public void onBindHeaderViewHolder(HelperRecyclerViewHolder holder, int groupPosition, GroupBean item) {
            //也可以通过Group获取
            //GroupEntity entity =getGroup(groupPosition);
            holder.setText(R.id.tv_header, item.getHeader());
        }
    
        //绑定尾部布局数据。(如果hasFooter返回false,这个方法不会执行)
        @Override
        public void onBindFooterViewHolder(HelperRecyclerViewHolder holder, int groupPosition, GroupBean item) {
            holder.setText(R.id.tv_footer, item.getFooter());
        }
    
        //绑定子项布局数据。
        @Override
        public void onBindChildViewHolder(HelperRecyclerViewHolder holder, int groupPosition, int childPosition, GroupBean item) {
            holder.setText(R.id.tv_child, item.getChildren().get(childPosition).getChild());
        }
    }
    

    还可是重写GroupedRecyclerViewAdapter方法实现头、尾和子项的多种类型item。

    //返回头部的viewType。
    public int getHeaderViewType(int groupPosition);
    
    //返回尾部的viewType。
    public int getFooterViewType(int groupPosition) ;
    
    //返回子项的viewType。
    public int getChildViewType(int groupPosition, int childPosition) ;
    

    设置点击事件的监听

    GroupedListAdapter adapter = new GroupedListAdapter(this, GroupModel.getGroups(10, 5));
    adapter.setOnHeaderClickListener(new com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter.OnHeaderClickListener<GroupBean>() {
        @Override
        public void onHeaderClick(com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter adapter, HelperRecyclerViewHolder holder, int groupPosition, GroupBean item) {
        }
    });
    adapter.setOnFooterClickListener(new com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter.OnFooterClickListener<GroupBean>() {
        @Override
        public void onFooterClick(com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter adapter, HelperRecyclerViewHolder holder,
                                  int groupPosition, GroupBean item) {
        }
    });
    adapter.setOnChildClickListener(new com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter.OnChildClickListener<GroupBean>() {
        @Override
        public void onChildClick(com.zhouyou.recyclerview.group.GroupedRecyclerViewAdapter adapter, HelperRecyclerViewHolder holder,
                                 int groupPosition, int childPosition, GroupBean item) {
        }
    });
    rvList.setAdapter(adapter);
    

    注:
    1.setLayoutManager()必须放在setAdapter之前,切记!切记!切记!
    2.更多功能使用讲解请参考本库demo中使用示例或者参考donkingliang的github,此模块功能是集成作者的,在此非常感谢,同时也处理了对XRecyclerview的兼容、能够做到分组也支持刷新和加载更多,同时新增支持:追加状态页面的分组效果(请看demo)。

    项目结构目录截图

    一个漂亮而强大的RecyclerView

    代码地址如下:
    http://www.demodashi.com/demo/13470.html

    注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    27 Spring Cloud Feign整合Hystrix实现容错处理
    26 Spring Cloud使用Hystrix实现容错处理
    25 Spring Cloud Hystrix缓存与合并请求
    24 Spring Cloud Hystrix资源隔离策略(线程、信号量)
    23 Spring Cloud Hystrix(熔断器)介绍及使用
    22 Spring Cloud Feign的自定义配置及使用
    21 Spring Cloud使用Feign调用服务接口
    20 Spring Cloud Ribbon配置详解
    19 Spring Cloud Ribbon自定义负载均衡策略
    18 Spring Cloud Ribbon负载均衡策略介绍
  • 原文地址:https://www.cnblogs.com/demodashi/p/9443298.html
Copyright © 2011-2022 走看看