RecyclerView的通用适配器,和滚动时不加载图片的封装



对本文有任何问题,可加我的个人微信询问:kymjs666

很早之前写过一篇讲ListView的万能适配器的方案通用Adapter与ListView滚动时不加载图片的封装,可以让你在写ListView的Adapter时只关注对控件设置内容,而不需要再去考虑ViewHolder、控件初始化、以及实现BaseAdapter的其他三个必须实现的函数。
对于RecyclerView我们需要使用RecyclerAdapter,使用方式与ListViewAdapter类似,具体代码大家可以在网上搜索,这里就只教大家使用封装后的简洁RecyclerAdapter了。

核心代码

首先我们来看一部分核心代码:

public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerHolder> {

    public BaseRecyclerAdapter(RecyclerView v, Collection<T> datas, int itemLayoutId) {
      //...
    }

    /**
     * Recycler适配器填充方法
     *
     * @param holder      viewholder
     * @param item        javabean
     * @param isScrolling RecyclerView是否正在滚动
     */
    public abstract void convert(RecyclerHolder holder, T item, int position, boolean isScrolling);

    @Override
    public RecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(cxt);
        View root = inflater.inflate(mItemLayoutId, parent, false);
        return new RecyclerHolder(root);
    }

    @Override
    public void onBindViewHolder(RecyclerHolder holder, int position) {
        convert(holder, realDatas.get(position), position, isScrolling);
        holder.itemView.setOnClickListener(getOnClickListener(position));
    }
}

以及RecyclerHolder的代码

public class RecyclerHolder extends RecyclerView.ViewHolder {
    private final SparseArray<View> mViews;

    public RecyclerHolder(View itemView) {
        super(itemView);
        //一般不会超过8个吧
        this.mViews = new SparseArray<View>(8);
    }

    /**
     * 通过控件的Id获取对于的控件,如果没有则加入views
     */
    public <T extends View> T getView(int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }

    /**
     * 为TextView设置字符串
     */
    public RecyclerHolder setText(int viewId, String text) {
        TextView view = getView(viewId);
        view.setText(text);
        return this;
    }

    /**
     * 为ImageView设置图片
     */
    public RecyclerHolder setImageByUrl(KJBitmap bitmap, int viewId, String url) {
        bitmap.display(getView(viewId), url);
        return this;
    }
}

实现原理

其实实现原理和我们之前讲过的ListView通用适配器的实现原理是一致的。Google以及在RecyclerAdapter中规范了Holder的应用,加入了onCreateViewHolder()和onBindViewHolder()方法分别来实现ViewHolder的创建和对Holder中的控件设置内容。
但是适配器写多了以后我们就会发现,其实这两个函数中写的内容也是重复的,于是就有了RecyclerHolder这个我们自己封装的Holder。
与平时我们自己实现的ViewHolder最大的不同在于,我们以前定义ViewHolder都是一个item里面要用哪个控件就定义哪个控件,而为了通用,我们抽出它们共同的部分————都是View。
但是我们还不知道我们的item中究竟会有多少个控件,所以这里我们再定义一个集合类,当然它也可以是一个Map(不能是List,因为我们还需要读取这个View呢,如果是List,就不知道哪个View保存在List的哪个index了,而用map可以通过View的id来作为key读取),这里我们依旧使用推荐的SparseArray,原因我们之前的通用ListView适配器中已经讲过了,这里就不再多说了,只需要把它看成是一个性能更好的Map就行了。

完整代码

最后,还有一个细节大家可以在完整代码中看到,就是我加入了滚动监听与item点击事件,这样就可以很方便的在基类中实现RecyclerView滚动的时候不加载图片,以及Google没有提供的RecyclerView的item点击事件了。

一点补充

有关滚动时不加载图片,之前有人提起了说我没有提到,这里就顺带讲一下实现原理:
其实就是在我们我适配器中声明一个全局的boolean变量用来保存此刻是否在滚动,然后通过给ListView或RecyclerView设置滚动监听,然后在滚动监听器的onScrollStateChanged()方法中给boolean值赋值,看是否在滚动。
这样在我们使用这个适配器的时候,就可以根据滚动状态的不同来判断:比如正在滚动的时候就只显示内存缓存的图片,如果内存缓存中没有就显示一张默认图片;而如果没有在滚动就采用正常的图片加载方案去加载网络或者缓存中的图片。