前言
昨天开始接触江湖口碑很好的RecyclerView,事实上,我已经被她的强大所征服了!资源回收,数据绑定,布局显示,分割线,Item动画多个模块高度解耦,灵活优雅。其实,RecyclerView在使用上已经是相当简单了(个人觉得),但仍有很多代码是可以加以封装的。今天受简书上一篇博文的启发,作为写代码喜欢优(tou)雅(lan)的人,想到了一种封装方式,打造万能适配器,供大家食用。
正统模式:
<span style="font-size:12px;"><code class="hljs axapta has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimplerItemAdapter</span> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">Adapter</span><<span class="hljs-title">SimplerItemAdapter</span>.<span class="hljs-title">SimpleItemViewHolder</span> > {</span> <span class="hljs-keyword">private</span> List <String> items; <span class="hljs-keyword">public</span> SimplerItemAdapter (@NonNull List<String> dateItems ) { <span class="hljs-keyword">this</span>.items = (dateItems != <span class="hljs-keyword">null</span> ? dateItems : <span class="hljs-keyword">new</span> ArrayList<String>()); } @Override <span class="hljs-keyword">public</span> SimpleItemViewHolder onCreateViewHolder (ViewGroup viewGroup, <span class="hljs-keyword">int</span> viewType) { View itemView = LayoutInflater.from( viewGroup.getContext ()).inflate(R.layout .item, viewGroup, <span class="hljs-keyword">false</span> ); <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SimpleItemViewHolder(itemView); } @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> onBindViewHolder (SimpleItemViewHolder viewHolder, <span class="hljs-keyword">int</span> position) { viewHolder.textView .setText(items.get (position)); } @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> getItemCount () { <span class="hljs-keyword">return</span> (<span class="hljs-keyword">this</span>.items != <span class="hljs-keyword">null</span>) ? <span class="hljs-keyword">this</span> .items. size() : <span class="hljs-number">0</span> ; } <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleItemViewHolder</span> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">ViewHolder</span> {</span> <span class="hljs-keyword">protected</span> TextView textView ; <span class="hljs-keyword">public</span> SimpleItemViewHolder (View itemView) { <span class="hljs-keyword">super</span>(itemView); <span class="hljs-keyword">this</span>.textView = (TextView) itemView.findViewById (R. id.text); } } } </code></span>
- 首先,
@Override public int getItemCount () {
这段代码完全可以封装起来的。
return (this.items != null) ? this .items. size() : 0 ;
} - onCreatedViewHolder()方法作用是绑定item视图,可以进一步封装,给子类提供一个getLayoutItemId的抽象方法,这样就可以简化成一行代码了。
- 因此我们发现,这个adapter的核心代码在与onBindViewHolder()中,作用是将数据跟视图(ViewHolder)绑定,可以给子类提供一个bindData()抽象方法。
- 当然了,使用泛型也是极好的,拓广了adapter的使用范围。
- 添加点击事件的监听也可以封装到万能adapter中,子类就不用再写item点击事件处理代码了
封装后的Adapter
<span style="font-size:12px;"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> { <span class="hljs-keyword">protected</span> final List<T> mData; <span class="hljs-keyword">protected</span> final Context mContext; <span class="hljs-keyword">protected</span> LayoutInflater mInflater; <span class="hljs-keyword">private</span> OnItemClickListener mClickListener; <span class="hljs-keyword">private</span> OnItemLongClickListener mLongClickListener; <span class="hljs-keyword">public</span> <span class="hljs-title">BaseRecyclerAdapter</span>(Context ctx, List<T> list) { mData = (list != <span class="hljs-keyword">null</span>) ? list : <span class="hljs-keyword">new</span> ArrayList<T>(); mContext = ctx; mInflater = LayoutInflater.<span class="hljs-keyword">from</span>(ctx); } @Override <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">onCreateViewHolder</span>(ViewGroup parent, <span class="hljs-keyword">int</span> viewType) { final RecyclerViewHolder holder = <span class="hljs-keyword">new</span> RecyclerViewHolder(mContext, mInflater.inflate(getItemLayoutId(viewType), parent, <span class="hljs-keyword">false</span>)); <span class="hljs-keyword">if</span> (mClickListener != <span class="hljs-keyword">null</span>) { holder.itemView.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() { @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) { mClickListener.onItemClick(holder.itemView, holder.getLayoutPosition()); } }); } <span class="hljs-keyword">if</span> (mLongClickListener != <span class="hljs-keyword">null</span>) { holder.itemView.setOnLongClickListener(<span class="hljs-keyword">new</span> View.OnLongClickListener() { @Override <span class="hljs-keyword">public</span> boolean <span class="hljs-title">onLongClick</span>(View v) { mLongClickListener.onItemLongClick(holder.itemView, holder.getLayoutPosition()); <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>; } }); } <span class="hljs-keyword">return</span> holder; } @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBindViewHolder</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position) { bindData(holder, position, mData.<span class="hljs-keyword">get</span>(position)); } @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemCount</span>() { <span class="hljs-keyword">return</span> mData.size(); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span>(<span class="hljs-keyword">int</span> pos, T item) { mData.add(pos, item); notifyItemInserted(pos); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span>(<span class="hljs-keyword">int</span> pos) { mData.remove(pos); notifyItemRemoved(pos); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnItemClickListener</span>(OnItemClickListener listener) { mClickListener = listener; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnItemLongClickListener</span>(OnItemLongClickListener listener) { mLongClickListener = listener; } <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType); <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position, T item); <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> OnItemClickListener { <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemClick</span>(View itemView, <span class="hljs-keyword">int</span> pos); } <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> OnItemLongClickListener { <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemLongClick</span>(View itemView, <span class="hljs-keyword">int</span> pos); } }</code></span>
Super ViewHolder!
其实,这还没完呢!重头戏在ViewHolder上!RecyclerView强制我们使用ViewHolder模式,然而缺不可避免地要写findViewById代码,有没有办法不写这样的代码呢?甚至连ViewHolder都不写呢?当然可以!
<span style="font-size:12px;"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecyclerViewHolder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">ViewHolder</span> {</span> <span class="hljs-keyword">private</span> SparseArray<View> mViews;<span class="hljs-comment">//集合类,layout里包含的View,以view的id作为key,value是view对象</span> <span class="hljs-keyword">private</span> Context mContext;<span class="hljs-comment">//上下文对象</span> <span class="hljs-keyword">public</span> <span class="hljs-title">RecyclerViewHolder</span>(Context ctx, View itemView) { <span class="hljs-keyword">super</span>(itemView); mContext = ctx; mViews = <span class="hljs-keyword">new</span> SparseArray<View>(); } <span class="hljs-keyword">private</span> <T extends View> T <span class="hljs-title">findViewById</span>(<span class="hljs-keyword">int</span> viewId) { View view = mViews.get(viewId); <span class="hljs-keyword">if</span> (view == <span class="hljs-keyword">null</span>) { view = itemView.findViewById(viewId); mViews.put(viewId, view); } <span class="hljs-keyword">return</span> (T) view; } <span class="hljs-keyword">public</span> View <span class="hljs-title">getView</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> findViewById(viewId); } <span class="hljs-keyword">public</span> TextView <span class="hljs-title">getTextView</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> (TextView) getView(viewId); } <span class="hljs-keyword">public</span> Button <span class="hljs-title">getButton</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> (Button) getView(viewId); } <span class="hljs-keyword">public</span> ImageView <span class="hljs-title">getImageView</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> (ImageView) getView(viewId); } <span class="hljs-keyword">public</span> ImageButton <span class="hljs-title">getImageButton</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> (ImageButton) getView(viewId); } <span class="hljs-keyword">public</span> EditText <span class="hljs-title">getEditText</span>(<span class="hljs-keyword">int</span> viewId) { <span class="hljs-keyword">return</span> (EditText) getView(viewId); } <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setText</span>(<span class="hljs-keyword">int</span> viewId, String value) { TextView view = findViewById(viewId); view.setText(value); <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>; } <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setBackground</span>(<span class="hljs-keyword">int</span> viewId, <span class="hljs-keyword">int</span> resId) { View view = findViewById(viewId); view.setBackgroundResource(resId); <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>; } <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setClickListener</span>(<span class="hljs-keyword">int</span> viewId, View.OnClickListener listener) { View view = findViewById(viewId); view.setOnClickListener(listener); <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>; } }</code></span>
该类的核心方法是private T findViewById(int viewId),核心成员变量是private SparseArray mViews; 不信可以不写一句ViewHolder代码?接下来看看用法。
实践用法
添加Adapter仅需短短的几行代码:
<span style="font-size:12px;"><code class="hljs java has-numbering">Adapter = <span class="hljs-keyword">new</span> BaseRecyclerAdapter<String>(<span class="hljs-keyword">this</span>,mDataList) { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType) { <span class="hljs-keyword">return</span> R.layout.item; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position,String item) { <span class="hljs-comment">//调用holder.getView(),getXXX()方法根据id得到控件实例,进行数据绑定即可</span> holder.setText(R.id.tv_num,item) .getTextView(R.id.tv_title,item).setText(item); } };</code></span>
完整代码:
<span style="font-size:12px;"><code class="hljs java has-numbering"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span>() { recyclerView = (RecyclerView) findViewById(R.id.recyclerView); mDataList = <span class="hljs-keyword">new</span> ArrayList<>(); <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i <= <span class="hljs-number">100</span>; i++) { mDataList.add(String.valueOf(i)); } <span class="hljs-comment">//设置item动画</span> recyclerView.setItemAnimator(<span class="hljs-keyword">new</span> DefaultItemAnimator()); mAdapter = <span class="hljs-keyword">new</span> BaseRecyclerAdapter<String>(<span class="hljs-keyword">this</span>,mDataList) { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType) { <span class="hljs-keyword">return</span> R.layout.item; } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position,String item) { <span class="hljs-comment">//调用holder.getView(),getXXX()方法根据id得到控件实例,进行数据绑定即可</span> holder.setText(R.id.tv_num,item) .getTextView(R.id.tv_title,item).setText(item); } }; recyclerView.setAdapter(mAdapter); <span class="hljs-comment">//添加item点击事件监听</span> ((BaseRecyclerAdapter)mAdapter).setOnItemClickListener(<span class="hljs-keyword">new</span> BaseRecyclerAdapter.OnItemClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemClick</span>(View itemView, <span class="hljs-keyword">int</span> pos) { Toast.makeText(AdapterTestActivity.<span class="hljs-keyword">this</span>, <span class="hljs-string">"click "</span> + pos, Toast.LENGTH_SHORT).show(); } }); ((BaseRecyclerAdapter)mAdapter).setOnItemLongClickListener(<span class="hljs-keyword">new</span> BaseRecyclerAdapter.OnItemLongClickListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemLongClick</span>(View itemView, <span class="hljs-keyword">int</span> pos) { Toast.makeText(AdapterTestActivity.<span class="hljs-keyword">this</span>, <span class="hljs-string">"long click "</span> + pos, Toast.LENGTH_SHORT).show(); } }); <span class="hljs-comment">//设置布局样式LayoutManager</span> recyclerView.setLayoutManager(<span class="hljs-keyword">new</span> LinearLayoutManager(AdapterTestActivity.<span class="hljs-keyword">this</span>, LinearLayoutManager.VERTICAL, <span class="hljs-keyword">false</span>)); <span class="hljs-comment">// recyclerView.addItemDecoration(new ItemDividerDecoration(MainActivity.this, OrientationHelper.VERTICAL));</span> }</code></span>
如果觉得有什么不妥之处或建议,敬请指教!
完整项目代码已上传至Github。—Github跳转。
see also:
Listview的Adapter应该这样写