前一段时间,因为项目需要使用了RecyclerView,为了方便使用还进行封装了,详细参见此处:RecyclerView中Adapter和ViewHolder的封装 - DevWiki。
那样的封装有几个问题:
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的代码,在其中添加了``方法,方便设置分割线的形式。
具体的代码参见此处:DividerDecoration.java
优化后的使用
优化后使用方式有所改变,具体使用方式如下:
[代码]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上,具体地址在此:Dev-Wiki/RecyclerView
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/2508/viewspace-2814823/ ,如需转载,请注明出处,否则将追究法律责任。