12.Android开发笔记:RecyclerView

0.准备工作

RecyclerView是一个滚动控件,
想要使用RecyclerView这个控件,需要将 v7 支持库添加到项目中,
recyclerview 官方文档

首先需要在项目的build.gradle中添加相应的依赖库才行。
打开app/build.gradle文件,在dependencies闭包中添加如下内容:

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

gradle文件自上次同步之后又发生了变化,需要再次同步才能使项目正常工作。这里只需要点击Sync Now就可以了,然后gradle会开始进行同步。

1.简单用法

(1).修改 activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app = "http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<!--   
   官网示例,运行出错
   <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
-->

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

(2).创建Fruit

public class Fruit {

    private String name;
    private int imageId;

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

    }

    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }
}


(3)创建Adapter

1).FruitAdapter 继承 RecyclerView.Adapter<FruitViewHolder>
2).FruitViewHolder是自定义内部类
3).要实现三个接口:
onCreateViewHolder, onBindViewHolder ,getItemCount


public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitViewHolder> {

    private List<Fruit> fruits;

    static class FruitViewHolder extends RecyclerView.ViewHolder{

        ImageView fruitImage;
        TextView fruitName;

        /**
         * ViewHolder的构造函数中要传入一个View参数,
         * 这个参数通常就是RecyclerView子项的最外层布局,
         * 那么我们就可以通过findViewById()方法来获取到布局中的ImageView和TextView的实例
         * */
        public FruitViewHolder(@NonNull View itemView) {
            super(itemView);
            fruitImage = itemView.findViewById(R.id.fruit_image);
            fruitName = itemView.findViewById(R.id.fruit_name);

        }
    }

    public FruitAdapter(List<Fruit> fruits) {
        this.fruits = fruits;
    }

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

        return  holder;
    }

    /**
     *用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行
     * */
    @Override
    public void onBindViewHolder(@NonNull FruitViewHolder holder, int position) {
        Fruit fruit = fruits.get(position);

        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

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

}


(4)绑定数据


public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruits = new ArrayList<>();

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

        InitFruits();

        RecyclerView recyclerView = findViewById(R.id.my_recycler_view);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);
        FruitAdapter fruitAdapter = new FruitAdapter(fruits);
        recyclerView.setAdapter(fruitAdapter);
    }

    private  void InitFruits(){

        for (int i= 0; i < 10 ; i++){
            Fruit taozi = new Fruit(R.drawable.taozi, "桃子");
            fruits.add(taozi);

            Fruit putao = new Fruit(R.drawable.putao, "葡萄");
            fruits.add(putao);

            Fruit huolongguo = new Fruit(R.drawable.huolongguo, "火龙果");
            fruits.add(huolongguo);

            Fruit lamei = new Fruit(R.drawable.lanmei, "蓝莓");
            fruits.add(lamei);

            Fruit xiguang = new Fruit(R.drawable.xigua, "西瓜");
            fruits.add(xiguang);
        }
    }
}


效果:

把图片放在res/drawable文件夹,启动App时,可能出现的bug:

    ...
    java.lang.RuntimeException: Canvas: trying to draw too large(110250000bytes) bitmap.
    ...

参见:解决方案

新建 drawable-nodpi 文件夹把图片移动到该文件夹即可。

2 实现横向滚动

1).修改fruit_item.xml 布局:
图片和文字垂直排列: android:orientation="vertical"
调整每个Fruit宽度: android:layout_width="wrap_content"


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView android:id="@+id/fruit_image"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="center_vertical"/>
    <TextView android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"/>
</LinearLayout> 

2)修改滚动方向:

  ...
  linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
  ...

3.瀑布流布局

ListView的布局排列是由自身去管理的,
RecyclerView则将这个工作交给了LayoutManager,
LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局了。
除了LinearLayoutManager之外,
RecyclerView还给我们提供了GridLayoutManagerStaggeredGridLayoutManager这两种内置的布局排列方式。
GridLayoutManager可以用于实现网格布局,
StaggeredGridLayoutManager可以用于实现瀑布流布局。

1)修改fruit_item.xml 布局:
修改宽度: android:layout_width="120dp"


<!--瀑布流布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView android:id="@+id/fruit_image"
        android:layout_width="120dp"
        android:layout_height="100dp"
        android:layout_gravity="center_vertical"/>
    <TextView android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"/>
</LinearLayout>

2)使用瀑布流布局


        RecyclerView recyclerView = findViewById(R.id.my_recycler_view);

       /*
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //横向布局
        recyclerView.setLayoutManager(linearLayoutManager);
        */

        //瀑布流布局:其中参数StaggeredGridLayoutManager.VERTICAL表示 从左往右依次排开,满行另起一行
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(6, StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);

        FruitAdapter fruitAdapter = new FruitAdapter(fruits);
        recyclerView.setAdapter(fruitAdapter);

4.RecyclerView的点击事件

不过不同于ListView的是,RecyclerView并没有提供类似于setOnItemClickListener()这样的注册监听器方法,而是需要我们自己给子项具体的View去注册点击事件,
ListView在点击事件上的处理并不人性化,setOnItemClickListener()方法注册的是子项的点击事件,但如果我想点击的是子项里具体的某一个按钮呢?虽然ListView也是能做到的,但是实现起来就相对比较麻烦了。
为此,RecyclerView干脆直接摒弃了子项点击事件的监听器,所有的点击事件都由具体的View去注册,就再没有这个困扰了。

1)FruitViewHolder
新增变量 View fruitView; 用于保存子项的最外层布局的实例


    static class FruitViewHolder extends RecyclerView.ViewHolder{
        View fruitView; //子项的最外层布局的实例
        ImageView fruitImage;
        TextView fruitName;

        /**
         * ViewHolder的构造函数中要传入一个View参数,
         * 这个参数通常就是RecyclerView子项的最外层布局,
         * 那么我们就可以通过findViewById()方法来获取到布局中的ImageView和TextView的实例
         * */
        public FruitViewHolder(@NonNull View itemView) {
            super(itemView);

            fruitView = itemView; //子项的最外层布局的实例
            fruitImage = itemView.findViewById(R.id.fruit_image);
            fruitName = itemView.findViewById(R.id.fruit_name);

        }
    }

  1. FruitAdapter
    在方法onCreateViewHolder中注册点击事件
@NonNull
    @Override
    public FruitViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fruit_item, parent, false);
        final FruitViewHolder holder = new FruitViewHolder(view);

        //为子项的最外层布局注册点击事件:
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = fruits.get(position);
                Toast.makeText( v.getContext(), "点击子项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 = fruits.get(position);
                Toast.makeText(v.getContext(), "点击子项View中的ImageView:"+ fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });

        return  holder;
    }

点击图片

点击文字,文字没有注册点击事件,最终会被最外层布局捕获

原文地址:https://www.cnblogs.com/easy5weikai/p/12518238.html