第十三章 强大的滚动控件——RecyclerView

作者: 忆念成风 | 来源:发表于2017-09-14 17:53 被阅读117次

1. 引言

  上两章简单讲了下ListView的一些基本用法。ListView由于其强大的功能,在Android 开发的过程中贡献卓越。但是由于它自身的部分缺点,不使用一些技巧来提高它的运行效率的话,它的性能体验会非常的差。还有就是ListView的扩展性不够完美,我们在实现数据纵向排列的同时,还需要数据的横向排列。
 RecyclerView可以说是ListView的增强版,不仅可以轻松实现和ListView同样的效果,还会优化ListView存在的各种问题。目前官方更推荐RecyclerView,相信未来也会有更多的程序逐渐从ListView转向Recyclerview。

2. RecyclerView的基本用法

  RecyclerView也属于新增的控件,所以为了能够使用RecyclerView,首先得在项目的build.gradle的dependencies中添加相应的依赖库。


dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:recyclerview-v7:25.3.1'
}

添加完依赖库之后,Sync Now来进行同步,修改activity_main.xml文件中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="com.demo.recyclerviewdemo.MainActivity">

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

</LinearLayout>

在你添加完了RecyclerView的依赖包之后,添加依赖控件的,记得显示完整的包名。为了和之前的ListView的项目有一个对比,这里用同样的图和布局模式。
1.item_list.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="100dp"
    android:gravity="center_vertical"
    android:padding="10dp">

    <ImageView
        android:id="@+id/iv_head"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_launcher"
        android:scaleType="centerCrop"/>
    <TextView
        android:id="@+id/tv_student_name"
        android:layout_width="wrap_content"
        android:layout_height="80dp"
        android:text="学生姓名"
        android:layout_gravity="center"
        android:gravity="center"/>

</LinearLayout>

2.Student.java 学生的实体类


public class Student {
    private  String name;
    private  int  imageId;

    public Student(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getImageId() {
        return imageId;
    }

    public void setImageId(int imageId) {
        this.imageId = imageId;
    }
}


  1. 自定义一个适配器,让这个适配器继承RecyclerView.Adapter ,并将泛型指定为StudentAdapter.ViewHolder,其中ViewHolder是我们自己定义的一个内部类。

public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {

    private List<Student> mStudentList;

    public StudentAdapter(List<Student> mStudentList) {
        this.mStudentList = mStudentList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
        ViewHolder holder=new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Student student=mStudentList.get(position);
        holder.studentImage.setImageResource(student.getImageId());
        holder.studentname.setText(student.getName());

    }

    @Override
    public int getItemCount() {
        return mStudentList.size();
    }

    static  class  ViewHolder extends  RecyclerView.ViewHolder{

        private ImageView studentImage;
        private TextView  studentname;
        public ViewHolder(View itemView) {
            super(itemView);
            studentImage= (ImageView) itemView.findViewById(R.id.iv_head);
            studentname= (TextView) itemView.findViewById(R.id.tv_student_name);
        }
    }
}

