Android RecyclerView 问题汇总

01.Recyclerview.getLayoutPosition()问题

  • 在RecycleView中的相关方法中,有两种类型的位置

    • 布局位置:从LayoutManager的角度看,条目在最新布局计算中的位置。
      • 返回布局位置的方法使用最近一次布局运算后的位置,如getLayoutPosition()和findViewHolderForLayoutPosition(int)。这些位置包含了最近一次布局运算后的变化。你可以根据这些位置来与用户正在屏幕上看到的保持一致。比如,你有一个条目列表,当用户请求第5个条目时,你可以使用这些方法来匹配用户看到的。
    • 适配器位置:从适配器的角度看,条目在是适配器中的位置。
      • 另外一系列方法与AdapterPosition关联,比如getAdapterPosition()和findViewHolderForAdapterPosition(int)。当你想获得条目在更新后的适配器中的位置使用这些方法,即使这些位置变化还没反映到布局中。比如,你想访问适配器中条目的位置时,就应该使用getAdapterPosition()。注意,notifyDataSetChanged()已经被调用而且还没计算新布局,这些方法或许不能够计算适配器位置。所以,你要小心处理这些方法返回NO_POSITION和null的情况。
    • 注意: 这两种类型的位置是等同的,除非在分发adapter.notify*事件和更新布局时。
  • 关于两者的区别

    • 网上查了一些资料,发现相关内容很少,最后在stackoverflow上终于看到有大神这样解释两者的区别
    • 具体区别就是adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样。
      • 在notifyDataSetChanged之后并不能马上获取Adapter中的position, 要等布局结束之后才能获取到
      • 在notifyItemInserted之后,Layout不能马上获取到新的position,因为布局还没更新(需要<16ms的时间刷新视图), 所以只能获取到旧的,但是Adapter中的position就可以马上获取到最新的position。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public final int getAdapterPosition() {
    if (mOwnerRecyclerView == null) {
    return NO_POSITION;
    }
    return mOwnerRecyclerView.getAdapterPositionFor(this);
    }

    public final int getLayoutPosition() {
    return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
    }
  • 可能会导致的错误

    • 这种情况有点难以复现,在 ViewHolder 中处理 item 的点击事件的时候,发现多个 item 同时点击就会出现闪退,debug 看到 position = -1
    • 解决办法:使用 ViewHolder#getLayoutPosition() 获取 position,而不要通过 ViewHolder#getAdapterPosition() 来获取 position 的