前情提要
最近项目我在项目中使用了RecyclerView代替了ListView.由于项目中有多出列表项使用RecyclerView,这就导致需要写多个Adapter和ViewHolder.
其实,怎么说呢?就是懒,想少写代码,所以想研究一下能否简化一下.
具体实现
封装分为Adapter和ViewHolder两部分,如下所示.
ViewHolder
抽象类BaseHolder继承RecyclerView.ViewHolder,并依赖注入的数据类型M,即和ViewHolder绑定的数据类型为M.
该抽象类包含一个构造方法,用于获取item对应的布局.一个抽象函数用于将数据设置到item上面.
[代码]java代码:01
02
03
04
05
06
07
08
09
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/**
* 基础的ViewHolder
* Created by zyz on 2016/5/17.
*/
public abstract class BaseHolder extends RecyclerView.ViewHolder {
public BaseHolder(ViewGroup parent, @LayoutRes int resId) {
super(LayoutInflater.from(parent.getContext()).inflate(resId, parent, false));
}
/**
* 获取布局中的View
* @param viewId view的Id
* @param View的类型
* @return view
*/
protected T getView(@IdRes int viewId){
return (T) (itemView.findViewById(viewId));
}
/**
* 获取Context实例
* @return context
*/
protected Context getContext() {
return itemView.getContext();
}
/**
* 设置数据
* @param data 要显示的数据对象
*/
public abstract void setData(M data);
}
Adapter
Adapter类也为抽象类,继承于RecyclerView.Adapter,并绑定了两个泛型:
1. M : 用于该 Adapter 的列表的数据类型,即List.
2. H : 即和 Adapter 绑定的 Holder 的类型.
并且,该 Adapter 自带 List 数据集合,声明时可以不用传递数据集合.也包含了 List 的相关操作.同时还给该 Adapter 绑定了一个 item 的点击事件,且为可选操作,不需要点击操作,直接传null即可.
[代码]java代码:001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118/**
* 基础的Adapter
* Created by zyz on 2016/5/17.
*/
public abstract class BaseAdapter> extends RecyclerView.Adapter {
protected List dataList;
protected OnItemClickListener listener;
/**
* 设置数据,并设置点击回调接口
*
* @param list 数据集合
* @param listener 回调接口
*/
public BaseAdapter(@Nullable List list, @Nullable OnItemClickListener listener) {
this.dataList = list;
if (this.dataList == null) {
this.dataList = new ArrayList<>();
}
this.listener = listener;
}
@Override
public void onBindViewHolder(final H holder, int position) {
holder.setData(dataList.get(position));
if (listener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onItemClick(holder);
}
});
}
}
@Override
public int getItemCount() {
return dataList.size();
}
/**
* 填充数据,此方法会清空以前的数据
*
* @param list 需要显示的数据
*/
public void fillList(List list) {
dataList.clear();
dataList.addAll(list);
}
/**
* 更新数据
*
* @param holder item对应的holder
* @param data item的数据
*/
public void updateItem(H holder, M data) {
dataList.set(holder.getLayoutPosition(), data);
}
/**
* 获取一条数据
*
* @param holder item对应的holder
* @return 该item对应的数据
*/
public M getItem(H holder) {
return dataList.get(holder.getLayoutPosition());
}
/**
* 获取一条数据
*
* @param position item的位置
* @return item对应的数据
*/
public M getItem(int position) {
return dataList.get(position);
}
/**
* 追加一条数据
*
* @param data 追加的数据
*/
public void appendItem(M data) {
dataList.add(data);
}
/**
* 追加一个集合数据
*
* @param list 要追加的数据集合
*/
public void appendList(List list) {
dataList.addAll(list);
}
/**
* 在最顶部前置数据
*
* @param data 要前置的数据
*/
public void preposeItem(M data) {
dataList.add(0, data);
}
/**
* 在顶部前置数据集合
*
* @param list 要前置的数据集合
*/
public void preposeList(List list) {
dataList.addAll(0, list);
}
}
使用范例
使用范例为一种Item和多种Item这两种类型.
一种Item
运行结果如下图所示:
单个Item类型的ViewHolder如下:
[代码]java代码:01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22/**
* 一种View的Holder
* Created by zyz on 2016/5/17.
*/
public class SingleHolder extends BaseHolder {
TextView nameView;
TextView ageView;
public SingleHolder(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
nameView = getView(R.id.name_tv);
ageView = getView(R.id.age_tv);
}
@Override
public void setData(Person data) {
nameView.setText(data.getName());
ageView.setText(String.valueOf(data.getAge()));
}
}
与之对应的Adapter如下:
[代码]java代码:01
02
03
04
05
06
07
08
09
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/**
* 一种item的Adapter
* Created by zyz on 2016/5/17.
*/
public class SingleAdapter extends BaseAdapter {
public SingleAdapter(SingleItemClickListener listener) {
super(null, listener);
}
@Override
public SingleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new SingleHolder(parent, R.layout.item_single);
}
@Override
public void onBindViewHolder(final SingleHolder holder, int position) {
super.onBindViewHolder(holder, position);
holder.nameView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((SingleItemClickListener) listener).onNameClick(getItem(holder).getName());
}
});
holder.ageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((SingleItemClickListener) listener).onAgeClick(getItem(holder).getAge());
}
});
}
public interface SingleItemClickListener extends OnItemClickListener {
void onNameClick(String name);
void onAgeClick(int age);
}
}
多种Item
运行结果如下图所示:
多个Item的ViewHolder的写法,可以根据Item的View重合度来写:
1. 如果多个item完全没有相同的部分,则单独继承ViewHolder
2. 如果Item之间有相同的部分,可以抽出来一个父类来继承ViewHolder
这里的范例Item是具有重合部分的.模型来自聊天界面.
[代码]java代码:1
2
3
4
5
6
7
8
9Holder部分如下:
|-ChatHolder //聊天View的Holder,包含公共部分
|-TextHolder //文字消息的Holder,包含文字特有的部分
|-ImageHolder //图片消息的Holder,包含图片特有的部分.
数据部分如下:
|-ChatMsg //代表一条聊天消息
|-TextMsg //代表一条文字消息
|-ImageMsg //代表一条图片消息
ChatHolder代码如下,包含发送者的名称和时间:
[代码]java代码:01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22/**
* 聊天界面的ViewHolder
* Created by zyz on 2016/5/18.
*/
public class ChatHolder extends BaseHolder {
TextView senderNameTv;
TextView createTimeTv;
public ChatHolder(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
senderNameTv = getView(R.id.name_tv);
createTimeTv = getView(R.id.create_time_tv);
}
@Override
public void setData(ChatMsg data) {
senderNameTv.setText(data.getSenderName());
createTimeTv.setText(data.getCreateTime());
}
}
TextHolder的代码如下,包含文本显示的View
[代码]java代码:01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20/**
* 文本消息的Holder
* Created by zyz on 2016/5/18.
*/
public class TextHolder extends ChatHolder {
TextView contentTv;
public TextHolder(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
contentTv = getView(R.id.content_tv);
}
@Override
public void setData(ChatMsg data) {
super.setData(data);
contentTv.setText(((TextMsg)data).getText());
}
}
其中的setData()方法默认调用父类的方法,可以直接设置发送者的名称和时间.
ImageHolder的代码如下,包含显示图片的View
[代码]java代码:01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19/**
* 表情消息的Holder
* Created by zyz on 2016/5/18.
*/
public class ImageHolder extends ChatHolder {
ImageView contentIv;
public ImageHolder(ViewGroup parent, @LayoutRes int resId) {
super(parent, resId);
contentIv = getView(R.id.content_iv);
}
@Override
public void setData(ChatMsg data) {
super.setData(data);
contentIv.setImageResource(((ImageMsg)data).getResId());
}
}
最后是我们的Adapter,代码不多.
```java
/**
· 聊天界面的Adapter
· Created by zyz on 2016/5/18.
*/
public class ChatAdapter extends BaseAdapter {
private static final int VIEW_TEXT = 0;
private static final int VIEW_IMAGE = 1;
public ChatAdapter(OnItemClickListener listener) {
super(null, listener);
}
@Override
public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ChatHolder holder;
if (viewType == VIEW_IMAGE) {
holder = new ImageHolder(parent, R.layout.item_msg_img_left);
} else {
holder = new TextHolder(parent, R.layout.item_msg_text_left);
}
return holder;