当前位置 博文首页 > 启舰:自定义控件三部曲视图篇(四)——RecyclerView系列之一简

    启舰:自定义控件三部曲视图篇(四)——RecyclerView系列之一简

    作者:[db:作者] 时间:2021-06-30 15:30

    绝望的时候不要那么绝望,高兴的时候不要那么高兴,是你慢慢会学会的。 ——董卿


    系列文章: Android自定义控件三部曲文章索引: http://blog.csdn.net/harvic880925/article/details/50995268


    转了一年多,又回来继续做Android。果然还是看到代码最让我兴奋……但有些事,没经历过,总归还是遗憾的。在VIVO的游戏中心,有一个特别炫酷的功能:

    Vivo游戏空间效果

    这个功能就是使用RecyclerView来实现的,在本系列中,我们将最终制作出这样的一个效果出来。

    一、导入Support-v7包

    工欲善其事必先利其器,RecyclerView存在于support-v7包中,我们需要在新建的gradle工程中导入support-v7包:

    compile 'com.android.support:recyclerview-v7:21.0.3'
    

    gralde 版本较高的同学,会发现compile关键字这里会报警告,在高版本gradle中compile已经弃用了,改成了implementation,所以你可以改为:

    implementation 'com.android.support:recyclerview-v7:21.0.3'
    

    加上上面的依赖代码以后,会发现根本不好使,依赖库根本拉不下来。这是为什么呢?

    support-v7包并不是通过maven从远程下载的,而是通过Android Studio的SDK Manager来下载到本地,然后再引用的。本地有没有support-v7包,大家可以看下SDK的这个位置(Sdk\extras\android\m2repository\com\android\support):

    这里写图片描述
    从这里可以看到,在我的com/android/support目录下有所有的support包,这里也有recyclerview-v7包。如果在该文件夹下,你没有的话,可以通过SDK Manager引入:
    这里写图片描述
    下载完成后,在这个文件夹下就会有对应的包存在了,当我们点进去recyclerview-v7文件夹里面,可以看到各种版本:
    这里写图片描述
    大家就可以选择这里已有的版本引入了,比如我这里是有25.3.1的,所以我这里最终的引用包是:

    implementation 'com.android.support:recyclerview-v7:25.3.1'
    

    在引入support包时,需要有两个注意事项:

    • 引入的support包的版本要比targetSdkVersion要高,不然会报错
    • 如果引入了多个support包组件,它们的版本号要保持一致,不然有可能因为不是同一个版本,代码不配套而出现错误,比如我同时引入appcompat包和recyclerview包,那么它们的写法应该是:
    implementation 'com.android.support:appcompat-v7:25.3.1'
    implementation 'com.android.support:recyclerview-v7:25.3.1'
    

    二、简单使用

    在这部分,我们首先做出一个最简单的例子,来看下RecyclerView的使用方法。本小节所实现的效果如下图所示:

    这里写图片描述

    2.1 引入RecyclerView

    首先,在XML中引入RecyclerView:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".LinearActivity">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/linear_recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </android.support.v7.widget.RecyclerView>
    
    </android.support.constraint.ConstraintLayout>
    

    2.2 实现Adapter

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

    当我们写一个Adapter的类派生自RecyclerView.Adapter<RecyclerView.ViewHolder>时,最简单的形式是这样的:

    public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return null;
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
    
        }
    
        @Override
        public int getItemCount() {
            return 0;
        }
    }
    

    这三个函数是强制必须重写的,其中:

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

    可见,这三项其实在listview中也都是需要做的,只是这里单独通过回调给列出来了,我们只需要补充上这三个函数,就算实现了Adapter了.

    在填充RecyclerAdapter之前,我们知道,一般而言ListView的数据都是从外部传进来的,所以我们需要给RecyclerAdapter添加上一个构造函数,将数据从外部传进来:

    private Context mContext;
    private ArrayList<String> mDatas;
    
    public RecyclerAdapter(Context context, ArrayList<String> datas) {
        mContext = context;
        mDatas = datas;
    }
    

    为了方便起来,我们传进来的数据非常简单,就是一个String字符串,同时,由于在RecyclerAdater中,经常会用到Context,所以我们也把Context传进来,并且保存起来.

    接下来,我们就是先创建一个HolderView,然后填充那三个函数。我们都知道HolderView主要是为了保存每一个Item的视图的控件元素。所以我们要先创建一个Item的xml(item_layout.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/item_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp" />
    
    </LinearLayout>
    

    在这个item中,只有一个TextView,所以我们先写一个ViewHolder,ViewHolder的主要作用就是将XML中的控件以变量的形式保存起来,方便我们后面数据绑定.

    public class NormalHolder extends RecyclerView.ViewHolder{
            public TextView mTV;
    
            public NormalHolder(View itemView) {
                super(itemView);
    
                mTV = (TextView) itemView.findViewById(R.id.item_tv);
                mTV.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext,mTV.getText(),Toast.LENGTH_SHORT).show();
                    }
                });
    
            }
        }
    

    在这里,在创建ViewHolder时,将整个ItemView传了进来,然后将TextView从ItemView中取出来保存在mTV变量中.并且,在点击mTV后,弹出这个TextView的内容.

    在写好ViewHolder以后,我们就要逐个填充RecyclerAdapter的三个函数了。首先是onCreateViewHolder:

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        return new NormalHolder(inflater.inflate(R.layout.item_layout,parent,false));
    }
    

    在每一次需要创建ViewHolder时,都会调用onCreateViewHolder函数,所以我们需要在onCreateViewHolder中返回我们创建的ViewHolder实例。

    然后在onBindViewHolder中,将数据与ViewHolder绑定起来:

    public void onBindViewHolder(ViewHolder holder, int position) {
        NormalHolder normalHolder = (NormalHolder)holder;
        normalHolder.mTV.setText(mDatas.get(position));
    }
    

    最后,在getItemCount中返回数据的个数:

    public int getItemCount() {
        return mDatas.size();
    }
    

    到这里,整个RecyclerAdapter就实现完了,完整的代码如下,供大家参考:

    public class RecyclerAdapter extends RecyclerView.Adapter<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.item_layout, parent, false));
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            NormalHolder normalHolder = (NormalHolder) holder;
            normalHolder.mTV.setText(mDatas.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mDatas.size();
        }
    
        public class NormalHolder extends RecyclerView.ViewHolder {
            public TextView mTV;
    
            public NormalHolder(View itemView) {
                super(itemView);
    
                mTV = (TextView) itemView.findViewById(R.id.item_tv);
                mTV.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
                    }
                });
    
            }
        }
    }
    

    2.3 填充RecyclerView

    之后,回到Activity中,首先,构造一个模拟数据的函数,用于填充RecyclerVIew:

    public class LinearActivity extends AppCompatActivity {
    	…………
    	private ArrayList<String> mDatas =new ArrayList<>();
    	private void generateDatas(){
    	    for (int i=0;i<200;i++){
    	        datas.