美文网首页自定义控件
四、自定义RecycleView,看完这篇就够了

四、自定义RecycleView,看完这篇就够了

作者: 27efec53a72d | 来源:发表于2019-04-18 22:40 被阅读57次

    本文参考以下文章,总结梳理、实操后整理为这篇学习笔记:
    https://blog.csdn.net/harvic880925/article/details/82656394
    https://blog.csdn.net/harvic880925/article/details/82959754
    https://blog.csdn.net/harvic880925/article/details/84789602
    https://blog.csdn.net/harvic880925/article/details/84866486
    https://blog.csdn.net/harvic880925/article/details/84979161
    https://blog.csdn.net/harvic880925/article/details/86606873

    一、RecyclerView的基本使用

    1、用RecyclerView实现一个普通的ListView列表

    我们将要实现的效果如图:


    屏幕快照 2019-04-18 下午9.58.51.png

    1.1 在XML中引入RecyclerView

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_linear"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
    

    1.2 实现Adapter

    与ListView一样,同样需要一个Adapter来将数据和Item视图绑定起来,但不同的的是RecyclerView的Adapter需要派生自RecyclerView.Adapter<RecyclerView.ViewHolder>

    public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private Context mContext;
        private ArrayList<String> mDatas;
    
        public RecyclerAdapter(Context context, ArrayList<String> datas) {
            mContext = context;
            mDatas = datas;
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            return new NormalHolder(inflater.inflate(R.layout.layout_recyclerview_item, parent, false));
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            NormalHolder normalHolder = (NormalHolder) holder;
            normalHolder.mTV.setText(mDatas.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mDatas == null ? 0 : mDatas.size();
        }
    
        public class NormalHolder extends RecyclerView.ViewHolder {
            public TextView mTV;
    
            public NormalHolder(View itemView) {
                super(itemView);
    
                mTV = itemView.findViewById(R.id.tv_item);
                mTV.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    
    }
    

    下面三个函数是必须重写的:

    • onCreateViewHolder:用于得到我们自定义的ViewHolder,在listView中,我们也会定义ViewHolder来承载视图中的元素.
    • onBindViewHolder:是用于将指定位置的数据和视图绑定起来的
    • getItemCount:用于获取列表总共的item数

    我们创建的 HolderView主要是为了保存每一个Item的视图的控件元素,所以我们要创建一个Item的xml(layout_recyclerview_item.xml):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/tv_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp" />
    
    </LinearLayout>
    

    1.3 填充RecyclerView

    public class LinearAct extends AppCompatActivity {
    
        private RecyclerView recyclerView;
        private ArrayList<String> mDatas = new ArrayList<>();
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recyclerview_linear);
            generateDatas();
            initViews();
        }
    
        private void initViews() {
            recyclerView = findViewById(R.id.recycler_linear);
    
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(linearLayoutManager);
    
            RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
            recyclerView.setAdapter(adapter);
        }
    
        private void generateDatas() {
            for (int i = 0; i < 200; i++) {
                mDatas.add("第" + i + "个items");
            }
        }
    
    }
    

    到这里,我们就实现了本部分开头的上下滚动的效果了。

    这里与listVIew唯一的区别是,这里需要设置一个LayoutManager对象,这里设置的是LinearLayoutManager,也就是垂直列表。我们知道Adapter的职责是用数据将每个Item的控件填充起来,而RecycerView与ListView不一样的是,它不仅能实现传统的滑动的列表,还能实现GridView和瀑布流造型,或者其它各式各样的特殊造型。
    这些造型的实现就是通过LayoutManger来实现的,我们通过Adapter将Item填充了以后,那每个Item怎么摆放是由谁来做的呢?摆放Item的操作就是使用LayoutManager来实现出来的。所以每个LayoutManger所实现的摆放Item的方式都是不一样的,比如:LinearLayoutManager就是传统的ListView的功能,上下滚动或者左右滚动,而GridLayoutManager则是网格摆放,而StaggeredGridLayoutMnager则是瀑布流摆放。

    2、用RecyclerView实现一个横向滑动的ListView列表

    只需要把

    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    

    修改成

    linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    

    就可以把上下滑动的列表修改成左右滑动的列表

    3、用RecyclerView实现一个普通的GridView列表

    我们将要实现的效果如图:


    屏幕快照 2019-04-18 下午10.35.50.png

    LayoutManger的职责就是如何摆放Item,所以对于Adapter与RecyclerView是没有影响的,除非我们为了迎合LayoutManger要改Item的布局,比如为了实现瀑布流效果而需要改变每个Item的宽或高等。一般而言,我们更改LayoutManager,不需要对其它对象操作。所以这也是RecyclerView比较好的一个地方,通过RecycerView本身,Adapter,LayoutManger实现了完全解耦。各自实现各自的功能,与其它部分无关。
    GridLayoutManager的主要作用就是将Item进行网格状摆放,进而实现网格布局效果,所以我们要设置GridLayoutManager时,也只需要更改Acitivity中的设置LayoutManager这块代码即可,其它都不需要动,完整代码如下:

    public class GridAct extends AppCompatActivity {
        private ArrayList<String> mDatas = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recyclerview_grid);
            generateDatas();
            initViews();
        }
    
        private void initViews() {
            RecyclerView mRecyclerView = findViewById(R.id.recycler_grid);
    
            //如果是横向滚动,后面的数值表示的是几行,如果是竖向滚动,后面的数值表示的是几列
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 5);
            //gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(gridLayoutManager);
    
            RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
            mRecyclerView.setAdapter(adapter);
        }
    
        private void generateDatas() {
            for (int i = 0; i < 200; i++) {
                mDatas.add("第 " + i + " 个item");
            }
        }
    }
    

    4、用RecyclerView实现一个瀑布流列表

    我们将要实现的效果如图:


    屏幕快照 2019-04-18 下午10.36.21.png

    原理同上,StaggeredGridLayoutMnager主要用来实现瀑布流效果,我们直接把LayoutManager改为StaggeredGridLayoutMnager。
    完整代码如下:

    public class StaggeredAct extends AppCompatActivity {
        private ArrayList<String> mDatas = new ArrayList<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recyclerview_staggered);
            generateDatas();
            initViews();
        }
    
        private void initViews() {
            RecyclerView mRecyclerView = findViewById(R.id.recycler_staggered);
    
            //StaggeredGridLayoutManager staggeredManager = new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL);
            StaggeredGridLayoutManager staggeredManager = new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(staggeredManager);
    
            StaggeredRecyclerAdapter adapter = new StaggeredRecyclerAdapter(this, mDatas);
            mRecyclerView.setAdapter(adapter);
        }
    
        private void generateDatas() {
            for (int i = 0; i < 200; i++) {
                mDatas.add("第 " + i + " 个item");
            }
        }
    }
    

    这里由于每个Item的高度是一定的,所有的Item的高度都一样,导致所实现的瀑布流布局跟网格布局完全相同,所以如果想实现瀑布流布局,那必然需要每个Item的高度是不一样的。所以我们需要修改Adapter,在代码中动态设置每个Item的高度,让每个Item的高度尽量都不一样,这样就可以看到瀑布流效果了。

    public class StaggeredRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private Context mContext;
        private ArrayList<String> mDatas;
        private ArrayList<Integer> mHeights = new ArrayList<>();
    
        public StaggeredRecyclerAdapter(Context context, ArrayList<String> datas) {
            mContext = context;
            mDatas = datas;
            if (mDatas.size() > 0) {
                for (int i = 0; i < mDatas.size(); i++) {
                    mHeights.add(getRandomHeight());
                }
            }
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            return new NormalHolder(inflater.inflate(R.layout.layout_recyclerview_item, parent, false));
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            NormalHolder normalHolder = (NormalHolder) holder;
            normalHolder.mTV.setText(mDatas.get(position));
    
            ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();
            lp.height = mHeights.get(position);
            normalHolder.mTV.setLayoutParams(lp);
        }
    
        private int getRandomHeight() {
            int randomHeight = 0;
            do {
                randomHeight = (int) (Math.random() * 300);
            } while (randomHeight == 0);
            return randomHeight;
        }
    
        @Override
        public int getItemCount() {
            return mDatas == null ? 0 : mDatas.size();
        }
    
        public class NormalHolder extends RecyclerView.ViewHolder {
            public TextView mTV;
    
            public NormalHolder(View itemView) {
                super(itemView);
                mTV = itemView.findViewById(R.id.tv_item);
                mTV.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    }
    

    5、用RecyclerView实现一个分组列表

    我们将要实现的效果如图:


    屏幕快照 2019-04-19 上午7.35.13.png

    实现原理很简单,在public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)中,第二个参数就是View的类型,而且这个类型,我们是可以人为更改的。我们就可以根据类型的不同,传进去不同的参数。在自定义的Adapter中,我们可以通过getItemViewType函数来返回每个position所对应的类型。
    先上代码:
    填充的Activity类

    public class LinearMultiAct extends AppCompatActivity {
    
        private RecyclerView recyclerView;
        private ArrayList<String> mDatas = new ArrayList<>();
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_recyclerview_linear);
            generateDatas();
            initViews();
        }
    
        private void initViews() {
            recyclerView = findViewById(R.id.recycler_linear);
    
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            //linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(linearLayoutManager);
    
            RecyclerMultiAdapter adapter = new RecyclerMultiAdapter(this, mDatas);
            recyclerView.setAdapter(adapter);
        }
    
        private void generateDatas() {
            for (int i = 0; i < 200; i++) {
                mDatas.add("第" + i + "个items");
            }
        }
    
    }
    

    适配器RecyclerMultiAdapter

    public class RecyclerMultiAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private Context mContext;
        private ArrayList<String> mDatas;
    
        public static enum ITEM_TYPE {
            ITEM_TYPE_SECTION,
            ITEM_TYPE_ITEM
        }
    
        private int M_SECTION_ITEM_NUM = 10;
    
        public RecyclerMultiAdapter(Context context, ArrayList<String> datas) {
            mContext = context;
            mDatas = datas;
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(mContext);
            if (viewType == ITEM_TYPE.ITEM_TYPE_ITEM.ordinal()) {
                return new NormalHolder(inflater.inflate(R.layout.layout_recyclerview_item, parent, false));
            }
            return new SectionHolder(inflater.inflate(R.layout.layout_recyclerview_section, parent, false));
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (holder instanceof NormalHolder) {
                NormalHolder normalHolder = (NormalHolder) holder;
                normalHolder.mTV.setText(mDatas.get(position));
            } else if (holder instanceof SectionHolder) {
                SectionHolder sectionHolder = (SectionHolder) holder;
                sectionHolder.mSectionTv.setText("第 " + position / M_SECTION_ITEM_NUM + " 组");
            }
        }
    
        @Override
        public int getItemCount() {
            return mDatas == null ? 0 : mDatas.size();
        }
    
        @Override
        public int getItemViewType(int position) {
            if (position % M_SECTION_ITEM_NUM == 0) {
                return ITEM_TYPE.ITEM_TYPE_SECTION.ordinal();
            }
            return ITEM_TYPE.ITEM_TYPE_ITEM.ordinal();
        }
    
    
        public class NormalHolder extends RecyclerView.ViewHolder {
            public TextView mTV;
    
            public NormalHolder(View itemView) {
                super(itemView);
                mTV = itemView.findViewById(R.id.tv_item);
                mTV.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    
        public class SectionHolder extends RecyclerView.ViewHolder {
            public TextView mSectionTv;
    
            public SectionHolder(View itemView) {
                super(itemView);
                mSectionTv = itemView.findViewById(R.id.tv_section);
            }
        }
    }
    

    组内容Item的xml(layout_recyclerview_item.xml)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/tv_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp" />
    
    </LinearLayout>
    

    组标题Item的xml(layout_recyclerview_section.xml)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_green_dark">
    
        <TextView
            android:id="@+id/tv_section"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp" />
    
    </LinearLayout>
    

    代码很简单,一些细节还是要说明一下。
    在RecyclerAdapter中,另外再添加一个ViewHolder,命名为SectionHolder,用于加载组标题item的布局。我们假设每十个常规Item添加一个组item,所以这里定义了一个常量M_SECTION_ITEM_NUM ,它的值设定为10,每十个元素返回一个组item的类型数值。为了标识组类型和常规item类型,这里定义一个枚举类型,在其中定义两个值。而ITEM_TYPE.ITEM_TYPE_SECTION.ordinal()的ordinal()函数,则是返回当前枚举值的位置索引,然后在onCreateViewHolder(ViewGroup parent, int viewType)中根据不同的viewType返回不同的viewHolder,如果是常规类型就返回NormalHolder的实例,如果是标题样式,则返回SectionHolder的实例。

    修改一下LayoutManageryou 还可以实现两外一种分组效果

            //如果是横向滚动,后面的数值表示的是几行,如果是竖向滚动,后面的数值表示的是几列
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 5);
            //gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            mRecyclerView.setLayoutManager(gridLayoutManager);
    

    效果如图:


    屏幕快照 2019-04-19 上午7.54.51.png

    二、自定义“间隔线”——ItemDecoration

    三、自定义LayoutManager

    四、RecyclerView实现回收复用

    五、RecyclerView实现滚动画廊控件

    相关文章

      网友评论

        本文标题:四、自定义RecycleView,看完这篇就够了

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