  可能刚开始看的时候有点晕,哪里好理解了?我发四没有骗你,你等我慢慢给你分析下。首先我们定义了一个内部类 ViewHolder 继承RecyclerView.ViewHolder,然后ViewHolder下面会有一个构造函数ViewHolder(itemView)需要让你实现,你发现没有,你在构造函数传的这个View参数,其实就是RecyclerView中的子项的最外层布局。所以我们就可以通过findviewByid的方法来获取布局中实例。
  然后我们接下来继续分析,StudentAdapter 中也有一个构造函数,这个方法是用于将要展示的数据源传进来,并赋值给全局变量,后续的操作都是围着这个操作进行的。
  继续往下看,由于StudentAdapter 是继承RecyclerView.Adapter的,所以必须重写 onCreateViewHolder(), onBindViewHolder(),getItemCount()三个方法。 onCreateViewHolder()这个方法是创建ViewHolder 实例,然后将item_list.xml这个子布局也装载进去,并把加载出来的布局传入到构造函数当中,最后返回ViewHolder的实例。onBindViewHolder()这个方法是对RecyclerView的子项的数据进行赋值,会在子项被滚动到屏幕中的时候被执行。我们通过get(position)的方法得到当前项的Student的实例。getItemCount(),告诉RecyclerView,这个集合有多少个子项。直接返回数据的长度。

4.开始使用RecyclerView了。


public class MainActivity extends AppCompatActivity {
    private List<Student> studentList=new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initStudent();//初始化学生数据
        RecyclerView  recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);
        StudentAdapter adapter=new StudentAdapter(studentList);
        recyclerView.setAdapter(adapter);


    }

    private void initStudent() {
        for (int i = 0; i < 2; i++) {
            Student student1=new Student("学生一",R.mipmap.a1);
            studentList.add(student1);
            Student student2=new Student("学生二",R.mipmap.a2);
            studentList.add(student2);
            Student student3=new Student("学生三",R.mipmap.a3);
            studentList.add(student3);
            Student student4=new Student("学生四",R.mipmap.a4);
            studentList.add(student4);
            Student student5=new Student("学生五",R.mipmap.a5);
            studentList.add(student5);
            Student student6=new Student("学生六",R.mipmap.a6);
            studentList.add(student6);
            Student student7=new Student("学生七",R.mipmap.a7);
            studentList.add(student7);
            Student student8=new Student("学生八",R.mipmap.a8);
            studentList.add(student8);
            Student student9=new Student("学生九",R.mipmap.a9);
            studentList.add(student9);
            Student student10=new Student("学生十",R.mipmap.a10);
            studentList.add(student10);
            Student student11=new Student("学生十一",R.mipmap.a11);
            studentList.add(student11);
            Student student12=new Student("学生十二",R.mipmap.a12);
            studentList.add(student12);
            Student student13=new Student("学生十三",R.mipmap.a13);
            studentList.add(student13);

        }
    }

}

 可以看到我们使用同样的办法initStudent()初始化所有的学生对象,接着在OnCreate()方法中线获取到RecyclerView的实例。然后创建一个LinearLayoutManager对象,将它设置到RecyclerView当中,这里使用LinearLayoutManager是线性布局的意思。可以实现和ListView类似的效果。然后是创建Studentadapter的实例,将studentList实例数据传到StudentAdapter的构造函数当中。最后调用setAdapter()方法完成适配器的设置。这样RecyclerView和数据之间的关联就建立完成了。效果如下图所示:

recyclerviewdemo

3.实现横向滚动

 之前说了ListView不能设置横向排列,RecyclerView可以很好的弥补这个不足,使用方法很简单,在MainActivity加入一行代码就可以了。

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initStudent();//初始化学生数据
        RecyclerView  recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);

        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

        recyclerView.setLayoutManager(linearLayoutManager);
        StudentAdapter adapter=new StudentAdapter(studentList);
        recyclerView.setAdapter(adapter);
    }

发现没有,就多了一行代码 linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);设置布局的横向排列。默认是纵向排列的。显示效果如图所示:

横向排列

为啥子ListView不能横向滑动,而RecyclerView可以很轻松的实现呢?这主要的得益于RecyclerView出色的设计。ListView的布局排列是由自身去管理的,而RecyclerView将这个工作给了LayoutManager,LayoutManager中制定了一套可扩展的布局排列接口,子类按照这个借口的规范来实现,这样就可以制定出各种不同的排列的方式的布局。

4.瀑布流布局

RecyclerView除了提供了LinearLayoutManager线性布局排列以外,还提供了GridLayoutManager(网格布局)和StaggeredGridLayoutManager(瀑布流布局)。
这里稍稍修改了下item_list的布局:

<?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:orientation="vertical"
    android:padding="5dp"
    >

    <ImageView
        android:id="@+id/iv_head"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:scaleType="centerCrop"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/tv_student_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:text="学生姓名"
        android:layout_marginTop="10dp"/>

</LinearLayout>

代码也更改了一下,仔细看:

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initStudent();//初始化学生数据
        RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);

        StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);

        recyclerView.setLayoutManager(staggeredGridLayoutManager);
        StudentAdapter adapter=new StudentAdapter(studentList);
        recyclerView.setAdapter(adapter);


    }

