文章目录
- 一、简介
- 二、基本自定义使用
- 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。
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> mFruitList
和public 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());
}
}
四、出错原因
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。