美文网首页Android学习
RecyclerView 打造通用 Adapter

RecyclerView 打造通用 Adapter

作者: 博弈史密斯 | 来源:发表于2017-07-05 21:49 被阅读68次

打造通用的 Adapter,是为了避免 在有多个 RecycleView 的情况下,需要创建对应个数的 Adapter 类,每个类都要去写 onCreateViewHolder 等大量的重复性的代码。

通用的 ViewHolder

RecyclerView要求必须使用ViewHolder模式,一般我们在使用过程中,都需要去建立一个新的ViewHolder然后作为泛型传入Adapter。那么想要建立通用的Adapter,必须有个通用的ViewHolder。

首先我们确定下ViewHolder的主要的作用,实际上是通过成员变量存储对应的convertView中需要操作的字View,避免每次findViewById,从而提升运行的效率。

那么既然是通用的View,那么对于不同的ItemType肯定没有办法确定创建哪些成员变量View,取而代之的只能是个集合来存储了。

那么代码如下:

/**
 * Created by zhangyb on 2017/7/3.
 */
public class ViewHolder extends RecyclerView.ViewHolder {

    private SparseArray<View> mViews;
    private View mConvertView;
    private Context mContext;

    public ViewHolder(Context context, View itemView) {
        super(itemView);
        mContext = context;
        mConvertView = itemView;
        mViews = new SparseArray<View>();
    }

    public static ViewHolder createViewHolder(Context context, View itemView) {
        return new ViewHolder(context, itemView);
    }

    public static ViewHolder createViewHolder(Context context, ViewGroup parent, int layoutId) {
        View itemView = LayoutInflater.from(context).inflate(layoutId, parent, false);
        return new ViewHolder(context, itemView);
    }

    /**
     * 通过viewId获取控件
     *
     * @param viewId
     * @return
     */
    public <T extends View> T getView(int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }

    public View getConvertView() {
        return mConvertView;
    }


    /****以下为辅助方法*****/

    /**
     * 设置TextView的值
     *
     * @param viewId
     * @param text
     * @return
     */
    public ViewHolder setText(int viewId, String text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    public ViewHolder setImageResource(int viewId, int resId) {
        ImageView view = getView(viewId);
        view.setImageResource(resId);
        return this;
    }

    public ViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
        ImageView view = getView(viewId);
        view.setImageBitmap(bitmap);
        return this;
    }

    public ViewHolder setImageDrawable(int viewId, Drawable drawable) {
        ImageView view = getView(viewId);
        view.setImageDrawable(drawable);
        return this;
    }

    public ViewHolder setBackgroundColor(int viewId, int color) {
        View view = getView(viewId);
        view.setBackgroundColor(color);
        return this;
    }

    public ViewHolder setBackgroundRes(int viewId, int backgroundRes) {
        View view = getView(viewId);
        view.setBackgroundResource(backgroundRes);
        return this;
    }

    public ViewHolder setTextColor(int viewId, int textColor) {
        TextView view = getView(viewId);
        view.setTextColor(textColor);
        return this;
    }

    public ViewHolder setTextColorRes(int viewId, int textColorRes) {
        TextView view = getView(viewId);
        view.setTextColor(mContext.getResources().getColor(textColorRes));
        return this;
    }

    /**
     * 关于事件的
     */
    public ViewHolder setOnClickListener(int viewId, View.OnClickListener listener) {
        View view = getView(viewId);
        view.setOnClickListener(listener);
        return this;
    }
    
    public ViewHolder setOnClickListener(View view, View.OnClickListener listener) {
        view.setOnClickListener(listener);
        return this;
    }

    public ViewHolder setOnTouchListener(int viewId, View.OnTouchListener listener) {
        View view = getView(viewId);
        view.setOnTouchListener(listener);
        return this;
    }

    public ViewHolder setOnLongClickListener(int viewId, View.OnLongClickListener listener) {
        View view = getView(viewId);
        view.setOnLongClickListener(listener);
        return this;
    }
}

我们的ViewHolder继承自RecyclerView.ViewHolder,内部通过SparseArray来缓存我们itemView内部的子View,从而得到一个通用的ViewHolder。每次需要创建ViewHolder只需要传入我们的layoutId即可。

封装 Adapter

