最近項目我在項目中使用了RecyclerView
代替了ListView
.因爲項目中有多出列表項使用RecyclerView
,這就致使須要寫多個Adapter
和ViewHolder
.java
其實,怎麼說呢?就是懶,想少寫代碼,因此想研究一下可否簡化一下.git
封裝分爲Adapter
和ViewHolder
兩部分,以下所示.github
抽象類BaseHolder
繼承RecyclerView.ViewHolder
,並依賴注入的數據類型M
,即和ViewHolder
綁定的數據類型爲M.微信
該抽象類包含一個構造方法,用於獲取item對應的佈局.一個抽象函數用於將數據設置到item上面.app
/** * 基礎的ViewHolder * Created by zyz on 2016/5/17. */ public abstract class BaseHolder<M> 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 <T> View的類型 * @return view */ protected <T extends View>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
類也爲抽象類,繼承於RecyclerView.Adapter
,並綁定了兩個泛型:ide
M : 用於該 Adapter 的列表的數據類型,即List<M>
.函數
H : 即和 Adapter 綁定的 Holder 的類型.佈局
而且,該 Adapter 自帶 List 數據集合,聲明時能夠不用傳遞數據集合.也包含了 List 的相關操做.同時還給該 Adapter 綁定了一個 item 的點擊事件,且爲可選操做,不須要點擊操做,直接傳null
便可.this
/** * 基礎的Adapter * Created by zyz on 2016/5/17. */ public abstract class BaseAdapter<M, H extends BaseHolder<M>> extends RecyclerView.Adapter<H> { protected List<M> dataList; protected OnItemClickListener<H> listener; /** * 設置數據,並設置點擊回調接口 * * @param list 數據集合 * @param listener 回調接口 */ public BaseAdapter(@Nullable List<M> list, @Nullable OnItemClickListener<H> 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<M> 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<M> list) { dataList.addAll(list); } /** * 在最頂部前置數據 * * @param data 要前置的數據 */ public void preposeItem(M data) { dataList.add(0, data); } /** * 在頂部前置數據集合 * * @param list 要前置的數據集合 */ public void preposeList(List<M> list) { dataList.addAll(0, list); } }
使用範例爲一種Item和多種Item這兩種類型.spa
運行結果以下圖所示:
單個Item類型的ViewHolder以下:
/** * 一種View的Holder * Created by zyz on 2016/5/17. */ public class SingleHolder extends BaseHolder<Person> { 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<Person, SingleHolder> { 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<SingleHolder> { void onNameClick(String name); void onAgeClick(int age); } }
運行結果以下圖所示:
多個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<ChatMsg> { 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<ChatMsg, ChatHolder> { private static final int VIEW_TEXT = 0; private static final int VIEW_IMAGE = 1; public ChatAdapter(OnItemClickListener<ChatHolder> 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<ChatHolder>() { @Override public void onItemClick(ChatHolder holder) { //處理事件 } });
以上就是對ViewHolder和Adapter的簡易封裝,之後會根據須要繼續封裝簡化.
代碼地址以下:https://github.com/Dev-Wiki/RecyclerView
更多文章請移步個人博客:DevWiki Blog
重要說明
想隨時獲取最新博客文章更新,請關注公共帳號DevWiki,或掃描下面的二維碼: