RecyclerView.Adapter通用基类

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。

如有不正,还请指摘。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值