Android之RecyclerView用法总结

之前总结了ListView的用法(链接:https://www.jianshu.com/p/0c30f59e0280),但是ListView的扩展性不够好,只能实现数据纵向滚动的效果,如果我们想实现横向滚动的话,ListView是做不到的。因此,我们需要一个更强大的滚动控件RecyclerView。

RecyclerView的基本用法
RecyclerView是属于新增的控件,为了让RecyclerView在所有Android版本上都能使用,Android团队将其定义在了support库当中。因此,想要使用RecyclerView 控件,首先需要在项目的app/build.gradle中添加相应的依赖库才行

  1. 打开app/build.gradle文件,在dependenceies闭包中添加如下内容:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    // 添加的依赖库
    implementation'com.android.support:recyclerview-v7:28.0.0'
}

添加之后记得要点击一下Sync Now来进行同步

  1. 修改activity_main中的代码如下所示:



    
    


在布局中加入RecyclerView控件,为其制定一个id,并将宽度和高度都设置为match_parent,这样RecyclerView就可以占满整个布局空间

  1. 为了实现和ListView一样的效果,我们将之前的Fruit类和fruit_item代码复制过来
    Fruit类代码:

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

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

    public String getName(){
        return name;
    }

    public int getImageId(){
        return  imageId;
    }
}

fruit_item代码




    

    

这里我们需要注意,fruit_item中的LinearLayout的高度指定了为50dp,运行的效果是这样的:


image.png

如果我们将高度指定为match_parent,则得到的效果如下:


image.png

这是为什么呢?这是因为,如果将高度指定为match_parent,那么一个fruit_item就会占满了整个布局的空间,因此我们就只能看到一个选项
  1. 接下来需要为RecyclerView准备一个适配器FruitAdapter,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。其中ViewHolder是我们在FruitAdapter中定义的一个内部类,代码如下所示:
package com.example.apple.recyclerview;


import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class FruitAdapter extends RecyclerView.Adapter {

    private List mFruitList;

    // 定义一个内部类ViewHolder,继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中要传入一个
    // view参数,这个参数通常就是RecyclerView子项的最外层布局,那么我们就可以通过findViewById()方法
    // 来获取到布局中的ImageView等的实例了
    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view){
            super(view);
            fruitImage=view.findViewById(R.id.fruit_image);
            fruitName=view.findViewById(R.id.fruit_name);
        }
    }

    // 用于把要展示的数据传进来 ,并赋值给一个全局变量mFruitList,后续的操作都将在这个数据源的基础上进行
    public FruitAdapter(List fruitList){
        mFruitList=fruitList;
    }

    // 用于创建ViewHolder实例,我们在这个方法中将fruit_item布局加载进来,然后创建一个ViewHolder实例,
    // 并把加载出来的布局传入到构造函数当中,最后将ViewHolder的实例返回
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        ViewHolder holder=new ViewHolder(view);
        return holder;
    }

    // 用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行,这里我们通过position
    // 参数得到当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView当中
    @Override
    public void onBindViewHolder(ViewHolder holder,int position){
        Fruit fruit=mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    // 用于告诉RecyclerView一共有多少子项,直接返回数据源长度
    @Override
    public int getItemCount(){
        return mFruitList.size();
    }

}

其中内部的函数的作用均在代码中有注释

  1. 适配器准备好了之后,我们就可以开始使用RecyclerView了,修改MainActivity中的代码,如下所示:
package com.example.apple.recyclerview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List fruitList=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化水果数据
        initFruits();
        RecyclerView recyclerView=findViewById(R.id.recycler_view);

        // 创建一个LinearLayoutManager对象,并把它设置到RecyclerView当中
        // LayoutManager用于指定RecyclerView的布局方式,这里是线性布局的意思
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        // 创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    // 初始化数据
    private void initFruits(){
        for(int i=0;i<10;i++){
            Fruit a=new Fruit("a",R.drawable.a);
            fruitList.add(a);
            Fruit b=new Fruit("B",R.drawable.b);
            fruitList.add(b);
            Fruit c=new Fruit("C",R.drawable.c);
            fruitList.add(c);
            Fruit d=new Fruit("D",R.drawable.d);
            fruitList.add(d);
        }
    }
}

现在运行一下程序,效果如下图所示:

image.png

可以看到,我们使用RecyclerView实现了和ListView几乎一模一样的效果,虽说代码量方面并没有明显的减少,但是逻辑变得更加清晰了。当然这只是RecyclerView的基本用法而已,接下来我们就看一看RecyclerView如何实现横向滚动和瀑布流布局

实现横向滚动和瀑布流布局

  1. 要实现横向滚动的效果,首先要对fruit_item布局进行修改,因为目前这个布局里面的元素是水平排列的,使用于纵向滚动的场景,而如果要实现横向滚动的话,应该把fruit_item里的元素改成垂直排列的才比较合理。修改fruit_item中的代码,如下所示:



    

    

可以看到我们把LinearLayout改成竖直方向,并把宽度设置为100dp

  1. 接下来修改MainActivity中的代码,如下所示:
package com.example.apple.recyclerview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List fruitList=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化水果数据
        initFruits();
        RecyclerView recyclerView=findViewById(R.id.recycler_view);

        // 创建一个LinearLayoutManager对象,并把它设置到RecyclerView当中
        // LayoutManager用于指定RecyclerView的布局方式,这里是线性布局的意思
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);  //新加入的代码
        recyclerView.setLayoutManager(layoutManager);

        // 创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    // 初始化数据
    private void initFruits(){
        for(int i=0;i<10;i++){
            Fruit a=new Fruit("a",R.drawable.a);
            fruitList.add(a);
            Fruit b=new Fruit("B",R.drawable.b);
            fruitList.add(b);
            Fruit c=new Fruit("C",R.drawable.c);
            fruitList.add(c);
            Fruit d=new Fruit("D",R.drawable.d);
            fruitList.add(d);
        }
    }
}

