Android 探究onCreateViewHolder和onBindViewHolder两者关系和调用次数

    As we all know-众所周知,RecycleView 在展示数据列表的页面替换Listview的趋势已然出现,现在网络上关于它的资料也是铺天盖地,我就不再介绍如何使用了。但是,我对RecycleView的Adapter还是比较感兴趣的,因为懂得了Adapter,对于RecycleView的扩展及优化会很有帮助。这篇文章,先了解一下 onCreateViewHolder调用次数和 onBindViewHolder 之间的暧昧关系。

添加依赖

com.android.support:appcompat-v7:26.1.0
com.android.support:recyclerview-v7:26.1.0

(提示:根据自己android.support 版本去调整recycleView依赖版本)

 

上代码嘞!

 

RecyclerViewAdapter.class :继承RecyclerView.Adapter后,会重写三个方法:

public class RecycleViewAdapter extends RecyclerView.Adapter {

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}
onCreateViewHolder()方法,负责承载每个子项的布局。它有两个参数,其中一个是 int viewType;
onBindViewHolder()方法,负责将每个子项holder绑定数据。俩参数分别是RecyclerView.ViewHolder holder, int position;
by the way-顺便说下,该类继承的RecyclerView.Adapter。其实可以加泛型ViewHolder,如下:
public class RecycleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

 

布局文件

item_layout.xml 。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="xxx"/>

</LinearLayout>

Usually-通常情况下,我们会define一个ViewHolder类喔,这样方便绑定数据和扩展。

 public Context mContext;
    static class ViewHolde extends RecyclerView.ViewHolder {

        TextView txt;
        public ViewHolde(View itemView) {
            super(itemView);
            txt = itemView.findViewById(R.id.txt);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View item = LayoutInflater.from(mContext).inflate(R.layout.item_layout , parent ,false);
        return new RecyclerAdapter.ViewHolde(item);
    }

 

绑定数据

data.class 实体类。(动手敲代码的复制即可)

public class data {
    /**
     * 名字
     */
    private String name;
    /**
     * 子项类型
     * 1: 标题
     * 2:普通
     */
    private int type;
    public data(String name, int type) {
        this.name = name;
        this.type = type;
    }
    public String getName() {
        return name;
    }
    
    public int getType() {
        return type;
    }
    /**
     * 存储的数据列表
     */
    private static List<data> mDataList;
    public static List<data> getDataList() {
        if (mDataList == null){
            mDataList = new ArrayList<>();

            data bean1 = new data("热销推荐",1);
            mDataList.add(bean1);
            mDataList.addAll(foods("热销零食"));

            data bean2 = new data("菜品",1);
            mDataList.add(bean2);
            mDataList.addAll(foods("小菜一碟"));

            data bean3 = new data("主食",1);
            mDataList.add(bean3);
            mDataList.addAll(foods("馒头,嗷~"));

            data bean4 = new data("饮料",1);
            mDataList.add(bean4);
            mDataList.addAll(foods("啤酒饮料矿泉水"));

        }
        return mDataList;
    }
    /**
     * 循环存储食物名字
     * @param foodName
     * @return
     */
    private static List<data> foods(String foodName){
        List<data> foods =new ArrayList<>();
        data food = null;
        for (int i = 0 ; i < 10 ; i++){
            food = new data(foodName+"("+i+")",2);
            foods.add(food);
        }
        return foods;
    }
}

索性,我就把Adapter里的全部代码贴出来咯!其实代码很简单,如下:

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    /**
     * context
     */
    public Context mContext;

    /**
     * 集合
     */
    public List<data> mDatas = new ArrayList<>();
    /**
     * data
     */
    public data mData;

    public RecyclerAdapter(Context mContext, List<data> mDatas) {
        this.mContext = mContext;
        this.mDatas = mDatas;
    }

    static class ViewHolde extends RecyclerView.ViewHolder {

        TextView txt;
        public ViewHolde(View itemView) {
            super(itemView);
            txt = itemView.findViewById(R.id.txt);
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View item = LayoutInflater.from(mContext).inflate(R.layout.item_layout , parent ,false);
        Log.d("aaa","onCreateViewHolder————"+viewType);
        if (viewType == 1){//标题
            item.setTag(true);
        }else{
            item.setTag(false);
        }
        return new ViewHolde(item);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof  ViewHolde){
            mData =mDatas.get(position);
           ((ViewHolde) holder).txt.setText(mData.getName());
            Log.d("aaa","onBindViewHolder————"+mData.getName());
           if (mData.getType() == 1){
               ((ViewHolde) holder).txt .setTextColor(mContext.getColor(R.color.colorAccent));
               ((ViewHolde) holder).txt .setBackgroundColor(mContext.getColor(R.color.colorPrimary));
               ((ViewHolde) holder).txt .setTextSize(20);
           }
        }
    }

    @Override
    public int getItemViewType(int position) {
        Log.d("aaa","getItemViewType————"+ mDatas.get(position).getType());
        return mDatas.get(position).getType();
    }
   
    @Override
    public int getItemCount() {
        return mDatas.size();
    }
}

