android - 如何在 RecyclerView 中使用 GridLayoutAnimation?

标签 android android-recyclerview layout-animation

我正在尝试用新的 RecyclerView 替换我的 GridView(使用 GridLayoutManager),但它似乎不能很好地处理 gridLayoutAnimation(ClassCastException: LayoutAnimationController$AnimationParameters cannot be cast to GridLayoutAnimationController$AnimationParameters)。它适用于常规布局动画,但由于它是网格,因此在平板电脑上需要很长时间才能完成。

我想要完成的类似于 Hierarchical Timing .如果您查看示例视频,它会显示布局动画从左上角到右下角。常规布局动画会逐行执行动画,因此在较大的网格(例如平板电脑)上需要花费太多时间来完成。我也试过探索 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 的回答,here是一种只需要您使用扩展 RecyclerView 的自定义 View 的解决方案.基本上只覆盖 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);
        }
    }
}

最佳答案

LayoutAnimationController 耦合到 ViewGroup 并且 ListViewGridView 都扩展了下面的方法以提供 child 的 动画参数。问题是 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 1 开始就存在的 API,没有真正的文档或示例,我强烈建议不要使用它,因为有很多方法可以复制它的功能。

关于android - 如何在 RecyclerView 中使用 GridLayoutAnimation?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26539663/

相关文章:

android - 如何更新Gradle 4.1的output.outputFile.name.replace?

android - Recycler 中的 Diff Util 实现在每次刷新时将其滚动到顶部

java - Kotlin 需要 ViewHolder getter/setter 中的对象

listview - 使用 React Native LayoutAnimation 对 ListView 行删除进行动画处理

android - DownloadManager 没有收到下载完成的 Action

java - NoClassDefFoundError 异常 android joda 时间

javascript - React Native - LayoutAnimation : how to make it just animate object inside component, 不是整个组件/ View ?

使用按钮 OnClick 监听器时 Android 应用程序崩溃

java - 添加 RecyclerView Layoutmanager 时我的应用程序停止了