调用notifyDataSetChanged

时间:2015-11-22 03:44:41

标签: android

我意识到,如果我在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个可见项目)

https://youtu.be/yB4UP2wEFk0

此问题仅发生在API 21及更高版本,因为只有API 21支持android:stateListAnimator

这是一个错误,还是我错过了什么?

完整的最小可行代码可以从https://github.com/yccheok/RecyclerViewTutorial/tree/4763879598864233a8e6544fe240c3fb34a15b73

下载

2 个答案:

答案 0 :(得分:7)

  

并非所有项目,奇怪的是

这是设计(我相信)。

在内部,所有回收 ViewGroups(我已经处理过)都维护着一个View池。从头开始创建View非常昂贵。这些成本中的一部分是通过使用View池来消耗的,但会牺牲资源的使用。该池的大小代表了这种权衡。可以在此处查看基本实现:ViewPool from DeckView

RecyclerViewRecycledViewPool也一样。请注意默认的最大尺寸:

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