文章目录

  • 一、简介
  • 二、基本自定义使用
  • 1.activity_main里定义控件
  • 2.自定义Adapter
  • (1)给条目创建一个Class类
  • (2)给条目创建一个布局
  • (3)自定义Adapter
  • 3.在MainActivity中使用
  • 三、点击事件
  • 1.修改onCreaterViewHolder()
  • 2.例子:
  • 四、出错原因
  • 1.控件id引用错误
  • 2.List容器问题



一、简介

RecyclerView控件可以说是ListView的升级版,尤其是RecyclerView解决了ListView上控件和条目点击冲突的问题。

Google官方也推荐使用RecyclerView代替ListView。

二、基本自定义使用

这样的效果:很明显这是两个Item条目,每个Item里面有一个文本TextView和一张图片ImageView。

Android RecyclerView实现PickerView android中的recycleview_控件

1.activity_main里定义控件

这个很简单,就和Button控件一样。

<?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=".MainActivity">

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

</LinearLayout>

2.自定义Adapter

我们将RecyclerView的每个Item称为条目。

我们要作的事情有三步:

  • 给条目创建一个Class类
  • 给条目创建一个布局
  • 自定义Adapter

(1)给条目创建一个Class类

  • 文本TextView是String字符串
  • 图片ImageView是int资源索引id
  • 按钮之类的就不用写,因为不需要赋值。
package com.example.hello;

public class Fruit {
    private String mName;
    private int mImageId;

    public Fruit(String name,int ImageId)
    {
        mName=name;
        mImageId=ImageId;
    }

    public String getmName()
    {
        return mName;
    }

    public int getmImageId()
    {
        return mImageId;
    }
}

(2)给条目创建一个布局

这个布局是用来排布条目内部的布局。这个加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"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/fruit_name"/>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/fruit_image"/>

</LinearLayout>

(3)自定义Adapter

定义Adapter才是考验技术的时刻。

package com.example.hello;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        TextView fruitName;
        ImageView fruitImage;

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

    public FruitAdapter(List<Fruit> fruitList)
    {
        mFruitList=fruitList;
    }

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


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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit=mFruitList.get(position);
        holder.fruitName.setText(fruit.getmName());
        holder.fruitImage.setImageResource(fruit.getmImageId());
    }
}
  • 分析FruitAdapter类的继承:RecyclerView.Adapter<FruitAdapter.ViewHolder>
  • 我们肯定是要继承RecyclerView的东西,而我们是定义Adapter,那就是RecyclerView.Adapter。
  • 里面的模板参数是自定义Adapter的名字.ViewHolder,这个ViewHolder是我们自定义的一个内部类。
  • 分析成员变量和构造函数:
    private List<Fruit> mFruitListpublic FruitAdapter(List<Fruit> fruitList)
  • 这个List成员变量表示一个条目的内容,如String的值和图片的索引id。
  • 到时候我们的使用的时候,是传一个List容器进来,所以构造函数是这样。也因此需要一个List成员变量来保存传进来的List容器,而且成员方法也需要用到List。
  • 分析静态类ViewHolder:
  • 为什么要继承?
    因为RecyclerView.ViewHolder是个抽象类,所以必须继承才能使用。
  • 这个构造方法作用?
    这个构造方法就是像在MainActivity.class里面绑定Button控件一样,将控件和条目内部的布局里的id通过findViewById()绑定在一起。
  • ViewHolder的作用理解:
    我认为ViewHolder其实就是一个把控件View整合在一起的仓库Holder,里面的东西不就是把各个View声明和findViewById()的过程写在一起。
    这样整合到一起后,就很方便。
  • 分析三个重写的成员方法
  • 必须重写,少写报错
    因为RecyclerView.Adapter也是个抽象类,所以必须重写它的这个三个方法:onCreateViewHolder()、onBindViewHolder()和getItemCount()。
  • onCreateViewHolder()
    用于创建ViewHolder实例的。
    我们在这个方法中将条目的布局加载到View中,然用View创建一个ViewHolder实例,并把加载出来的布局传入到构造函液当中,最后将ViewHolder的实例返回。
  • onBindViewHolder()
    用于对RecyclerView子项的数据进行赋值的。
    会在每个子项被滚动到屏幕内的时候执行(滚动到屏幕内才执行,这就是优化),这里我们通过position参数得到当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView当中即可。
  • getltemCountO
    告诉RecyclerView一共有多少子项,直接返回成员变量List的长度就可以了。

3.在MainActivity中使用

package com.example.hello;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;

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

        recyclerView=findViewById(R.id.rl);
		
		//构造一个List对象
        List<Fruit> fruitList=new ArrayList<>();
        fruitList.add(new Fruit("banana",R.drawable.banana));
        fruitList.add(new Fruit("watermelon",R.drawable.watermelon));

		//设置RecyclerView的条目在RecyclerView中的排布布局
        LinearLayoutManager linearLayoutManager=new LinearLayoutManager(MainActivity.this);
        recyclerView.setLayoutManager(linearLayoutManager);

		//设置RecyclerView的Adatper
        FruitAdapter mFruitAdapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(mFruitAdapter);
    }
}

三、点击事件

1.修改onCreaterViewHolder()

只需要在FruitAdapter里做一点修改,比ListView简单多了。

冲突解决机制:
这个控件可以是条目布局里面的控件或者条目本身。当你没有点击特定的条目里的控件时,就是认为点击条目本身。

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_item,parent,false);
    ViewHolder viewHolder=new ViewHolder(view);
    viewHolder.控件.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
    return viewHolder;
}
  • 在ViewHolder构造里声明条目本身点击:
    View myView=view,view是ViewHolder里传进来的参数
  • 在setOnClickListener()中
    获得点击条目的下标(从0开始):int pos=viewHolder.getAdapterPosition(); 获得上下文:view.getContext(),用MainActivity.class报错。

2.例子:

package com.example.hello;

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 androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{
        View fruitView;
        TextView fruitName;
        ImageView fruitImage;

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

    public FruitAdapter(List<Fruit> fruitList)
    {
        mFruitList=fruitList;
    }

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


    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.layout,parent,false);
        final ViewHolder viewHolder=new ViewHolder(view);
        viewHolder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int pos=viewHolder.getAdapterPosition();
                Toast.makeText(view.getContext(),"你点击了条目,位置"+pos,Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int pos=viewHolder.getAdapterPosition();
                Toast.makeText(view.getContext(),"你点击了图片,位置"+pos,Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.fruitName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int pos=viewHolder.getAdapterPosition();
                Toast.makeText(view.getContext(),"你点击了名字,位置"+pos,Toast.LENGTH_SHORT).show();
            }
        });
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit=mFruitList.get(position);
        holder.fruitName.setText(fruit.getmName());
        holder.fruitImage.setImageResource(fruit.getmImageId());
    }
}

Android RecyclerView实现PickerView android中的recycleview_控件_02

四、出错原因

1.控件id引用错误

static class ViewHolder extends RecyclerView.ViewHolder
{
    TextView itemName;
    TextView itemProgress;
    Button itemBtn;

    public ViewHolder(View view)
    {
        super(view);
        itemName=view.findViewById(R.id.name);
        itemProgress=view.findViewById(R.id.progress);
        itemBtn=view.findViewById(R.id.show);
    }
}

文件多了,控件id就容易记混。比如,itemBtn应该绑定的是R.id.btn。

2.List容器问题

Adapter中的List不用new ArrayList<>(),因为List在构造参数会传入一个实例参数。

而Activity中的List必须new ArrayList<>(),因为List要在Activity中添加数据,否则会报错java.lang.NullPointerException。