Android RecyclerView ViewHolder

01.ViewHolder的作用

  • ViewHolder作用大概有这些:
    • adapter应当拥有ViewHolder的子类,并且ViewHolder内部应当存储一些子view,避免时间代价很大的findViewById操作
    • 其RecyclerView内部定义的ViewHolder类包含很多复杂的属性,内部使用场景也有很多,而我们经常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView需要一个新类型。item的ViewHolder时调用来创建一个ViewHolder,而onBindViewHolder()方法则当RecyclerView需要在特定位置的item展示数据时调用。

02.ViewHolder与复用

  • 在复写RecyclerView.Adapter的时候,需要我们复写两个方法:

    • onCreateViewHolder
    • onBindViewHolder
    • 这两个方法从字面上看就是创建ViewHolder和绑定ViewHolder的意思
  • 复用机制是怎样的?

    • 模拟场景:只有一种ViewType,上下滑动的时候需要的ViewHolder种类是只有一种,但是需要的ViewHolder对象数量并不止一个。所以在后面创建了9个ViewHolder之后,需要的数量够了,无论怎么滑动,都只需要复用以前创建的对象就行了。那么逗比程序员们思考一下,为什么会出现这种情况呢
    • 看到了下面log之后,第一反应是在这个ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,但是onBindViewHolder方法每次都会调用的
    • image
  • 查看一下createViewHolder源代码

    • 发现这里并没有限制
    1
    2
    3
    4
    5
    6
    7
    public final VH createViewHolder(ViewGroup parent, int viewType) {
    TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
    final VH holder = onCreateViewHolder(parent, viewType);
    holder.mItemViewType = viewType;
    TraceCompat.endSection();
    return holder;
    }
  • 对于ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,可以查看

    • 获取为给定位置初始化的视图。
    • 此方法应由{@link LayoutManager}实现使用,以获取视图来表示来自{@LinkAdapter}的数据。
    • 如果共享池可用于正确的视图类型,则回收程序可以重用共享池中的废视图或分离视图。如果适配器没有指示给定位置上的数据已更改,则回收程序将尝试发回一个以前为该数据初始化的报废视图,而不进行重新绑定。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public View getViewForPosition(int position) {
    return getViewForPosition(position, false);
    }

    View getViewForPosition(int position, boolean dryRun) {
    return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
    }

    @Nullable
    ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {
    //代码省略
    }

03.ViewHolder简单封装

  • 关于ViewHolder简单的封装代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    public abstract class BaseMViewHolder<M> extends RecyclerView.ViewHolder {


    // SparseArray 比 HashMap 更省内存,在某些条件下性能更好,只能存储 key 为 int 类型的数据,
    // 用来存放 View 以减少 findViewById 的次数

    private SparseArray<View> viewSparseArray;

    BaseMViewHolder(View itemView) {
    super(itemView);
    if(viewSparseArray==null){
    viewSparseArray = new SparseArray<>();
    }
    }

    public BaseMViewHolder(ViewGroup parent, @LayoutRes int res) {
    super(LayoutInflater.from(parent.getContext()).inflate(res, parent, false));
    if(viewSparseArray==null){
    viewSparseArray = new SparseArray<>();
    }
    }

    /**
    * 子类设置数据方法
    * @param data
    */
    public void setData(M data) {}

    /**
    * 第二种findViewById方式
    * 根据 ID 来获取 View
    * @param viewId viewID
    * @param <T> 泛型
    * @return 将结果强转为 View 或 View 的子类型
    */
    @SuppressWarnings("unchecked")
    protected <T extends View> T getView(int viewId) {
    // 先从缓存中找,找打的话则直接返回
    // 如果找不到则 findViewById ,再把结果存入缓存中
    View view = viewSparseArray.get(viewId);
    if (view == null) {
    view = itemView.findViewById(viewId);
    viewSparseArray.put(viewId, view);
    }
    return (T) view;
    }


    /**
    * 获取上下文context
    * @return context
    */
    protected Context getContext(){
    return itemView.getContext();
    }


    /**
    * 获取数据索引的位置
    * @return position
    */
    protected int getDataPosition(){
    RecyclerView.Adapter adapter = getOwnerAdapter();
    if (adapter!=null && adapter instanceof RecyclerArrayAdapter){
    return getAdapterPosition() - ((RecyclerArrayAdapter) adapter).getHeaderCount();
    }
    return getAdapterPosition();
    }


    /**
    * 获取adapter对象
    * @param <T>
    * @return adapter
    */
    @Nullable
    private <T extends RecyclerView.Adapter> T getOwnerAdapter(){
    RecyclerView recyclerView = getOwnerRecyclerView();
    //noinspection unchecked
    return recyclerView != null ? (T) recyclerView.getAdapter() : null;
    }


    @Nullable
    private RecyclerView getOwnerRecyclerView(){
    try {
    Field field = RecyclerView.ViewHolder.class.getDeclaredField("mOwnerRecyclerView");
    field.setAccessible(true);
    return (RecyclerView) field.get(this);
    } catch (NoSuchFieldException ignored) {
    ignored.printStackTrace();
    } catch (IllegalAccessException ignored) {
    ignored.printStackTrace();
    }
    return null;
    }


    /**
    * 添加子控件的点击事件
    * @param viewId 控件id
    */
    protected void addOnClickListener(@IdRes final int viewId) {
    final View view = getView(viewId);
    if (view != null) {
    if (!view.isClickable()) {
    view.setClickable(true);
    }
    view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    if(getOwnerAdapter()!=null){
    if (((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener() != null) {
    ((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener()
    .onItemChildClick(v, getDataPosition());
    }
    }
    }
    });
    }
    }

    //省略部分代码
    }