我意识到,如果我在android:stateListAnimator
的项目上应用RecylerView
,则调用adapter.notifyDataSetChanged
会对某些RecylerView
的项目产生不良的闪烁效果(并非所有项目,奇怪的)
这是我的RecylerView项目
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:stateListAnimator="@anim/lift_up"
android:background="@drawable/white" >
...
</LinearLayout>
@anim/lift_up
定义为
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="true"
android:state_pressed="true">
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="8dip"
android:valueType="floatType" />
</item>
<item>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="4dip"
android:valueType="floatType" />
</item>
</selector>
和@drawable/white
定义为
<drawable name="white">#ffffffff</drawable>
当我拨打adapter.notifyDataSetChanged
时,RecylerView的最后5项会发生以下奇怪的闪烁效果。 (屏幕上共有10个可见项目)
此问题仅发生在API 21及更高版本,因为只有API 21支持android:stateListAnimator
这是一个错误,还是我错过了什么?
完整的最小可行代码可以从https://github.com/yccheok/RecyclerViewTutorial/tree/4763879598864233a8e6544fe240c3fb34a15b73
下载答案 0 :(得分:7)
并非所有项目,奇怪的是
这是设计(我相信)。
在内部,所有回收 ViewGroups(我已经处理过)都维护着一个View池。从头开始创建View非常昂贵。这些成本中的一部分是通过使用View池来消耗的,但会牺牲资源的使用。该池的大小代表了这种权衡。可以在此处查看基本实现:ViewPool from DeckView。
RecyclerView
对RecycledViewPool也一样。请注意默认的最大尺寸:
public static class RecycledViewPool {
....
private static final int DEFAULT_MAX_SCRAP = 5;
....
}
我认为你案例中的前5个观点不会闪烁,因为它们来自池 - 它们是在notifyDataSetChanged()
调用时创建的。这可能是StateListAnimator
没有启动的原因。对于其余5行/项,创建了新的视图。
来自源代码:
View getViewForPosition(int position, boolean dryRun) {
....
// 0) If there is a changed scrap, try to find from there
....
// 1) Find from scrap by position
....
// 2) Find from scrap via stable ids, if exists
....
// fallback to recycler
....
// getRecycledViewPool() returns an instance of RecycledViewPool
holder = getRecycledViewPool().getRecycledView(type);
....
// if holder is still 'null' after checking the pool, create a new one
....
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);
}
....
}
正如您所知,回收是一项严肃的事业。我无法解释的是为什么选项0, 1, and 2
失败 - 或者告诉他们是否会这样做。要检查我的假设,您可以更改池的max
大小并记下任何差异(在闪烁的视图中):
mRecyclerView
.getRecycledViewPool()
.setMaxRecycledViews(RecyclerView.INVALID_TYPE, 10);
答案 1 :(得分:1)
如果您在方法notifyDatasetChanged中检查了http://androidxref.com/6.0.0_r1/xref/frameworks/support/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java中的recyclerview方法notifyDataSetChanged的文档,则会提到&#34;
RecyclerView
将尝试合成可见的结构变化事件
对于报告他们具有{@link #hasStableIds()稳定ID}的适配器
使用此方法。这有助于动画和视觉目的
对象持久性,但单个项目视图仍然需要反弹
并重新开始。
类似的想法也在https://www.youtube.com/watch?v=8MIfSxgsHIs中勾画出来,如果你不得不为列表视图做动画,你可以做到stableIds,在上面提到的dev bytes系列中的更多例子假设列表项中动画持久性的稳定id