对比下前面的发现就改变了一句是不是:
StaggeredGridLayoutManager staggeredGridLayoutManager=new StaggeredGridLayoutManager (3, StaggeredGridLayoutManager.VERTICAL);
StaggeredGridLayoutManager 的参数:

  1. 第一个参数,是设置多少列,我这里设为3列
  2. 第二个参数 ,是指定布局的排列方向
    实现的效果如下图所示:
瀑布流布局

5.网格布局

在经过了这个多的布局之后,下面的的网格布局应该也猜到了,也是修改其中的LayoutManager。

  @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initStudent();//初始化学生数据
        RecyclerView recyclerView= (RecyclerView) findViewById(R.id.recycler_view);
        GridLayoutManager gridLayoutManager=new GridLayoutManager(this,4);
        gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        StudentAdapter adapter=new StudentAdapter(studentList);
        recyclerView.setAdapter(adapter);
    }

GridLayoutManager的两个参数:
1、第一个参数,是设置上下文对象
2、第二个参数是设置排列的行数
还有最后设置好排列的方向


网格布局

6.RecyclerView的点击事件

  正如ListView那样,RecyclerView也必须满足点击这个硬性要求的。但是与ListView不同的是,RecyclerView并没有提供类似setOnItemClickLisenter()这样的注册监听器的方法,而是需要我们自己给子项具体的view去注册点击事件。相比较实现起来,可能会稍微复杂一点。
  大兄弟,看到这里你是不是又有点晕了啊?都说了是比ListView更优秀,那搞个点击事件怎么实现起来还复杂了呢?...... 大哥,我真的没骗你,拳头放下来。因为ListView的点击处理,是点击一整个子项目,然后出发点击事件的是不,哪如果我只想子项目里面的某一个控件出发自己的点击事件呢?ListView并没有给出人性化的处理,处理起来比较麻烦。所以RecyclerView就放弃了子项的点击监听事件的监听器。所有的点击事件都由具体的View来处理,这样是不是没有这个烦恼了啊。
  这里讲一下它的点击事件,需要修改StudentAdapter的代码。我们先是修改了ViewHolder,在ViewHolder 添加studentView 变量来保存子项最外层的布局的实例。然后在onCreateViewHolder()方法中注册点击事件就可以了。这里分别为最外层的布局和ImageViewd都注册了点击事件。(意思是,它不仅给每个item都设置了点击事件,连item里面的imageview也设置了点击事件)。通过holder 获取子项的view,然后设置点击事件。通过holder获取子项的view的position。这样就拿到了子项的Student的数据。设置一个点击事件,来显示具体的数据值。在点击文字的时候,我们没有给文字设置点击事件,但是给studentView设置了点击事件,所以还是被最外层的布局捕获到,弹出Toast。

ublic class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.ViewHolder> {

    private List<Student> mStudentList;

    public StudentAdapter(List<Student> mStudentList) {
        this.mStudentList = mStudentList;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
        final ViewHolder holder=new ViewHolder(view);

        holder.studentView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Student student = mStudentList.get(position);
                Toast.makeText(v.getContext(), "你点击了"+student.getName(), Toast.LENGTH_SHORT).show();

            }
        });
        holder.studentImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Student student = mStudentList.get(position);
                Toast.makeText(v.getContext(), "你点击了"+student.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Student student=mStudentList.get(position);
        holder.studentImage.setImageResource(student.getImageId());
        holder.studentname.setText(student.getName());

    }

    @Override
    public int getItemCount() {
        return mStudentList.size();
    }

    static   class  ViewHolder extends  RecyclerView.ViewHolder{

        private ImageView studentImage;
        private TextView  studentname;
          View  studentView;

          public ViewHolder(View itemView) {
            super(itemView);
            studentView=itemView;
            studentImage= (ImageView) itemView.findViewById(R.id.iv_head);
            studentname= (TextView) itemView.findViewById(R.id.tv_student_name);
        }
    }


}

效果如图:

recyclerview的点击事件
项目代码github地址:https://github.com/wangxin3119/recyclerview

相关文章

网友评论

本文标题:第十三章 强大的滚动控件——RecyclerView

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