Как использовать GridLayoutAnimation в RecyclerView?
Я пытаюсь заменить свой GridView новым RecyclerView (используя GridLayoutManager), но кажется, что он плохо справляется с gridLayoutAnimation (ClassCastException: LayoutAnimationController$AnimationParameters cannot be cast to GridLayoutAnimationController$AnimationParameters
). Он работает с обычной анимацией макета, но, поскольку это сетка, на планшетах это занимает слишком много времени.
То, что я пытаюсь сделать, похоже на иерархическую синхронизацию. Если вы посмотрите на пример видео, оно показывает, что анимация макета перемещается по диагонали от левого верхнего угла к правому нижнему углу. Обычная анимация макета будет выполнять анимацию строка за строкой, поэтому на больших сетках (например, на планшетах) потребуется слишком много времени. Я также попытался исследовать ItemAnimator, но при этом анимация запускалась бы одновременно на всех ячейках, как в примере "Не".
Есть ли способ выполнить эту анимацию макета сетки в RecyclerView?
Это gridview_layout_animation.xml:
<!-- replace gridLayoutAnimation with layoutAnimation and -->
<!-- replace column- and rowDelay with delay for RecyclerView -->
<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:columnDelay="15%"
android:rowDelay="15%"
android:animation="@anim/grow_in"
android:animationOrder="normal"
android:direction="top_to_bottom|left_to_right"
android:interpolator="@android:interpolator/linear"
/>
А это анимация grow_in.xml:
<set android:shareInterpolator="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:interpolator="@android:interpolator/decelerate_quint"
android:fromXScale="0.0"
android:toXScale="1.0"
android:fromYScale="0.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="true"
android:duration="400"
android:startOffset="200"
/>
</set>
РЕДАКТИРОВАТЬ: Основываясь на ответе Galaxas0, вот решение, которое требует только от вас использовать пользовательское представление, которое расширяет RecyclerView
, В основном только переопределение attachLayoutAnimationParameters()
метод. С этим <gridLayoutAnimation>
работает как это было с GridView.
public class GridRecyclerView extends RecyclerView {
public GridRecyclerView(Context context) {
super(context);
}
public GridRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setLayoutManager(LayoutManager layout) {
if (layout instanceof GridLayoutManager){
super.setLayoutManager(layout);
} else {
throw new ClassCastException("You should only use a GridLayoutManager with GridRecyclerView.");
}
}
@Override
protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) {
if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager){
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount();
animationParams.count = count;
animationParams.index = index;
animationParams.columnsCount = columns;
animationParams.rowsCount = count / columns;
final int invertedIndex = count - 1 - index;
animationParams.column = columns - 1 - (invertedIndex % columns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;
} else {
super.attachLayoutAnimationParameters(child, params, index, count);
}
}
}
3 ответа
LayoutAnimationController
связан в ViewGroup
и оба ListView
а также GridView
расширить метод ниже, чтобы обеспечить ребенка animationParams
, Проблема в том, что GridLayoutAnimationController
требует своего AnimationParameters
это не может быть приведено в класс.
/**
* Subclasses should override this method to set layout animation
* parameters on the supplied child.
*
* @param child the child to associate with animation parameters
* @param params the child's layout parameters which hold the animation
* parameters
* @param index the index of the child in the view group
* @param count the number of children in the view group
*/
protected void attachLayoutAnimationParameters(View child,
LayoutParams params, int index, int count) {
LayoutAnimationController.AnimationParameters animationParams =
params.layoutAnimationParameters;
if (animationParams == null) {
animationParams = new LayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
animationParams.count = count;
animationParams.index = index;
}
Поскольку этот метод по умолчанию добавляет LayoutAnimationController.AnimationParameters
вместо GridLayoutAnimationController.AnimationParameters
Исправление должно состоять в том, чтобы создать и прикрепить один заранее. Что нам нужно реализовать, это то, что GridView
уже делает:
@Override
protected void attachLayoutAnimationParameters(View child,
ViewGroup.LayoutParams params, int index, int count) {
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
animationParams.count = count;
animationParams.index = index;
animationParams.columnsCount = mNumColumns;
animationParams.rowsCount = count / mNumColumns;
if (!mStackFromBottom) {
animationParams.column = index % mNumColumns;
animationParams.row = index / mNumColumns;
} else {
final int invertedIndex = count - 1 - index;
animationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / mNumColumns;
}
}
Тиражировать GridView
самое близкое, что мы можем сделать - это вставить в onBindViewHolder()
что позволяет им бегать раньше dispatchDraw
вызов, который вызывает анимацию.
ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
GridLayoutAnimationController.AnimationParameters animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
animationParams.count = 9;
animationParams.columnsCount = 3;
animationParams.rowsCount = 3;
animationParams.index = position;
animationParams.column = position / animationParams.columnsCount;
animationParams.row = position % animationParams.columnsCount;
При использовании RecyclerView
новый GridLayoutManager
попробуйте получить параметры из этого. Приведенный выше пример является подтверждением концепции, демонстрирующей, что она работает. У меня есть жестко закодированные значения, которые не совсем подходят и для моего приложения.
Поскольку это API, существующий со времен API 1 без реальной документации или примеров, я настоятельно рекомендую не использовать его, учитывая, что существует множество способов тиражировать его функциональность.
Более простое решение
TransitionManager.beginDelayedTransition(moviesGridRecycler);
gridLayoutManager.setSpanCount(gridColumns);
adapter.notifyDataSetChanged();
Но не забудьте сделать свой RecyclerAdapter setHasStableIds(true);
и реализовать getItemID()
@Override
public long getItemId(int position) {
return yourItemSpecificLongID;
}
Цитирование @Musenkishi на https://gist.github.com/Musenkishi/8df1ab549857756098ba
Понятия не имею. Ты звонишь
recyclerView.scheduleLayoutAnimation();
после настройки адаптера? И вы установилиandroid:layoutAnimation="@anim/your_layout_animation"
на ваш<GridRecyclerView>
в макете?
Это решило мою проблему.