public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
    protected Context mContext;
    protected int mLayoutId;
    protected List<T> mDatas;

    public CommonAdapter(Context context, int layoutId, List<T> datas) {
        mContext = context;
        mLayoutId = layoutId;
        mDatas = datas;
    }

    @Override
    public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        return ViewHolder.createViewHolder(mContext, parent, mLayoutId);
    }
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        convert(holder, mDatas.get(position));
    }

    public abstract void convert(ViewHolder holder, T t);

    @Override
    public int getItemCount() {
        return mDatas.size();
    }
}

onBindViewHolder这里主要用于数据、事件绑定,我们这里直接抽象出去,让用户去操作。使用起来是这样的:

//使用 CommonAdapter,传入 layout id 和data,好吃是不需要自己创建一个Adapter类 和 ViewHolder,不需复写 onCreateViewHolder等大量重复代码
recyclerView.setAdapter(new CommonAdapter<String>(this, R.layout.item_picture_up, dataList) {
    @Override
    public void convert(ViewHolder holder, final String data) {
        holder.setText(R.id.text, data);
        //方便的添加监听事件
        holder.setOnClickListener(holder.itemView, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "data : " + data, Toast.LENGTH_SHORT).show();
            }
        });
    }
});

多种ItemViewType(不同 layout 的 item)

多种ItemViewType,一般我们的写法是:

  • 复写getItemViewType,根据我们的bean去返回不同的类型
  • onCreateViewHolder中根据itemView去生成不同的ViewHolder

如果大家还记得,我们的ViewHolder是通用的,唯一依赖的就是个layoutId。那么上述第二条就变成,根据不同的itemView告诉我用哪个layoutId即可,生成viewholder这种事我们通用adapter来做。

于是,引入一个接口:

public interface MultiItemTypeSupport<T> {

    int getLayoutId(int itemType);

    int getItemViewType(int position, T t);
}

可以很清楚的看到,这个接口实际就是完成我们上述的两条工作。用户在使用过程中,通过实现上面两个方法,指明不同的Bean返回什么itemViewType,不同的itemView所对应的layoutId.

ok,有了上面这个接口,我们的参数就够了,下面开始我们的MultiItemCommonAdapter的编写。

public abstract class MultiItemCommonAdapter<T> extends CommonAdapter<T> {
    protected MultiItemTypeSupport<T> mMultiItemTypeSupport;

    public MultiItemCommonAdapter(Context context, List<T> datas, MultiItemTypeSupport<T> multiItemTypeSupport) {
        super(context, -1, datas);
        mMultiItemTypeSupport = multiItemTypeSupport;
    }

    @Override
    public int getItemViewType(int position) {
        return mMultiItemTypeSupport.getItemViewType(position, mDatas.get(position));
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        int layoutId = mMultiItemTypeSupport.getLayoutId(viewType);
        return ViewHolder.createViewHolder(mContext, parent, layoutId);
    }
}

很简单,不再说明。

使用:

//使用 MultiItemCommonAdapter,回调 getItemViewType,让用户传入不同的 viewType,并根据 viewType 在 getLayoutId 中传入 layout id
recyclerView.setAdapter(new MultiItemCommonAdapter<String>(this, dataList, new MultiItemTypeSupport<String>() {
    @Override
    public int getLayoutId(int viewType) {
        //根据不同 viewType 返回不同的 layout 给 viewHolder类 拿去创建 ViewHolder。
        if (viewType == 0) {
            return R.layout.item_picture_left;
        }
        return R.layout.item_picture_up;
    }

    @Override
    public int getItemViewType(int position, String s) {
        //前三行的 item 和 后面的 item 使用不同的 item
        if (position < 3) {
            return 0;
        }
        return 1;
    }
}) {
    @Override
    public void convert(ViewHolder holder, String data) {
        holder.setText(R.id.text, data);
    }
});

参考

一些想法

RecyclerView 写了四篇文章,其实还是有很多东西,没再写了,因为真的快写吐了。。。很多东西 都是越了解就感觉越了解的不够,想要成为 Android 高手 还有很长的路要走…… 每天坚持学些东西,日积月累总会质变的,加油。

ps:发现了一个很不错的Android导航网站:AndroidCat安卓书签网,是专门为Android开发者而收集整理的网站资源导航,以后可以方便的通过这个门户找资源啦!

相关文章

网友评论

    本文标题:RecyclerView 打造通用 Adapter

    本文链接:https://www.haomeiwen.com/subject/ythchxtx.html