Fork me on GitHub

RecyclerView设计通用Adapter

打造一个属于自己的通用Adapter

前言

目前的项目开发来看,列表依旧是整个APP内最主要的控件,而说到列表就会想到ListView,但ListView不仅优化不好,而且已经被新版Api中RecycleView所取代。所以今天来用RecycleView来设计Adapter的使用。

准备

引入RecycleView

如果我们想在项目中引用RecycleView,那我们首先得先添加V7包中的RecycleView,方法如下

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

创建一个Bean类

因为这个Demo中,需要一个Bean来存储数据,先把这些琐碎的工作做完,才好更好的开展。结构很简单,如下

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
public class Data {
private String title;
private String content;

public Data(String title, String content) {
this.title = title;
this.content = content;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}

Application

因为这个Demo中,我需要在整个项目中使用数据源,所以把数据源写到Application中吧,省着在主要代码中来上一段ArrayList的初始化赋值,有点违和。

定义条目布局

先来写老方式的Adapter

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
public class OldAdapter extends RecyclerView.Adapter<OldAdapter.MyViewHolder> {

private List<Data> dataList;
private LayoutInflater layoutInflater;

OldAdapter(Context context, List<Data> dataList) {
this.dataList = dataList;
this.layoutInflater = LayoutInflater.from(context);
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View inflate = layoutInflater.inflate(R.layout.item_list, viewGroup, false);
return new MyViewHolder(inflate);
}

@Override
public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, int i) {
myViewHolder.tvTitle.setText(dataList.get(i).getTitle());
myViewHolder.tvContent.setText(dataList.get(i).getContent());
}

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

class MyViewHolder extends RecyclerView.ViewHolder {

private TextView tvTitle;
private TextView tvContent;

MyViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tv_title);
tvContent = itemView.findViewById(R.id.tv_content);
}
}
}

实现思路:先定义一个ViewHolder用来做页面的缓存,由于我这个Item布局定义的很简单,所以同理,都是在ViewHolder中find到控件的id,然后初始化。最后在onBindViewHolder()中进行控件和数据的绑定。

最后在MainActivity中,绑定Adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends AppCompatActivity {

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

RecyclerView recyclerView = findViewById(R.id.recycle_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

OldAdapter oldAdapter = new OldAdapter(this, App.dataList);
recyclerView.setAdapter(oldAdapter);
}
}

呈现的效果的话就很简单了,能显示出列表就ok了

ok,现在回归主题,设计一个通用的Adapter来进行数据的显示

先设计一个通用的,实用性高的ViewHolder

其实仔细想,列表中的条目,无非是Textview和ImageView,好像百分之80的条目都是这样,毕竟空间小,也没地方放那么多控件,所以就在ViewHolder里写上TextView和ImageView的复用方法吧。下面有几个关键点

SparseArray

使用 SparseArray 来存放 View 以减少 findViewById 的次数,SparseArray 比 HashMap 更省内存,在某些条件下性能会更好,不过只能存储 key 为 int 类型的数据,正好用来存放资源ID。

getView方法

先从缓存中找,找打的话则直接返回,如果找不到则 findViewById ,再把结果存入缓存中。实现复用

onItemCommonClickListener

其中还定义了一个可扩展的接口,其中定义了两个示例方法。如下

TextView和ImageView的扩展方法

由于上面分析了,条目中一般都用到TextView和ImageView,所以定义几个最常用的方法,以后还可以自己添加

整体代码如下:

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class CommonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {

private SparseArray<View> viewSparseArray;

private onItemCommonClickListener commonClickListener;

public CommonViewHolder(@NonNull View itemView) {
super(itemView);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
viewSparseArray = new SparseArray<>();
}

/**
* 先从缓存中找,找打的话则直接返回,如果找不到则 findViewById ,再把结果存入缓存中
*/
public <T extends View> T getView(int viewId) {
View view = viewSparseArray.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
viewSparseArray.put(viewId, view);
}
return (T) view;
}

/**
* 设置textview内容
*/
public CommonViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}

/**
* 设置是否可见
*/
public CommonViewHolder setViewVisibility(int viewId, int visibility) {
getView(viewId).setVisibility(visibility);
return this;
}

/**
* 设置图片资源
*/
public CommonViewHolder setImageResource(int viewId, int resourceId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resourceId);
return this;
}

protected interface onItemCommonClickListener {
void onItemClickListener(int position);

void onItemLongClickListener(int position);
}

public void setCommonClickListener(onItemCommonClickListener commonClickListener) {
this.commonClickListener = commonClickListener;
}

@Override
public void onClick(View view) {
if (commonClickListener != null) {
commonClickListener.onItemLongClickListener(getAdapterPosition());
}
}

@Override
public boolean onLongClick(View view) {
if (commonClickListener != null) {
commonClickListener.onItemClickListener(getAdapterPosition());
}
return true;
}
}

设计通用的RecycleAdapter

再来实现一个通用的 RecyclerView.Adapter
因为不知道要使用到的数据类型是哪一种,也为了更好的适配各种数据类型,所以这里需要用到泛型
当中,onBindViewHolder(CommonViewHolder holder, int position) 需要我们自己来操作,所以这里再来声明一个抽象方法 bindData(CommonViewHolder holder, T data) ,由子类来负责实现绑定操作

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
abstract class BaseCommonRecycleViewAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> {

private LayoutInflater layoutInflater;
private List<T> dataList;
private int layoutId;

BaseCommonRecycleViewAdapter(Context context, List<T> dataList, int layoutId) {
layoutInflater = LayoutInflater.from(context);
this.dataList = dataList;
this.layoutId = layoutId;
}

@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}

@NonNull
@Override
public CommonViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View inflate = layoutInflater.inflate(layoutId, viewGroup, false);
return new CommonViewHolder(inflate);
}

@Override
public void onBindViewHolder(@NonNull CommonViewHolder commonViewHolder, int i) {
bindData(commonViewHolder, dataList.get(i));
}

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

abstract void bindData(CommonViewHolder holder, T data);
}
  • 大体思路就是把平时老写法的Adapter抽象一层,具体的onCreateViewHolder和onBindViewHolder都在这里进行,才可以减少重复,实现复用。
  • 定义了一个bindData的抽象方法,需要在使用过程中进行数据绑定的重写

如何使用编写好的通用Adapter?

重载两个构造方法,一个实现回调自定义接口,另一个不实现,提高扩展性。如下

最后在主页面,就完成了adapter的调用

0%