Android RecyclerView的ViewHolder和Ada
前一段時間,因為專案需要使用了RecyclerView,為了方便使用還進行封裝了,詳細參見此處:
那樣的封裝有幾個問題:
1. ViewHolder的setData(M data)雖然方便設定資料,但是ViewHolder需要知曉資料型別。ViewHolder應該只用作View的快取,而不應該知曉填充View的資料。
2. BaseAdapter無法新增Header和Footer。
3. 點選事件耦合性較高。
基於以上幾點,對BaseHolder和BaseAdapter進行修改最佳化。
BaseHolder的最佳化
相對於舊版的BaseHolder:
1. 新版的去除的資料型別的注入,使ViewHolder只用來快取View。
2. 新增SparseArray,使之來快取View。
3. 新增BaseHolder(View view)構造器,外部更方便控制View。
4. 保留getContext()方法,方便獲取Context物件。
新版的BaseHolder程式碼如下:
[程式碼]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 41 42 43 44 45 46 47 48 49 50 51 |
public class BaseHolder extends RecyclerView.ViewHolder {
private SparseArray
public BaseHolder(ViewGroup parent, @LayoutRes int resId) { super(LayoutInflater.from(parent.getContext()).inflate(resId, parent, false)); viewArray = new SparseArray(); }
public BaseHolder(View view) { super(view); viewArray = new SparseArray(); }
protected View view = viewArray.get(viewId); if (view == null) { view = itemView.findViewById(viewId); viewArray.put(viewId, view); } return (T) view; }
protected Context getContext() { return itemView.getContext(); } } |
Adapter部分的最佳化
Adapter拆分為兩個抽象類:AbsAdapter與BaseAdapter,其中:
AbsAdapter:封裝了和ViewHolder和HeaderView,FooterView相關的方法。
BaseAdapter:繼承AbsAdapter,封裝了資料相關的方法。
各自聚焦於不同的方面,方面日後擴充套件。
AbsAdapter的程式碼如下:
[程式碼]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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
public abstract class AbsAdapter
private static final String TAG = "AbsAdapter";
public static final int VIEW_TYPE_HEADER = 1024; public static final int VIEW_TYPE_FOOTER = 1025;
protected View headerView; protected View footerView;
protected Context context;
public AbsAdapter(Context context) { this.context = context; }
@Override public final BaseHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_HEADER) { return new BaseHolder(headerView); } else if (viewType == VIEW_TYPE_FOOTER) { return new BaseHolder(footerView); } else { return createCustomViewHolder(parent, viewType); } }
public abstract VH createCustomViewHolder(ViewGroup parent, int viewType);
@Override public final void onBindViewHolder(BaseHolder holder, int position) { switch (holder.getItemViewType()) { case VIEW_TYPE_HEADER: case VIEW_TYPE_FOOTER: break; default: bindCustomViewHolder((VH) holder, position); break; } }
public abstract void bindCustomViewHolder(VH holder, int position);
public void addHeaderView(View headerView) { if (headerView == null) { Log.w(TAG, "add the header view is null"); return ; } this.headerView = headerView; notifyDataSetChanged(); }
public void removeHeaderView() { if (headerView != null) { headerView = null; notifyDataSetChanged(); } }
public void addFooterView(View footerView) { if (footerView == null) { Log.w(TAG, "add the footer view is null"); return; } this.footerView = footerView; notifyDataSetChanged(); }
public void removeFooterView() { if (footerView != null) { footerView = null; notifyDataSetChanged(); } }
public int getExtraViewCount() { int extraViewCount = 0; if (headerView != null) { extraViewCount++; } if (footerView != null) { extraViewCount++; } return extraViewCount; }
public int getHeaderExtraViewCount() { return headerView == null ? 0 : 1; }
public int getFooterExtraViewCount() { return footerView == null ? 0 : 1; }
@Override public abstract long getItemId(int position); },> |
BaseAdapter的程式碼如下:
[程式碼]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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
public abstract class BaseAdapter
private List
public BaseAdapter(Context context) { super(context); this.dataList = new ArrayList(); }
public BaseAdapter(Context context, List super(context); this.dataList = new ArrayList(); this.dataList.addAll(list); }
public boolean fillList(List dataList.clear(); boolean result = dataList.addAll(list); if (result) { notifyDataSetChanged(); } return result; }
public boolean appendItem(M data) { boolean result = dataList.add(data); if (result) { if (getHeaderExtraViewCount() == 0) { notifyItemInserted(dataList.size() - 1); } else { notifyItemInserted(dataList.size()); } } return result; }
public boolean appendList(List boolean result = dataList.addAll(list); if (result) { notifyDataSetChanged(); } return result; }
public void proposeItem(M data) { dataList.add(0, data); if (getHeaderExtraViewCount() == 0) { notifyItemInserted(0); } else { notifyItemInserted(getHeaderExtraViewCount()); } }
public void proposeList(List dataList.addAll(0, list); notifyDataSetChanged(); }
@Override public long getItemId(int position) { return position; }
@Override public final int getItemViewType(int position) { if (headerView != null && position == 0) { return VIEW_TYPE_HEADER; } else if (footerView != null && position == dataList.size() + getHeaderExtraViewCount()) { return VIEW_TYPE_FOOTER; } else { return getCustomViewType(position); } }
public abstract int getCustomViewType(int position);
@Override public int getItemCount() { return dataList.size() + getExtraViewCount(); }
public M getItem(int position) { if (headerView != null && position == 0 || position >= dataList.size() + getHeaderExtraViewCount()) { return null; } return headerView == null ? dataList.get(position) : dataList.get(position - 1); }
public M getItem(VH holder) { return getItem(holder.getAdapterPosition()); }
public void updateItem(M data) { int index = dataList.indexOf(data); if (index return; } dataList.set(index, data); if (headerView == null) { notifyItemChanged(index); } else { notifyItemChanged(index + 1); } }
public void removeItem(int position) { if (headerView == null) { dataList.remove(position); } else { dataList.remove(position - 1); } notifyItemRemoved(position); }
public void removeItem(M data) { int index = dataList.indexOf(data); if (index return; } dataList.remove(index); if (headerView == null) { notifyItemRemoved(index); } else { notifyItemRemoved(index + 1); } } },>,> |
點選事件的最佳化
為了降低點選事件的耦合性,將點選事件從Adapter中移除,使用另外一種方式來實現。使用程式碼如下:
[程式碼]java程式碼:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 |
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(recyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Person person = singleAdapter.getItem(position); Toast.makeText(SingleActivity.this, "click:" + person, Toast.LENGTH_SHORT).show(); }
@Override public void onItemLongClick(View view, int position) { Person person = singleAdapter.getItem(position); Toast.makeText(SingleActivity.this, "Long Click:" + person, Toast.LENGTH_SHORT).show(); } })); |
使用RecyclerView的自帶的方法addOnItemTouchListener()實現點選和長點選事件。實現程式碼如下:
[程式碼]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 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 |
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener clickListener; private GestureDetector gestureDetector;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position); }
public RecyclerItemClickListener(final RecyclerView recyclerView, OnItemClickListener listener) { this.clickListener = listener; gestureDetector = new GestureDetector(recyclerView.getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; }
@Override public void onLongPress(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (childView != null && clickListener != null) { clickListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView)); } } }); }
@Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View childView = rv.findChildViewUnder(e.getX(), e.getY()); if (childView != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onItemClick(childView, rv.getChildAdapterPosition(childView)); return true; } return false; }
@Override public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
} } |
分割線的最佳化
RecyclerView的分割線不像ListView那樣方便,但是RecyclerView也提供了``方法來新增分割線。
分割線程式碼使用的是SDK中的Sample的程式碼,在其中新增了``方法,方便設定分割線的形式。
具體的程式碼參見此處:
最佳化後的使用
最佳化後使用方式有所改變,具體使用方式如下:
[程式碼]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 |
recyclerView = (RecyclerView) findViewById(R.id.single_rv); layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); DividerDecoration decoration = new DividerDecoration(this, DividerDecoration.VERTICAL_LIST); Drawable drawable = getResources().getDrawable(R.drawable.divider_single); decoration.setDivider(drawable); recyclerView.addItemDecoration(decoration); recyclerView.setAdapter(singleAdapter);
View view = LayoutInflater.from(this).inflate(R.layout.item_single_header, null, false); singleAdapter.addHeaderView(view);
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(recyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Person person = singleAdapter.getItem(position); Toast.makeText(SingleActivity.this, "click:" + person, Toast.LENGTH_SHORT).show(); }
@Override public void onItemLongClick(View view, int position) { Person person = singleAdapter.getItem(position); Toast.makeText(SingleActivity.this, "Long Click:" + person, Toast.LENGTH_SHORT).show(); } })); |
以上所有的程式碼均在GitHub上,具體地址在此:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2508/viewspace-2814823/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 你還在用Adapter和ViewHolder寫RecyclerView嗎?Out了!APTView
- android:ViewHolder模式AndroidView模式
- Android ViewHolder模式AndroidView模式
- 一勞永逸——RecyclerView無型別強轉的通用ViewHolderView型別
- Android中的RecyclerViewAndroidView
- Android RecyclerView的使用AndroidView
- Android RecyclerView中Adapter和ViewHoAndroidViewAPT
- Android 給RecyclerView新增頭部和尾部AndroidView
- Android:打造“萬能”Adapter與ViewHolderAndroidAPTView
- (轉) Android 優雅的為RecyclerView新增HeaderView和FooterViewAndroidViewHeader
- Android RecyclerView詳解AndroidView
- 在Android與recyclerview中使用列表和網格AndroidView
- 教你玩轉 Android RecyclerView:深入解析 RecyclerView ItemDecoration類AndroidView
- Android RecyclerView的簡便寫法AndroidView
- Android中的RecyclerView: 基礎知識AndroidView
- Android中利用ViewHolder優化自定義Adapter的典型寫法AndroidView優化APT
- Android開發之平板和橫豎屏適配-RecyclerViewAndroidView
- 使用模型驅動的方法和 UML 2.0 及 Ada 2005 介面來開發更靈活的 Ada 應用程式模型
- Android TV開發——RecyclerView For TVAndroidView
- Android RecyclerView 通用介面卡的實現AndroidView
- 安卓5.1原始碼解析 : RecyclerView解析從繪製流程,ViewHolder複用機制,LayoutManger,ItemAnimator等流程全面講解安卓原始碼View
- 【Android進階】RecyclerView之ItemDecoration(一)AndroidView
- Android RecyclerView 簡介與例項AndroidView
- Android開發 - RecyclerView 類詳解AndroidView
- 值得收藏的 ViewHolder 工具類實現View
- RecyclerView的Adapter中attach和detach探索ViewAPT
- 【Android進階】RecyclerView之快取(二)AndroidView快取
- Android入門教程 | RecyclerView使用入門AndroidView
- Android入門教程 | RecyclerView實際使用AndroidView
- Android 基於RecyclerView實現批量操作AndroidView
- Android使用RecyclerView實現二級列表AndroidView
- Android TV端RecyclerView焦點亂跑AndroidView
- Android 時間軸的實現(RecyclerView更簡單)AndroidView
- android利用recyclerview展示帶有日期的圖片列表AndroidView
- Android實現帶動畫的下拉重新整理RecyclerViewAndroid動畫View
- RecyclerView的使用View
- 說說在 Android 的 RecyclerView 中如何實現下拉刷AndroidView
- Android RecyclerView 區域性重新整理原理AndroidView