MainActivity中只加入了一行代码,调用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列的,我们传入LinearLayoutManager.HORIZONTAL表示让布局横行排列。重新运行一下程序,效果如下图所示:

image.png

除了LinearLayoutManager之外,RecyclerView还给我们提供了GridLayoutManager和StaggeredGridLayoutManager两种内置的布局排列方式。GridLayoutManager用于实现网格布局,StaggeredGridLayoutManager用于实现瀑布流布局。

瀑布流布局

  1. 下面我们来实现一下瀑布流布局,首先还是修改一下fruit_item中的代码,如下所示:



    

    

这里做了几处小的调整,首先将LinearLayout的宽度由100dp改成了match_parent,因为瀑布流布局的宽度是根据布局的列数来自动适配的,而不是一个固定的值。

  1. 接着修改MainActivity中的代码,如下所示:
package com.example.apple.recyclerview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private List fruitList=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化水果数据
        initFruits();
        RecyclerView recyclerView=findViewById(R.id.recycler_view);

        // 创建一个瀑布流布局对象
        StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);

        // 创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    // 初始化数据
    private void initFruits(){
        for(int i=0;i<10;i++){
            Fruit a=new Fruit(getRandomLengthName("a"),R.drawable.a);
            fruitList.add(a);
            Fruit b=new Fruit(getRandomLengthName("b"),R.drawable.b);
            fruitList.add(b);
            Fruit c=new Fruit(getRandomLengthName("c"),R.drawable.c);
            fruitList.add(c);
            Fruit d=new Fruit(getRandomLengthName("d"),R.drawable.d);
            fruitList.add(d);
        }
    }

    private String  getRandomLengthName(String name){
        Random random=new Random();
        int length=random.nextInt(20)+1;
        StringBuilder builder=new StringBuilder();
        for (int i=0;i

首先,在onCreate()方法中,我们创建了一个StaggeredGridLayoutManager的实例,它的构造函数接收两个参数,第一个参数用于指定布局的列数,传入3表示会把布局分为3列;第二个参数用于指定布局的排列方向,传入StaggeredGridLayoutManager.VERTICAL表示会让布局纵向排列,最后再把创建好的实例设置到RecyclerView当中就可以了。效果如下图所示:

image.png

看起来效果还是不错的,接下来继续看看RecyclerView的点击事件

RecyclerView的点击事件
不同于ListView,RecyclerView并没有提供类似于setOnItemClickListener()这样的注册监听器方法,而是需要我们自己给子项具体的View去注册点击事件。下面我们就来看一看如何在RecyclerView中注册点击事件

  1. 修改FruitAdapter中的代码,如下所示:
package com.example.apple.recyclerview;


import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

public class FruitAdapter extends RecyclerView.Adapter {

    private List mFruitList;

    // 定义一个内部类ViewHolder,继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中要传入一个
    // view参数,这个参数通常就是RecyclerView子项的最外层布局,那么我们就可以通过findViewById()方法
    // 来获取到布局中的ImageView等的实例了
    static class ViewHolder extends RecyclerView.ViewHolder{
        View fruitView;  // 新加
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder(View view){
            super(view);
            fruitView=view;  // 新加
            fruitImage=view.findViewById(R.id.fruit_image);
            fruitName=view.findViewById(R.id.fruit_name);
        }
    }

    // 用于把要展示的数据传进来 ,并赋值给一个全局变量mFruitList,后续的操作都将在这个数据源的基础上进行
    public FruitAdapter(List fruitList){
        mFruitList=fruitList;
    }

    // 用于创建ViewHolder实例,我们在这个方法中将fruit_item布局加载进来,然后创建一个ViewHolder实例,
    // 并把加载出来的布局传入到构造函数当中,最后将ViewHolder的实例返回
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        View view=LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        final ViewHolder holder=new ViewHolder(view);
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position=holder.getAdapterPosition();
                Fruit fruit=mFruitList.get(position);
                Toast.makeText(v.getContext(),"you clicked view"+fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });

        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position=holder.getAdapterPosition();
                Fruit fruit=mFruitList.get(position);
                Toast.makeText(v.getContext(),"you clicked image"+fruit.getName(),Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    // 用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行,这里我们通过position
    // 参数得到当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView当中
    @Override
    public void onBindViewHolder(ViewHolder holder,int position){
        Fruit fruit=mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    // 用于告诉RecyclerView一共有多少子项,直接返回数据源长度
    @Override
    public int getItemCount(){
        return mFruitList.size();
    }

}

我们先是修改了ViewHolder,在ViewHolder中添加了fruitView变量来保存子项最外层布局的实例,然后在onCreateViewholder()方法中注册点击事件就可以了。这里分别为最外层布局和ImageView都注册了点击事件。我们在两个点击事件中先获取了用户点击的position,然后通过position拿到相应的Fruit实例,再使用Toast分别弹出两种不同的内容以示区别。

注意:在ListView中,注册监听器是在MainActivity中;而在RecyclerView中,注册事件是在适配器FruitAdapter中注册

文章来源:《第一行代码第2版》

你可能感兴趣的:(Android之RecyclerView用法总结)