即使RecyclerView.findViewHolderForAdapterPosition()在该位置返回null,也不会在视图位置调用onBindViewHolder()

You*_*ode 22 android android-viewholder recycler-adapter android-recyclerview

我有一个包含13个项目的列表(虽然可以添加或删除项目),位置0-12.当首次显示包含RecyclerView的片段时,用户只能看到位置0到7(位置7只有一半可见).在我的适配器中,Log每次视图持有者被绑定/绑定(如果语法适用于此,则为idk)并记录其位置.

适配器

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    Log.d(TAG, "onBindViewHolder() position: " + position);
    ...
}
Run Code Online (Sandbox Code Playgroud)

从我Log看到0-7的位置是绑定的:

从适配器登录

我有一个selectAll()方法,ViewHolder通过适配器位置获取每个.如果返回holder的不是null我,则使用返回holder来更新视图以显示它已被选中.如果返回的持有者是null我调用selectOnBind()一个方法来标记该位置的视图更新,以显示它在绑定时被选中而不是实时,因为它当前未显示:

public void selectAll() {
    for (int i = 0; i < numberOfItemsInList; i++) {
        MyAdapter.ViewHolder holder = (MyAdapter.ViewHolder)
                mRecyclerView.findViewHolderForAdapterPosition(i);

        Log.d(TAG, "holder at position " + i + " is " + holder);

        if (holder != null) {
            select(holder);
        } else {
            selectOnBind(i);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种方法中我Logholder与它的位置一起:

从selectAll()登录

所以到目前为止,一切似乎都很正常.我们有0-7的位置显示,并根据Log这些位置限制.当我在selectAll()不改变可见视图(滚动)的情况下点击时,我看到定义0-7位置,8-12位置null.到现在为止还挺好.

这是有趣的地方.如果在调用后selectAll()我向下滚动列表位置8和9,则不显示它们被选中.

检查时,Log我发现它是因为它们从未被束缚,即使它们被报告为null:

滚动后适配器日志

更令人困惑的是,每次都不会发生这种情况.如果我首先启动应用程序并测试它可能会有效.但事后似乎没有失败.我猜这与被回收的观点有关,但即便如此,他们也不必受约束?

编辑(6-29-16)
AndroidStudio更新后我似乎无法重现该错误.它按照我的预期工作,绑定空视图.如果这个问题重新出现,我将回到这篇文章.

Ped*_*ira 17

发生这种情况是因为:

  • 视图不会添加到recyclerview(getChildAt不起作用,将为该位置返回null)
  • 它们也被缓存(onBind不会被调用)

通话recyclerView.setItemViewCacheSize(0)将解决这个"问题".

由于默认值为2(private static final int DEFAULT_CACHE_SIZE = 2;in RecyclerView.Recycler),因此您将始终获得2个不会调用onBind但未添加到回收站的视图

  • 这只是解释为什么会发生这种情况的原因.升级AndroidStudio改变了自Recyclerview问世以来发生的行为,这似乎很奇怪. (2认同)

Zaa*_*tha 6

在您的情况下,位置8和9的视图未被回收,它们将从窗口分离并将再次附加.并且对于这些分离视图onBindViewHolder不被调用,仅onViewAttachedToWindow被调用.如果您在适配器中覆盖这些功能,您可以看到我在说什么.

@Override
    public void onViewRecycled(ViewHolder vh){
        Log.wtf(TAG,"onViewRecycled "+vh);
    }

    @Override
    public void onViewDetachedFromWindow(ViewHolder viewHolder){
        Log.wtf(TAG,"onViewDetachedFromWindow "+viewHolder);
    }
Run Code Online (Sandbox Code Playgroud)

现在,为了解决您的问题,您需要跟踪应该回收但已分离的视图,然后执行您的部分过程

@Override
    public void onViewAttachedToWindow(ViewHolder viewHolder){
        Log.wtf(TAG,"onViewAttachedToWindow "+viewHolder);
    }
Run Code Online (Sandbox Code Playgroud)


Kac*_*acy 6

Pedro Oliveira 和 Zartha 的答案非常有助于理解问题,但我没有看到任何令我满意的解决方案。

我相信您有两个不错的选择,具体取决于您在做什么:

选项1

如果您想onBindViewHolder()调用屏幕外视图,而不管它是否已缓存/分离,那么您可以执行以下操作:

RecyclerView.ViewHolder view_holder = recycler_view.findViewHolderForAdapterPosition( some_position );

if ( view_holder != null )
{
    //manipulate the attached view
}
else //view is either non-existant or detached waiting to be reattached
    notifyItemChanged( some_position );
Run Code Online (Sandbox Code Playgroud)

这个想法是,如果视图被缓存/分离,那么notifyItemChanged()将告诉适配器视图无效,这将导致onBindViewHolder()被调用。

选项 2

如果您只想执行部分更改(而不是里面的所有内容onBindViewHolder()),那么在 of 内部onBindViewHolder( ViewHolder view_holder, int position ),您需要将 存储在positionview_holder,并在 中执行您想要的更改onViewAttachedToWindow( ViewHolder view_holder )

为简单起见,我推荐选项 1,除非您onBindViewHolder()正在做一些密集的事情,比如弄乱位图。

  • 选项 1 帮助了我。谢谢! (2认同)