RecyclerView 的基本使用

1.添加依赖

1
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha02'

2.在layout布局文件中声明

activity_main.xml的代码如下:

1
2
3
4
5
6
7
8
9
10
11
<?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:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

3.在当前类中获取RecyclerView控件,并设置适配器

因为数据是无法直接传递给RecyclerView的,所以需要借助适配器来完成。 在设置Adapter之前要初始化数据源,然后通过Adapter的构造方法将数据传递给适配器, 当数据发生变化时我们只需要更新这个数据源就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

public class MainActivity extends AppCompatActivity {
private ArrayList<Fruit> fruits = new ArrayList<>();

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

RecyclerView rv = findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(RecyclerView.VERTICAL);
rv.setLayoutManager(layoutManager);

//创建数据源
initData();
FruitAdapter adapter = new FruitAdapter(fruits);
rv.setAdapter(adapter);


}

}

RecyclerView必须要设置 layoutManager ,通过设置不同的 layoutManager 实现不同形式的列表。

常见的有:

  • LinearLayoutManager 纵向/横向列表
  • GridLayoutManager 网格布局
  • StaggeredGridLayoutManager 瀑布流布局

4. Adapter 的编码

RecyclerView已经为我们提供了一个适配器,我们只用继承它,并且重写它的三个方法 onCreateViewHolderonBindViewHoldergetItemCount就可以了 。

  • onCreateViewHolder主要是创建ViewHolder实例的, 在这个方法中可以加载子条目的布局文件, 设置子条目的点击事件
  • onBindViewHolder 主要是为子条目的控件进行赋值。 会在每个子条目滚动到屏幕内的时候执行。
  • getItemCount()返回的是条目数量,直接返回数据源的长度即可。

疑问: 那么onCreatViewHolder什么时候执行呢? (待理解)

我们定义的ViewHolder只需要继承Recycler的ViewHolder即可。复用holder的逻辑RecyclerView已经帮我们封装好了,所以不用像ListView那样还需要我们自己实现。

ViewHolder 构造函数的参数 itemView 其实就是 RecyclerView 子条目布局fruit_item(代码见下方)的根布局, 它就是RecyclerView列表中的子条目。 然后使用子条目fruit_item的findViewById()获取条目中的子View。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private static final String TAG = "FruitAdapter";

private final ArrayList<Fruit> mFruits;

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

static class ViewHolder extends RecyclerView.ViewHolder {

ImageView ivFruit;
TextView tvName;

public ViewHolder(@NonNull View itemView) {
super(itemView);
ivFruit = itemView.findViewById(R.id.iv_fruit);
tvName = itemView.findViewById(R.id.tv_name);
}
}


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

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruits.get(position);
holder.ivFruit.setImageResource(fruit.getImageId());
holder.tvName.setText(fruit.getName());
}


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

RecyclerView条目的布局fruit_item.xml的布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

<?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"
xmlns:tool="http://schemas.android.com/tools"
android:orientation="horizontal">

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


<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tool:text="苹果"
android:layout_gravity="center_vertical"
/>

</LinearLayout>

5.设置网格列表和瀑布流

我们可以通过 LayoutManager 来设置布局的类型。

LinearLayoutManager 为列表类型的布局, 可以通过setOrientation()来设置列表的方向。

1
2
3
4

LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//VERTICAL 竖直列表 HORIZONTAL:水平列表
layoutManager.setOrientation(RecyclerView.VERTICAL);

除了设置列表,还可以设置网格列表和瀑布流列表。
设置网格列表代码如下:

1
2
3
//参数二: 网格的列数
GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
rv.setLayoutManager(layoutManager);

设置瀑布流

1
2
3
4
// 参数一:如果瀑布流的方向是vertical, 该参数指定了布局的列数. 如果瀑布流的方向是horizontal, 该参数指定了布局的行数
//参数二: 瀑布流的方向
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
rv.setLayoutManager(layoutManager);

6.为RecyclerView设置点击事件

RecyclerView并没有像ListView一样为我们提供类似setOnItemClickListener的条目点击事件, 而是需要我们自己给子项具体的View去注册点击事件,所以实现起来比ListView要复杂一些。

那么为什么RecyclerView在各个方面的设计都要优于ListView, 偏偏在点击事件上却”没有处理的非常好”呢? 其实不是这样的, ListView的点击事件并不人性化, setOnItemClickListener方法注册的是子条目的点击事件, 但是如果点击的是子条目里具体的按钮呢? 虽然ListView也能实现,但是相对就比较麻烦了。 所以,RecyclerView干脆直接放弃了子条目的点击事件,所以的点击事件都由具体的View去注册。点击事件的实现方法如下:

首先, 在ViewHolder中添加fruitView变量来保存子项最外布局,

1
2
3
4
5
6
7
8
9
10
11
12
13
static class ViewHolder extends RecyclerView.ViewHolder {

View fruitView;
ImageView ivFruit;
TextView tvName;

ViewHolder(@NonNull View itemView) {
super(itemView);
fruitView = itemView;
ivFruit = itemView.findViewById(R.id.iv_fruit);
tvName = itemView.findViewById(R.id.tv_name);
}
}

然后在onCreateViewHolder()中注册条目或者条目中子view的点击事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

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

...

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

//因为fruitView为子条目的最外层布局,所以该点击事件就是整个条目的点击事件
viewHolder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (parent instanceof RecyclerView) {
int position = ((RecyclerView) parent).getChildAdapterPosition(view);
onRecyclerItemClickListener.onRecyclerItemClick(position);
}
}
});


//子项中子view的点击事件
viewHolder.ivFruit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

}
});

return viewHolder;
}



private OnRecyclerItemClickListener onRecyclerItemClickListener;

interface OnRecyclerItemClickListener {
void onRecyclerItemClick(int position);
}
@Override
public int getItemCount() {
return mFruits.size();
}

void setOnRecyclerItemClickListener(OnRecyclerItemClickListener onRecyclerItemClickListener) {
this.onRecyclerItemClickListener = onRecyclerItemClickListener;
}

...

}

参考:郭霖《第一行代码》