RecyclerView - 动画更改网格布局管理器的列数

13

我想为我的 RecyclerViewGridLayoutManager 添加动画效果。默认情况下,以3列的网格形式显示项目列表,用户可以选择显示更多或更少的列。

我希望RecyclerView中的 views 移动/缩放到它们的新位置,但我不知道如何实现。

我最终想要的

  • 通过扩展/收缩触摸手势来允许缩放网格=>我知道如何做到这一点
  • 动画改变 LayoutManager

有人知道如何动画改变 LayoutManager 吗?


你可以查看类似的问题和相关的代码片段,链接如下:问题代码片段 - Droidekas
然后调用 setSpanCount,接着调用 notifyDatasetChanged() - Droidekas
另一种我能想到的方法是假设有非常高的跨度计数(大约100个),并且在项目手势上,您可以更改所有项目的跨度大小。 - Droidekas
3个回答

18

这里的灵感来源是Google Photos应用程序和索尼库应用程序。

基本上有两种方法:

  1. 使用 setSpanCount(int) 修改GridLayoutManager的spancount

  2. 设置一个非常高的span count( ~100),使用 SpanSizeLookUp 来动态更改每个项目的spanSize。

    • 我在这个回答中提供的动画器使用了Musenkishi提供的Gist来动画展示网格布局的变化。
    • 我在一个示例GitHub项目中实现了此方法。
    • 注意事项:
      • 我目前使用单击监听器不断修改span size look up。这可以更改为ItemGestureListener以捕获缩放手势事件并相应更改。
      • 您需要确定一种选择span count的方法,以使一行中的所有项目占据整个屏幕宽度(因此您不会看到任何空白区域)
      • 由于无法在bindView / createView等内部调用notifyChanged方法,因此需要使用延迟后的可运行对象postDelayed来调用notifyItemRangeChanged。
      • 更改span size后,您需要notifyItemRangeChanged使用适当的范围,以便所有当前显示在屏幕上的项目相应地移动。我使用了(底部的代码)

这不是一个完整的解决方案,而是为同样问题提供的2小时解决方案。显然,您可以改进提到的所有点 : )。 我希望继续更新示例,因为这种视图一直吸引着我。 不要将此视为最终解决方案,而只是实现此方法的特定方式。如果您使用StaggerredLayoutManager,则可以轻松避免项目之间的空白。

public int calculateRange() {
     int start = ((GridLayoutManager)        grv.getLayoutManager()).findFirstVisibleItemPosition();
     int end = ((GridLayoutManager) grv.getLayoutManager()).findLastVisibleItemPosition();
     if (start < 0)
         start = 0;
     if (end < 0)
         end = getItemCount();
     return end - start;
  }

1
谢谢你提供的解决方案(比我想象中要简单得多)。改变跨度计数(这个我本来也可以自己想到)是我认为正确的方法。它的动画效果非常好。在更改后,只需调用适配器的 notifyItemRangeChanged 就可以正确启动动画...唯一的缺点是(但我在问题中没有要求更多),你只能使用 GridLayoutManager,不能在不同的 LayoutManager 之间切换...虽然,如果你想要,GridLayoutManager 也支持1列,因此至少也可以用作 LinearLayoutManager... - prom85
1
如果你真的想让动画更加流畅,并提高单列布局的性能,我建议你尝试构建自己的布局管理器:这个指南非常不错。 - Droidekas
@Droidekas,能否请你帮我在Android 7的快速设置中创建那个动画?(在你展开快捷方式时会发生 - 它们位于单行项目的快速瓦片中,然后扩展为3x3的网格。) - Alex Newman
@AlexNewman 尝试使用 LayoutAnimators 而不是网格 RVs 来完成此操作。非常抱歉回复晚了 :) - Droidekas

5

我和你遇到了同样的问题,到目前为止,我还没有找到一个好的解决方案。

在GridLayoutManager中简单更改列数似乎有些奇怪,所以目前我使用动画来淡入淡出整个布局。类似这样:

private void animateRecyclerLayoutChange(final int layoutSpanCount) {
    Animation fadeOut = new AlphaAnimation(1, 0);
    fadeOut.setInterpolator(new DecelerateInterpolator());
    fadeOut.setDuration(400);
    fadeOut.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            productsRecyclerLayoutManager.setSpanCount(layoutSpanCount);
            productsRecyclerLayoutManager.requestLayout();
            Animation fadeIn = new AlphaAnimation(0, 1);
            fadeIn.setInterpolator(new AccelerateInterpolator());
            fadeIn.setDuration(400);
            productsRecycler.startAnimation(fadeIn);
        }
    });
    productsRecycler.startAnimation(fadeOut);
}

如果您将淡入淡出动画与缩放每个可见项相结合,这将是一个适用于GridLayoutManager更改的不错的动画效果。

我目前有一个类似的解决方案...删除所有项目,更改布局管理器并重新添加所有项目也会产生动画效果,但也不完美...不过还是谢谢提供这个替代方案。 - prom85

2
你可以使用“手势检测器”来实现此操作,可以在此处查看示例教程:http://wiki.workassis.com/pinch-zoom-in-recycler-view/。在本教程中,我们将从图库中获取图像,并在recycler视图中以网格布局显示它们。您将能够在捏合手势上更改布局。以下是不同布局的屏幕截图。
mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
                @Override
                public boolean onScale(ScaleGestureDetector detector) {
                    if (detector.getCurrentSpan() > 200 && detector.getTimeDelta() > 200) {
                        if (detector.getCurrentSpan() - detector.getPreviousSpan() < -1) {
                            if (mCurrentLayoutManager == mGridLayoutManager1) {
                                mCurrentLayoutManager = mGridLayoutManager2;
                                mRvPhotos.setLayoutManager(mGridLayoutManager2);
                                return true;
                            } else if (mCurrentLayoutManager == mGridLayoutManager2) {
                                mCurrentLayoutManager = mGridLayoutManager3;
                                mRvPhotos.setLayoutManager(mGridLayoutManager3);
                                return true;
                            }
                        } else if(detector.getCurrentSpan() - detector.getPreviousSpan() > 1) {
                            if (mCurrentLayoutManager == mGridLayoutManager3) {
                                mCurrentLayoutManager = mGridLayoutManager2;
                                mRvPhotos.setLayoutManager(mGridLayoutManager2);
                                return true;
                            } else if (mCurrentLayoutManager == mGridLayoutManager2) {
                                mCurrentLayoutManager = mGridLayoutManager1;
                                mRvPhotos.setLayoutManager(mGridLayoutManager1);
                                return true;
                            }
                        }
                    }
                    return false;
                }
            });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接