Применение 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
Ответы
Ответ 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-байтов предполагают наличие стабильных идентификаторов для сохранения анимации в элементах списка.