前几天我写了一篇分析base-adapter-helper的文章,文章中提到了尝试用和base-adapter-helper相同的api实现RecyclerView适配器。现在已经实现了。
在这期间也看了简书作者轻微 的一篇文章:RecyclerView适配器的超省写法 。发现他的实现原理其实和base-adapter-helper有很大的相似之处,不知道这是英雄所见略同的巧合,还是借鉴了base-adapter-helper,我想还是前者的可能性略大,因为代码相似度很低。
好了,回到简化RecyclerView适配器这个话题上来。
其实总的来说要比ListView实现起来更简单。思路也和ListView版本很相似。就三点:
列表数据要使用泛型;
原本ViewHolder中的View成员变量转而通过view数组来实现(比如SparseArray);
把数据绑定通过实现抽象方法来实现。
我更改了一下base-adapter-helper的项目结构:
如果你想用RecyclerView的适配器,import recyclerview包下面的QuickAdapter。
package com.joanzapata.android.recyclerview; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * Created by jianghejie on 15/8/8. */ public abstract class BaseQuickAdapter <T, H extends BaseAdapterHelper> extends RecyclerView.Adapter<BaseAdapterHelper> implements View.OnClickListener{ protected static final String TAG = BaseQuickAdapter.class.getSimpleName(); protected final Context context; protected final int layoutResId; protected final List<T> data; protected boolean displayIndeterminateProgress = false; private OnItemClickListener mOnItemClickListener = null; //define interface public static interface OnItemClickListener { void onItemClick(View view , int position); } /** * Create a QuickAdapter. * @param context The context. * @param layoutResId The layout resource id of each item. */ public BaseQuickAdapter(Context context, int layoutResId) { this(context, layoutResId, null); } /** * Same as QuickAdapter#QuickAdapter(Context,int) but with * some initialization data. * @param context The context. * @param layoutResId The layout resource id of each item. * @param data A new list is created out of this one to avoid mutable list */ public BaseQuickAdapter(Context context, int layoutResId, List<T> data) { this.data = data == null ? new ArrayList<T>() : data; this.context = context; this.layoutResId = layoutResId; } @Override public int getItemCount() { return data.size(); } public T getItem(int position) { if (position >= data.size()) return null; return data.get(position); } @Override public BaseAdapterHelper onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutResId, viewGroup, false); view.setOnClickListener(this); BaseAdapterHelper vh = new BaseAdapterHelper(view); return vh; } @Override public void onBindViewHolder(BaseAdapterHelper helper, int position) { helper.itemView.setTag(position); T item = getItem(position); convert((H)helper, item); } /** * Implement this method and use the helper to adapt the view to the given item. * @param helper A fully initialized helper. * @param item The item that needs to be displayed. */ protected abstract void convert(H helper, T item); @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.onItemClick(v,(int)v.getTag()); } } public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; } }
在这里BaseAdapterHelper就是一个RecyclerView.ViewHolder,为了一致性,我取名为BaseAdapterHelper。
package com.joanzapata.android.recyclerview; import android.support.v7.widget.RecyclerView; import android.util.SparseArray; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; /** * Created by jianghejie on 15/8/8. */ public class BaseAdapterHelper extends RecyclerView.ViewHolder{ private SparseArray<View> views; public BaseAdapterHelper(View itemView){ super(itemView); this.views = new SparseArray<View>(); } public TextView getTextView(int viewId) { return retrieveView(viewId); } public Button getButton(int viewId) { return retrieveView(viewId); } public ImageView getImageView(int viewId) { return retrieveView(viewId); } public View getView(int viewId) { return retrieveView(viewId); } @SuppressWarnings("unchecked") protected <T extends View> T retrieveView(int viewId) { View view = views.get(viewId); if (view == null) { view = itemView.findViewById(viewId); views.put(viewId, view); } return (T) view; } }
在前面一片文章中,我们解决base-adapter-helper的缺点是采用抛弃了诸多setXXX方法,直接调用retrieveView,然而感觉那样会多一行代码,看起来不够精炼,在RecyclerView适配器的超省写法 中我学到了一点,那就是将setXXX改成getXXX,在getXXX方法中做好强制转换。
而且一般来说,我们只需要很有限的getXXX方法,基本有这三个就可以满足90%的需求了:
public TextView getTextView(int viewId) { return retrieveView(viewId); } public Button getButton(int viewId) { return retrieveView(viewId); } public ImageView getImageView(int viewId) { return retrieveView(viewId); }
但是有些时候也许我们并不需要知道view的具体类型,比如在设置透明度,可见状态的时候,所以最好还是增加一个获得普通view的方法:
public TextView getTextView(int viewId) { return retrieveView(viewId); } public Button getButton(int viewId) { return retrieveView(viewId); } public ImageView getImageView(int viewId) { return retrieveView(viewId); } public View getView(int viewId) { return retrieveView(viewId); }
最后,需要提醒大家一点,就是这种实现方式和传统的RecyclerView.Adapter在流程上稍微有一点点不同。
传统RecyclerView的ViewHolder在onCreateViewHolder的时候就已经找到了所有要绑定的view,如下面的ViewHolder
public static class ViewHolder extends RecyclerView.ViewHolder { public TextView title; public TextView author; public TextView description; public TextView date; public TextView count; public ImageView litPic; public ViewHolder(View view){ super(view); title = (TextView) view .findViewById(R.id.blog_listitem_title); author = (TextView) view .findViewById(R.id.blog_listitem_author); date = (TextView) view .findViewById(R.id.blog_listitem_date); litPic = (ImageView) view .findViewById(R.id.blog_listitem_litpic); description = (TextView) view .findViewById(R.id.description); } }
这些View都是成员变量,在调用构造函数的时候已经初始化了。
而我们的实现中,这些View是在绑定的时候被初始化的,我们必须这样做,因为我们无法预知绑定的item的布局以及数据模型是怎样的。
不过这并不会带来丝毫的性能损失,我们做的只是把findViewById放在了onBindViewHolder中,并且只有第一次绑定的时候才会去执行findViewById。
mRecyclerView = (RecyclerView) mPageView.findViewById(R.id.my_recycler_view); mRecyclerView.enableLoadmore(); LinearLayoutManager layoutManager = new LinearLayoutManager(appContext); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.scrollToPosition(0); mRecyclerView.setLayoutManager(layoutManager); //init the listview mAdapter = new QuickAdapter<Blog>(appContext, R.layout.item_blog_list, blogData){ @Override protected void convert(BaseAdapterHelper helper, Blog blog) { helper.getTextView(R.id.blog_listitem_title).setText(Html.fromHtml(blog.getTitle())); helper.getTextView(R.id.blog_listitem_author).setText(blog.getAuthor()); helper.getTextView(R.id.description).setText(blog.getDescription()); Picasso.with(context).load(blog.getImageUrl()).into(helper.getImageView(R.id.blog_listitem_litpic)); } } }; mRecyclerView.setAdapter(mAdapter);
是不是和ListView版本非常一致。
现在只提供网盘的下载地址,完善之后再提交到github。
http://pan.baidu.com/s/1kT9wBHH