前情提要
最近项目我在项目中使用了RecyclerView
代替了ListView
.由于项目中有多出列表项使用RecyclerView
,这就导致需要写多个Adapter
和ViewHolder
.
其实,怎么说呢?就是懒,想少写代码,所以想研究一下能否简化一下.
具体实现
封装分为Adapter
和ViewHolder
两部分,如下所示.
ViewHolder
抽象类BaseHolder
继承RecyclerView.ViewHolder
,并依赖注入的数据类型M
,即和ViewHolder
绑定的数据类型为M.
该抽象类包含一个构造方法,用于获取item对应的布局.一个抽象函数用于将数据设置到item上面.
/** * 基础的ViewHolder * Created by zyz on 2016/5/17. */public abstract class BaseHolderextends 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
,并绑定了两个泛型:
M : 用于该 Adapter 的列表的数据类型,即
List<M>
.H : 即和 Adapter 绑定的 Holder 的类型.
并且,该 Adapter 自带 List 数据集合,声明时可以不用传递数据集合.也包含了 List 的相关操作.同时还给该 Adapter 绑定了一个 item 的点击事件,且为可选操作,不需要点击操作,直接传null
即可.
/** * 基础的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如下:
/** * 一种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如下:
/** * 一种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重合度来写:
如果多个item完全没有相同的部分,则单独继承
ViewHolder
如果Item之间有相同的部分,可以抽出来一个父类来继承
ViewHolder
这里的范例Item是具有重合部分的.模型来自聊天界面.
Holder部分如下:|-ChatHolder //聊天View的Holder,包含公共部分 |-TextHolder //文字消息的Holder,包含文字特有的部分 |-ImageHolder //图片消息的Holder,包含图片特有的部分. 数据部分如下:|-ChatMsg //代表一条聊天消息 |-TextMsg //代表一条文字消息 |-ImageMsg //代表一条图片消息
ChatHolder
代码如下,包含发送者的名称和时间:
/** * 聊天界面的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
/** * 文本消息的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
/** * 表情消息的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,代码不多.
/** * 聊天界面的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; } @Override public int getItemViewType(int position) { if (getItem(position).getMsgType() == ChatMsg.TYPE_TEXT) { return VIEW_TEXT; } else { return VIEW_IMAGE; } }}
上述设置的有时间监听,则对应的事件处理在Activity中完成
chatAdapter = new ChatAdapter(new OnItemClickListener() { @Override public void onItemClick(ChatHolder holder) { //处理事件 }});
以上就是对ViewHolder和Adapter的简易封装,以后会根据需要继续封装简化.
代码地址如下:
更多文章请移步我的博客:
重要说明
想随时获取最新博客文章更新,请关注公共账号DevWiki,或扫描下面的二维码: