RecyclerView.Adapter通用基类
一般来说,我们在使用RecyclerView的时候,需要自定义一个Adapter,用来适配RecyclerView和data。这里主要来编写这个Adapter的基类,我们把它命名为RecyclerAdapter。下面来分析一下这个基类。
public abstract class RecyclerViewAdapter<Data> extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> implements View.OnClickListener, View.OnLongClickListener{
private static final String TAG = RecyclerViewAdapter.class.getSimpleName();
private List<Data> mList;
private OnItemClickListener mOnItemClickListener;
public RecyclerViewAdapter(List<Data> mList) {
this.mList = mList;
}
public void setmOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder() viewType = " + viewType);
// viewType from getItemViewType(). Different position, we can use different layout (This
// viewType actually is layout id, such as R.layout.recycler_view_item).
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
RecyclerViewHolder holder = onCreateViewHolder(view, viewType);
view.setTag(R.id.recycler_view_tag, holder);
ButterKnife.bind(holder, view);
view.setOnClickListener(this);
view.setOnLongClickListener(this);
return holder;
}
/**
* Declare this method for sub class to construct different holder by viewType.
* @param root The root view to construct holder.
* @param viewType Construct different holder by viewType.
* @return
*/
protected abstract RecyclerViewHolder onCreateViewHolder(View root, int viewType);
@Override
public int getItemViewType(int position) {
if (null != mList && null != mList.get(position)) {
int viewType = getItemViewType(position, mList.get(position));
Log.d(TAG, "getItemViewType() viewType = " + viewType);
return viewType;
}
return super.getItemViewType(position);
}
/**
* For sub class, sub class can return own layout xml file.
* Overload this method from super class, for different data, we can use different layout.
* @param position In some special case(such as position 0),
* we need position to decide which layout we should to use.
* @param data With different data, use different layout.
* @return
*/
protected abstract int getItemViewType(int position, Data data);
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder,
int position) {
if (null != mList && null != mList.get(position)) {
Data data = mList.get(position);
holder.onBind(data);
}
}
@Override
public int getItemCount() {
if (null != mList) {
return mList.size();
}
return 0;
}
@Override
public void onClick(View v) {
RecyclerViewHolder holder = (RecyclerViewHolder) v.getTag(R.id.recycler_view_tag);
if (null == holder || null == mList || null == mOnItemClickListener){
return;
}
int position = holder.getAdapterPosition();
if (null == mList.get(position)) {
return;
}
mOnItemClickListener.onItemClick(position, mList.get(position));
}
@Override
public boolean onLongClick(View v) {
RecyclerViewHolder holder = (RecyclerViewHolder) v.getTag(R.id.recycler_view_tag);
if (null == holder || null == mList || null == mOnItemClickListener){
return false;
}
int position = holder.getAdapterPosition();
if (null == mList.get(position)) {
return false;
}
mOnItemClickListener.onItemLongClick(position, mList.get(position));
return true;
}
public static abstract class RecyclerViewHolder<Data> extends RecyclerView.ViewHolder {
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
}
/**
* Exposed data to sub class.
* @param data The data at the position.
*/
protected abstract void onBind(Data data);
}
public interface OnItemClickListener<Data> {
void onItemClick(int position, Data data);
void onItemLongClick(int position, Data data);
}
}
我们主要分析以下4个部分。
onCreateViewHolder()
正常来说,我们在onCreateViewHolder中所作的无非是创建一个使用布局文件加载出view,然后使用这个view来创建一个ViewHolder对象返回。但是这里出现了一些不同。
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
RecyclerViewHolder holder = onCreateViewHolder(view, viewType);
我们使用了viewType而不是一个常规的类似于R.layout.recycler_item的参数作为viewType,平时我们是不会使用这个参数的,但是作为Adapter的基类,我们不可能确定子类会使用什么layout来加载view,所以这里使用了viewType这个参数加载view,那么你可能又要问了:你这个viewType就能确定子类的layout了?答案是:当然可以,只不过使用了一点小手段:getItemViewType()方法,这个方法的具体作用我在下一个部分写出。
我们继续看这个onCreateViewHolder()方法中,如何创建ViewHolder对象的。这里使用了onCreateViewHolder()方法,请注意,这个方法是:
protected abstract RecyclerViewHolder onCreateViewHolder(View root, int viewType);
很显然,这个方法也是需要子类去重写的,因为创建的ViewHolder也不能是基类的RecyclerViewHolder,而得是Adapter子类中的内部类ViewHolder,这个内部类ViewHolder继承了这个abstract类,这个abstract类是定义在基类Adapter中的。
public static abstract class RecyclerViewHolder<Data> extends RecyclerView.ViewHolder
getItemViewType()
@Override
public int getItemViewType(int position) {
if (null != mList && null != mList.get(position)) {
int viewType = getItemViewType(position, mList.get(position));
Log.d(TAG, "getItemViewType() viewType = " + viewType);
return viewType;
}
return super.getItemViewType(position);
}
我们可以看到,这个方法是@Override的,我们观察父类中这个方法的注释(RecyclerView.Adapter中的getItemViewType()方法)
- /* Return the view type of the item at position for the purposes of view recycling. */
返回在position位置子项的视图类型,用于视图的回收。但是这个方法中明确写返回0,这是什么意思呢?
其实这里的视图类型还是我们平时使用的布局文件的id,我们在这里定义了一个 abstract 方法:
protected abstract int getItemViewType(int position, Data data);
如果这个position处数据不为空,那么我们可以调用这个abstract方法,使用它的返回值来作为自己的返回值,而这个abstract方法的具体实现一定是写在Adapter的子类中的,这么一来,具体的布局还是在子类中写入的。
onBindViewHolder()
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder,
int position) {
if (null != mList && null != mList.get(position)) {
Data data = mList.get(position);
holder.onBind(data);
}
}
我们可以看出,这里的bind view holder操作不像常规的获取具体数据,并通过holder对象进行item中view的赋值,而是调用了holder的onBind方法,将data传递进去。这里的onBind方法,其实是在RecyclerViewHolder类中定义的abstract方法,onBind()方法的具体实现仍然交给子类viewHolder去实现。我们可以继续往下看,RecyclerViewHolder类。
class RecyclerViewHolder
public static abstract class RecyclerViewHolder<Data> extends RecyclerView.ViewHolder {
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
}
/**
* Exposed data to sub class.
* @param data The data at the position.
*/
protected abstract void onBind(Data data);
}
这个ViewHolder是一个抽象类,其中主要定义了一个抽象方法, onBind()方法,通过这个方法,传入具体的数据,将bind view 的具体操作交给子类去实现。
Demo
public class PicRecyclerViewAdapter extends RecyclerViewAdapter<Drawable> {
private static final String TAG = PicRecyclerViewAdapter.class.getSimpleName();
public PicRecyclerViewAdapter(List<Drawable> mList) {
super(mList);
}
@Override
protected RecyclerViewHolder onCreateViewHolder(View root, int viewType) {
return new PicViewHolder(root);
}
@Override
protected int getItemViewType(int position, Drawable drawable) {
Log.d(TAG, "getItemViewType() " + R.layout.recycler_view_item);
return R.layout.recycler_view_item;
}
class PicViewHolder extends RecyclerViewHolder<Drawable> {
@BindView(R.id.item_pic)
ImageView mImageView;
public PicViewHolder(@NonNull View itemView) {
super(itemView);
}
@Override
protected void onBind(Drawable drawable) {
mImageView.setImageDrawable(drawable);
}
}
}
我们编写了一个简单的PicRecyclerViewAdapter,继承了RecyclerViewAdapter类,以供参照。
另外
- 这次所编写的代码中使用了ButterKnife工具,如果不想了解可以跳过。
- 基类Adapter中使用了泛型参数,具体子类实现中可以传入这个泛型参数,作为每一个item的数据类。
- 使用这个基类后,一个RecyclerView可以使用不同的viewHolder,可以在特定的position处使用不同的layout和viewHolder来实现不一样的RecyclerView。
如有不正,还请指摘。