Подтвердить что ты не робот

Применение stateListAnimator в элементе RecylerView вызовет мерцающий эффект при вызове notifyDataSetChanged

Я понимаю, что если я применил элемент android:stateListAnimator on 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, на последних 5 элементах RecylerView возникает следующий странный мерцающий эффект. (На экране есть всего 10 видимых элементов)

https://youtu.be/yB4UP2wEFk0

Эта проблема возникает только в API 21 и выше, поскольку только API 21 поддерживает android:stateListAnimator

Является ли это ошибкой, или я что-то пропустил?

Полный минимальный работоспособный код можно загрузить с https://github.com/yccheok/RecyclerViewTutorial/tree/4763879598864233a8e6544fe240c3fb34a15b73

4b9b3361

Ответ 1

Не все элементы, странно

Это по дизайну (я считаю).

Внутри, все группы просмотра ViewGroups (с которыми я столкнулся) поддерживают пул View. Его дорого создать "С нуля". Некоторые из этих затрат рассеиваются с помощью пула View за счет использования ресурсов. Размер этого пула представляет этот компромисс. Базовая реализация может быть рассмотрена здесь: ViewPool из 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);

Ответ 2

Если вы проверяете документацию метода recyclerview notifyDataSetChanged в http://androidxref.com/6.0.0_r1/xref/frameworks/support/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java для метода notifyDatasetChanged, в нем упоминается следующее"
        

RecyclerView будет пытаться синтезировать видимые события структурных изменений         для адаптеров, которые сообщают, что они имеют {@link #hasStableIds() стабильные идентификаторы}, когда         этот метод используется. Это может помочь в целях анимации и визуального         постоянство объектов, но отдельные представления элементов все равно должны быть восстановлены         и отреагировал.

Аналогичная идея набросается также в https://www.youtube.com/watch?v=8MIfSxgsHIs, где вы бы сделали stableIds true, если вам нужно было сделать анимацию для просмотра списка, а также больше примеры в вышеупомянутых сериях dev-байтов предполагают наличие стабильных идентификаторов для сохранения анимации в элементах списка.