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

我有一个包含13个项目的列表(尽pipe可能会添加或删除项目),职位0-12。 当第一次显示包含RecyclerView的片段时,用户只能看到位置0到7(位置7只有一半可见)。 在我的适配器中,我Log每次查看持有人绑定/绑定(idk,如果语法适用于此)并logging其位置。

适配器

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

从我的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); } } } 

在这种方法中,我将holder与其位置一起Log holder

从selectAll()中记录日志

所以到此为止,一切似乎都很正常。 我们有位置0-7显示,并根据Log这些是约束的立场。 当我点击selectAll()而不更改可见视图(滚动)时,我看到位置0-7被定义,而8-12是null 。 到现在为止还挺好。

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

在检查Log我发现这是因为即使它们被报告为null也不会被绑定:

滚动后的适配器日志

更令人困惑的是,这不是每次都发生。 如果我第一次启动应用程序并testing它可能会工作。 但事情似乎没有发生。 我猜这与回收的意见有关,但即使如此,他们也不会受到约束?

编辑 (6-29-16)
AndroidStudio更新后,我似乎无法重现该错误。 它按我的预期工作,绑定null视图。 如果这个问题再次出现,我会回到这个post。

这是因为:

  • 视图不会被添加到recyclerview( getChildAt将不起作用,并将返回空位置)
  • 他们也被caching( onBind将不会被调用)

调用recyclerView.setItemViewCacheSize(0)将解决这个“问题”。

因为默认值是2( private static final int DEFAULT_CACHE_SIZE = 2;RecyclerView.Recycler ),你将总是得到2个视图,它们不会调用onBind但不会被添加到回收站

在你的情况下,第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); } 

现在,为了解决你的问题,你需要跟踪应该回收的意见,但要分离,然后做你的部分过程

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

Pedro Oliveira和Zartha的回答对于理解这个问题很有帮助,但是我没有看到任何我很满意的解决scheme。

我相信你有两个很好的select取决于你在做什么:

选项1

如果你想让onBindViewHolder()被调用来进行离屏视图,而不pipe它是否被caching/分离,那么你可以这样做:

 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 ); 

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

选项2

如果您只想执行部分更改(而不是onBindViewHolder()内的所有内容),则在onBindViewHolder( ViewHolder view_holder, int position ) ,您需要将该position存储在view_holder ,然后在onViewAttachedToWindow( ViewHolder view_holder )执行所需的更改onViewAttachedToWindow( ViewHolder view_holder )

为了简单起见,我推荐使用选项1,除非你的onBindViewHolder()正在做一些像Bitmap一样的事情。

我认为玩视图并不是一个在回收视图中的好主意。 我经常使用的方法只是为RecyclerView引入一个标志到模型。 假设你的模型是 –

 class MyModel{ String name; int age; } 

如果你正在追踪被select的视图,那么在模型中引入一个布尔值。 现在看起来像 –

 class MyModel{ String name; int age; boolean isSelected; } 

现在,您的checkbox将在新标志isSelected(onBindViewHolder())的基础上被选中/取消选中。 在视图上的每一个select将改变相应的模型select值的值为真,并在未选中将其更改为假。 在你的情况下,只需运行一个循环,将所有模型的isSelected值更改为true,然后调用notifyDataSetChanged()

例如,假设你的列表是

 ArrayList<MyModel> recyclerList; private void selectAll(){ for(MyModel myModel:recyclerList) myModel.isSelected = true; notifyDataSetChanged(); } 

我的build议,同时使用recyclerView或ListViewless尝试玩意见。

所以在你的情况下 –

 @Override public void onBindViewHolder(final ViewHolder holder, final int position) { holder.clickableView.setTag(position); holder.selectableView.setTag(position); holder.checkedView.setChecked(recyclerList.get(position).isSelected); Log.d(TAG, "onBindViewHolder() position: " + position); ... } @Override public void onClick(View view){ int position = (int)view.getTag(); recyclerList.get(position).isSelected = !recyclerList.get(position).isSelected; } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { int position = (int)buttonView.getTag(); recyclerList.get(position).isSelected = isChecked; } 

希望它会帮助你,请让我知道如果你需要任何进一步的解释:)

所以我认为你的问题由@Pedro Oliveira回答如下。 RecycleView的主要意义在于,他在任何时候都使用特殊的algorithm来cachingViewHolder。 所以下一个onBindViewHolder(…)可能无法正常工作,例如。 如果视图是静态的,或者其他的东西。

而关于你的问题,你认为使用RecycleView的dynamic更改视图。 不要这样做! 因为RecycleView使视图无效并且有caching系统,所以你将会遇到很多问题。

使用LinkedListView执行此任务!