运行结果

public class MainActivity extends AppCompatActivity {

    RecyclerView mRecyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView  =findViewById(R.id.recyclelist);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(new RecyclerAdapter(this, data.getDataList()));
    }
}

好了,我们先运行看效果吧。

估计,看官们看代码也已经不耐烦了吧?  

看完效果,我们接着说。

《Android 探究onCreateViewHolder和onBindViewHolder两者关系和调用次数》

Log日志分析

为了探究onCreateViewHolder和onBindViewHolder的调用次数,上面dapter代码贴出后,大家可能没注意,其实我在关键地方都标有log输出。

1、当第一屏页面显示出现数据时:

12-26 17:44:58.698 9860-9860/? D/aaa: onCreateViewHolder————1
12-26 17:44:58.699 9860-9860/? D/aaa: onBindViewHolder————热销推荐
12-26 17:44:58.705 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.706 9860-9860/? D/aaa: onBindViewHolder————热销零食(0)
12-26 17:44:58.710 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.710 9860-9860/? D/aaa: onBindViewHolder————热销零食(1)
12-26 17:44:58.715 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.715 9860-9860/? D/aaa: onBindViewHolder————热销零食(2)
12-26 17:44:58.720 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.720 9860-9860/? D/aaa: onBindViewHolder————热销零食(3)
12-26 17:44:58.725 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.725 9860-9860/? D/aaa: onBindViewHolder————热销零食(4)
12-26 17:44:58.731 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.731 9860-9860/? D/aaa: onBindViewHolder————热销零食(5)
12-26 17:44:58.736 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.736 9860-9860/? D/aaa: onBindViewHolder————热销零食(6)
12-26 17:44:58.741 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.741 9860-9860/? D/aaa: onBindViewHolder————热销零食(7)
12-26 17:44:58.746 9860-9860/? D/aaa: onCreateViewHolder————1
12-26 17:44:58.746 9860-9860/? D/aaa: onBindViewHolder————菜品
12-26 17:44:58.752 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.752 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(0)
12-26 17:44:58.757 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.757 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(1)
12-26 17:44:58.761 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.761 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(2)
12-26 17:44:58.765 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.765 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(3)
12-26 17:44:58.768 9860-9860/? D/aaa: onCreateViewHolder————2
12-26 17:44:58.768 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(4)
12-26 17:44:58.807 9860-9860/? D/aaa: onCreateViewHolder————1
12-26 17:44:58.808 9860-9860/? D/aaa: onBindViewHolder————热销推荐

我第一眼看上去,以为onCreateViewHolder 和 onBindViewHolder调用次数是保持一致的,它们为每个子项都执行了一次。本文开始写的时候,我还草率做了总结。还好,得到了热心网友的指正!

2、然后,我们滑动到底部,打印如下:  

        12-26 17:45:54.449 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(5)
        12-26 17:45:54.469 9860-9860/? D/aaa: onCreateViewHolder————2
        12-26 17:45:54.470 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(6)
        12-26 17:45:54.488 9860-9860/? D/aaa: onCreateViewHolder————2
        12-26 17:45:54.488 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(7)
        12-26 17:45:54.506 9860-9860/? D/aaa: onCreateViewHolder————1
        12-26 17:45:54.506 9860-9860/? D/aaa: onBindViewHolder————主食
        12-26 17:45:54.512 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(0)
        12-26 17:45:54.514 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(1)
        12-26 17:45:54.516 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(2)
        12-26 17:45:54.519 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(3)
        12-26 17:45:54.521 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(4)
        12-26 17:45:54.541 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(5)
        12-26 17:45:54.549 9860-9860/? D/aaa: onCreateViewHolder————2
        12-26 17:45:54.549 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(6)
        12-26 17:45:54.554 9860-9860/? D/aaa: onBindViewHolder————馒头,嗷~(7)
        12-26 17:45:54.556 9860-9860/? D/aaa: onBindViewHolder————饮料
        12-26 17:45:54.559 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(0)
        12-26 17:45:54.561 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(1)
        12-26 17:45:54.563 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(2)
        12-26 17:45:54.565 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(3)
        12-26 17:45:54.567 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(4)
        12-26 17:45:54.569 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(5)
        12-26 17:45:54.571 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(6)
        12-26 17:45:54.573 9860-9860/? D/aaa: onBindViewHolder————啤酒饮料矿泉水(7)
        12-26 17:45:54.585 9860-9860/? D/aaa: onBindViewHolder————菜品

