美文网首页RecyclerView下拉刷新,上拉加载listView,RecyclerView
安卓RecyclerView高阶使用详解(多套布局,加载更多,监

安卓RecyclerView高阶使用详解(多套布局,加载更多,监

作者: 番茄And鸡蛋 | 来源:发表于2016-11-18 08:07 被阅读2887次

    先来一张简单的效果图,实现了多套布局,以及滑到底部时的加载更多,内含监听自定义设置。


    效果图.gif

    实现原理##

    1.多套布局

    利用RecyclerView的特性==》必须实现的ViewHolder类,在定义RecyclerView.Adapter时,在内部实现多个ViewHolder的类,根据不同需求的布局,获取到View后,分别在不同的ViewHolder中进行管理,处理数据时自动判断加载哪个布局。

    2.加载更多

    给RecyclerView设置滑动监听OnScrollListener,并重写里面的onScrollStateChanged和onScrolled方法,在RecyclerView的Item监听滑动到底部时,加载动画,并为Item添加数据,刷新适配器显示。

    3.点击事件

    在Adapter内部定义回调接口,onBindViewHolder中为需要设置监听的某个 view或者整条Item返回数据给调用者。

    一,定义好RecyclerView

    在Xml中文件使用

    <android.support.v7.widget.RecyclerView
        android:id="@+id/myRecycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
    

    如果发现报错,找不到该View的话,需要在依赖中引入RecyclerView,以及待会用到的进度条。

    compile 'com.android.support:support-v4:25.0.1'
    compile 'com.android.support:design:25.0.1'
    compile 'com.android.support:recyclerview-v7:25.0.1'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.pnikosis:materialish-progress:1.7'
    

    在Activity中找到该RecyclerView后,需要设置如下属性

    public class MainActivity extends AppCompatActivity {
        private RecyclerView myRecyclerView;
        private LinearLayoutManager mLayoutManager;
        private MyRecyclerAdapter adapter;
        private boolean isLoading;
        private Handler handler = new Handler();
        private List<Info> mEntity = new ArrayList<>();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
                    myRecyclerView = (RecyclerView) findViewById(R.id.myRecycler);
            mLayoutManager = new LinearLayoutManager(this);
            myRecyclerView.setLayoutManager(mLayoutManager);
            setMyAdapter();
            loading();
        }
     
    

    这里我为RecyclerView 设置了一个setLayoutManager的参数传入了LinearLayoutManager 的一个对象,这个参数是设置RecyclerView应该以什么样的形态出现,有三种形式:

    • LinearLayoutManager==》普通列表型Item布局
    • GridLayoutManager==》Grid图片浏览型Item布局
    • StaggredGridLayoutManager==》瀑布流型Item布局

    这里主要演示LinearLayoutManager,在XML中写好自己需要的布局。代码就不一一展示了,如图效果中一样,共三种布局。这里就有一个新型的进度条显示的Xml介绍给大家,我用于最底部的加载更多显示。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:wheel="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center"
        android:orientation="horizontal">
        <com.pnikosis.materialishprogress.ProgressWheel
            android:layout_marginRight="10dp" 
           android:id="@+id/rcv_load_more"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_gravity="center" 
           wheel:matProg_barColor="#039ae4"
            wheel:matProg_progressIndeterminate="true" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="正在加载更多..."
            android:textColor="@android:color/darker_gray" />
    </LinearLayout>
    

    二,创建Adapter

    因为分开写怕大家看不懂,所以直接贴上源码,详细介绍在里面的备注中可以看到。

    public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {
        public final static int TYPE_FOOTER = 2;//加载更多的布局,进度条显示
        public final static int TYPE_NORMAL = 1;//Item的第一种布局
        public final static int TYPE_NORTWO = 3;//Item的第二种布局
        private Context mContext;
        private List<Info> mEntity;
        private MyRecyclerAdapter.OnRecyclerViewItemClickListener mOnItemClickListener = null;
        public interface OnRecyclerViewItemClickListener {
            void onItemClick(View view, int position);
        }
        public MyRecyclerAdapter(Context mContext) {
            this.mContext = mContext;
        } 
        //构造函数,用于接收Context和传进来的数据
          public MyRecyclerAdapter(Context mContext, List<Info> mEntity_WenTiList) {
            this.mContext = mContext;
            this.mEntity = mEntity_WenTiList;
        }
      //这里的监听是为了接收外面传递点击事件的数据
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                //注意这里使用getTag方法获取数据
                mOnItemClickListener.onItemClick(v, (int) v.getTag());
            }
        }
    //自定义的Item响应事件需要接收OnRecyclerViewItemClickListener接口的函数
        public void setOnItemClickListener(MyRecyclerAdapter.OnRecyclerViewItemClickListener listener) {
            this.mOnItemClickListener = listener;
        }
    //重写getItemViewType方法,接收不同的position信息,根据自己数据,判断来返回给onCreateViewHolder,选择加载的View布局
        @Override
        public int getItemViewType(int position) {
            if (mEntity.get(position) == null) {
    //这个Null是在第一波数据完结时,添加进来的,作为判断加载更多的条件
                return TYPE_FOOTER;
            } else if (position == 3 || position == 5 || position == 9 || position == 13 || position == 14 || position == 19) {
                return TYPE_NORTWO;
            } else {
                return TYPE_NORMAL;
            }
        }
    //根据不同的需求返回View布局
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            RecyclerView.ViewHolder vh;
            View view;
            switch (viewType) {
                default:
                case TYPE_NORMAL:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_wenti_recycler, parent, false);
                    vh = new MyRecyclerAdapter.WenTiHolder(view);
                    return vh;
                case TYPE_NORTWO:
                    view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image_recycler, parent, false);
                    vh = new MyRecyclerAdapter.TwoHolder(view);
                    return vh;
                case TYPE_FOOTER:
                    view = LayoutInflater.from(parent.getContext()).inflate(                        R.layout.recyclerview_footer, parent, false);
                    vh = new FooterViewHolder(view);
                    return vh;
            }
        }
    //这里需要注意的是因为接收的ViewHolder 有三种不同的类型,所以应该分开来处理。
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { 
           if (holder instanceof FooterViewHolder) {
                ((FooterViewHolder) holder).rcvLoadMore.spin();
                return; 
           }
            if (holder instanceof WenTiHolder) {
                WenTiHolder newHolder = (WenTiHolder) holder;
    //给整条Item添加点击事件,因为是自定义的,可以写多种点击事件,或者点击限制等。
                newHolder.itemView.setTag(position);
                newHolder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
    //接收到点击事件后,返回给外部数据回调
                        mOnItemClickListener.onItemClick(v, position);
                    }
                });
            } 
           if (holder instanceof TwoHolder) {
                TwoHolder newHolder = (TwoHolder) holder;
                newHolder.itemView.setTag(position);
                newHolder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        mOnItemClickListener.onItemClick(v, position);
                    }
                });
            }
        }
    //获取数据长度Item的个数
        @Override
        public int getItemCount() {
            if (mEntity != null) {
                return mEntity.size();
            } else { 
               return 0;
            } 
       }
        /**     * 第一种布局     */
        class WenTiHolder extends RecyclerView.ViewHolder {
            public WenTiHolder(View itemView) {
                super(itemView);
            }
        } 
       /**     * 第二种布局     */ 
       class TwoHolder extends RecyclerView.ViewHolder {
            public TwoHolder(View itemView) {
                super(itemView);
            }
        } 
       /**     * 底部加载更多     */ 
       class FooterViewHolder extends RecyclerView.ViewHolder {
            private ProgressWheel rcvLoadMore;
            public FooterViewHolder(View itemView) { 
               super(itemView);
                rcvLoadMore = (ProgressWheel) itemView.findViewById(R.id.rcv_load_more);
            }
        }
    }
    

    三,绑定适配器设置加载更多

    因为没写网络请求,就随意添加一堆数据,需要注意的是在结尾时,我添加了一个Null,在前面适配器中判断是否是加载更多布局的时候是判断Null值,所以我直接手动添加一个,不得省略。

    public List<Info> iniData() {
        Info info = new Info();
        for (int i = 0; i < 10; i++) {
            info.setBq(i + 1 + "");
            info.setContext("aa");
            info.setTime("14:11");
            info.setTitle("bjnsdkjas");
            mEntity.add(info);
        }    mEntity.add(null);
        return mEntity;
    }
    
    

    在Activity中,添加设配器设置,这里回调了我们自定义的监听设置,在接口中只返回了点击的Item,但也足够我们拿到所有的List数据了,可以在Click中进行自己需要的操作。

    //添加适配器设置
    private void setMyAdapter(){
        adapter = new MyRecyclerAdapter(this, iniData());
        adapter.setOnItemClickListener(new MyRecyclerAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
            }
        });
        myRecyclerView.setAdapter(adapter);
    }
    

    下面是加载更多的代码

    //加载更多,每次10条
    private void loading() {
        myRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
                if (lastVisibleItemPosition + 1 == adapter.getItemCount()) {
                    if (!isLoading) {
                        isLoading = true;
                        handler.postDelayed(new Runnable() { 
                           @Override
                            public void run() {
                                mEntity.remove(mEntity.size() - 1);
                                iniData();
                                adapter.notifyDataSetChanged();
                                isLoading = false;
                            }
                        }, 3000);
                    } 
               }
            }
        });
    }
    

    直接给RecyclerView添加滑动的状态监听,里面实现了滑动状态,以及滑动到底部时的状态,判断适配器中数据的长度,再进行对当前最底部的Item比较,如果是最后一条,则会在3秒内收到消息,可以在子线程中重新加载数据,我是直接为Info类Add了10条数据,注意添加前,要把以前添加的Null值删除。然后刷新适配器。完成加载更多,重置布尔值,下次滑动到底部重复以上操作。

    结语

    因为RecyclerView中自带了一种下拉刷新的控件,比较简单,我就不介绍在项目中。

    花了一整下午的时间写的Damo,手打简书。觉得有用,喜欢的请点个赞!想看更多安卓知识请关注我。因为才开始写文章不久,如有不理解和不足之处,欢迎评论留言,谢谢!

    相关文章

      网友评论

      • 浮游大虾:你是才发现这个功能吧!推荐你上GitHub看下assemblyadapter ,

      本文标题:安卓RecyclerView高阶使用详解(多套布局,加载更多,监

      本文链接:https://www.haomeiwen.com/subject/ehwdpttx.html