3、最后,我们滑动到顶部,打印如下:  

        12-26 17:47:23.099 9860-9860/? D/aaa: onBindViewHolder————主食
        12-26 17:47:23.109 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(7)
        12-26 17:47:23.114 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(6)
        12-26 17:47:23.119 9860-9860/? D/aaa: onBindViewHolder————饮料
        12-26 17:47:23.126 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(5)
        12-26 17:47:23.129 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(4)
        12-26 17:47:23.131 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(3)
        12-26 17:47:23.133 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(2)
        12-26 17:47:23.152 9860-9860/? D/aaa: onCreateViewHolder————2
        12-26 17:47:23.152 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(1)
        12-26 17:47:23.164 9860-9860/? D/aaa: onBindViewHolder————小菜一碟(0)
        12-26 17:47:23.167 9860-9860/? D/aaa: onBindViewHolder————菜品
        12-26 17:47:23.173 9860-9860/? D/aaa: onBindViewHolder————菜品
        12-26 17:47:23.177 9860-9860/? D/aaa: onBindViewHolder————热销零食(7)
        12-26 17:47:23.185 9860-9860/? D/aaa: onBindViewHolder————热销零食(6)
        12-26 17:47:23.187 9860-9860/? D/aaa: onBindViewHolder————热销零食(5)
        12-26 17:47:23.192 9860-9860/? D/aaa: onBindViewHolder————热销推荐
        12-26 17:47:23.195 9860-9860/? D/aaa: onBindViewHolder————热销零食(4)
        12-26 17:47:23.202 9860-9860/? D/aaa: onBindViewHolder————热销零食(3)
        12-26 17:47:23.208 9860-9860/? D/aaa: onBindViewHolder————热销零食(2)
        12-26 17:47:23.216 9860-9860/? D/aaa: onBindViewHolder————热销零食(1)
        12-26 17:47:23.219 9860-9860/? D/aaa: onBindViewHolder————热销零食(0)
        12-26 17:47:23.225 9860-9860/? D/aaa: onBindViewHolder————热销推荐
         

很简单,先来总结一波~~ ^_^

首先,onBindeViewHolder方法的调用时机是item出现(或将要出现)在屏幕上时,这时需要向传入的viewHolder中填充数据等操作。

然后,onCreateViewHolder的目的是创建viewHolder。而viewHolder作为recyclerView缓存管理的对象是可以在列表中复用的。

最后,当屏幕上下滑动,子项移除屏幕viewHolder就会被回收,子项复用时会从缓存池中判断item type再次调用onBindViewHolder方法。

 

复用回收机制

getViewForPosition()方法是复用机制的入口,也就是 Recycler 开放给外部使用复用机制的api,外部调用这个方法就可以返回想要的 View,而至于这个 View 是复用而来的,还是重新创建得来的,就都由 Recycler 内部实现,对外隐藏。

Recycler 两个重要的结构体,用来缓存 ViewHolder 的:

ArrayList<ViewHolder> mCachedViews:

滑动过程中的回收和复用都是先处理的这个 List,这个集合里存的 ViewHolder 的原本数据信息都在,所以可以直接添加到 RecyclerView 中显示,不需要再次重新 onBindViewHolder()。只有原来的子项可以重新复用这个 ViewHolder,新位置的子项无法从 mCachedViews 里拿 ViewHolder出来用。

ArrayList<ViewHolder> mRecyclerPool:

存在这里的 ViewHolder 的数据信息会被重置掉,相当于 ViewHolder 是一个重创新建的一样。拿到 ViewHolder 之后,还会再次调用 resetInternal() 来重置 ViewHolder,这样 ViewHolder 就可以当作一个全新的 ViewHolder 来使用,所以需要重新调用 onBindViewHolder 来绑定数据。

复用时,也是先到 mCachedViews 里找 ViewHolder,但需要各种匹配条件,概括一下就是只有原来位置的卡位可以复用存在 mCachedViews 里的 ViewHolder,如果 mCachedViews 里没有,那么才去 ViewPool 里找。

回收的逻辑比较简单,由 LayoutManager 来遍历移出屏幕的子项,然后对每个子项进行回收操作,回收时,mCachedViews 优先级高于 RecyclerViewPool,回收时都是把 ViewHolder 放在 mCachedViews 里面,如果 mCachedViews 满了,那就在 mCachedViews 里拿一个 ViewHolder 扔到 ViewPool 缓存里,然后 mCachedViews 就可以空出位置来放新回收的 ViewHolder 了。在 ViewPool 里的 ViewHolder 都是跟全新的 ViewHolder 一样,只要 type 一样,有找到,就可以拿出来复用,重新绑定下数据即可。

 

结尾,送菊花。

最后说一句话,下面一篇文章是把这个案例升级,实现可以吸附在顶部的列表小标题。欢迎各位看官去研究一波。

 

Android 使用RecycleView实现吸附小标题的Demo(附源码)

 

 

 

    原文作者:艾阳丶
    原文地址: https://blog.csdn.net/csdn_aiyang/article/details/80094302
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