如何指定TransitionManager.beginDelayedTransition仅影响直接子视图?

7

我试图将ConstraintLayout与具有动画效果的上下滑动视图配合使用。 这些视图垂直排列,顶部是RecyclerView,下面叠放着另外两个视图:

<constraint layout container>
    [                    ]
    [    recycler view   ] 
    [                    ]
    [48dp height 1st view]
    [48dp height 2nd view]
</constraint layout container>

动画非常简单:当点击按钮时,第一个视图从容器底部移动到上面能看到的位置,再次点击后它会向下移动并停留在第二个视图上方。这时,顶部的RecyclerView会改变高度,因为它被约束在第一个视图上,否则会留下空白的区域。
到目前为止,一切都很好,动画也运行良好,但当用户快速连续点击按钮时,就会出现以下错误:
    java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

调查后发现,我正在使用的 TransitionManager 造成了这个问题。当点击按钮将第一个视图向上移动(或向下移动,只需想象相反的步骤)时,我发现以下有趣的内容:
  • 从 RecyclerView 中淡出第一个/最后一个视图(取决于您是否启用了反转布局或从末尾堆叠)
  • 从 RecyclerView 中删除该视图
  • 减小 RecyclerView 的高度
  • 将第一个视图向上移动
即使我在所有可能的地方都指定了 RecyclerView 不应该动画更改项,TransitionManager 覆盖了它,这意味着有一个窗口,在此期间正在要从 RecyclerView 中删除的同一视图可以再次添加到同一父级中,并且这个窗口就是当视图由管理器淡出时。
由于淡入淡出动画不是很快,用户很容易在此期间轻松地连续两次点击按钮,导致管理器重新添加正在从 RecyclerView 中删除的淡出视图,从而导致应用程序崩溃并抛出上面的错误。
由于这是在 RecyclerView 内部引起的问题,因此我完全可以在其中不进行任何动画,因此我想知道如何在 TransitionManager.beginDelayedTransition 中指定仅对 ConstraintLayout 的直接子视图进行动画处理,这样它将不会动画处理 RecyclerView 中的视图。
相关文档并未对此作出任何说明,因此我在此提出了这个问题。如何限制转换为直接子视图?
如果有用的话,我在此包含了过渡管理器的片段。
    final ConstraintSet constraintSet;
    final ConstraintLayout constraintLayout;

    constraintSet = new ConstraintSet();
    constraintLayout = (ConstraintLayout) viewParent;

    TransitionManager.beginDelayedTransition(constraintLayout);

    constraintSet.clone(constraintLayout);
    constraintSet.connect(startId, startSide, endId, endSide, margin);
    constraintSet.applyTo(constraintLayout);

请使用TransitionManager.beginDelayedTransition的两个参数版本,传递一个排除了一些视图的Transition - pskink
@pskink,“TransitionSet”有一个允许忽略子视图的方法,谢谢。你能发布答案让我接受吗? - Shadow
Transition has that methods: Transition#excludeTarget / Transition#excludeChildren / Transition#removeTarget - pskink
@pskink我知道,是因为你的评论让我发现了它,这就是为什么我要求你将其发布为答案以便我接受,因为不允许将评论作为答案接受。 - Shadow
2个回答

19

pskink在评论中回答了问题,但是我已经两次要求他将其作为答案发布以供我接受,但他从未这样做,因此我将发布它并将其标记为已回答。

TransitionManager有一个方法excludeChildren,非常适合我所寻找的内容。您可以按类类型、特定视图的子级等使用它。

编辑:添加请求的示例

    AutoTransition autoTransition = new AutoTransition();
    autoTransition.excludeChildren(R.id.recyclerView, true);
    TransitionManager.beginDelayedTransition(constraintLayout, autoTransition);

在这个特定的例子中,以constraintLayout作为目标视图的父级,并且将R.id.recyclerView作为该父级的一个子视图,我不希望它被动画化。您还可以排除所有特定类别的子项(例如EditText)等。


谢谢提供示例。但正如我所说,使用AutoTransition更改了ConstraintLayout默认生成的动画,因此我不能使用它。 - BATMAN
@KashishMalhotra 很抱歉,但那是我用于解决问题的代码。如果它对你的情况不起作用,最好你发布一个新问题与此相关,这样其他人就可以尝试帮助你。 - Shadow
它起作用了。我犯了一个非常愚蠢的错误。在调试代码后,您的解决方案很好地工作了。谢谢! - BATMAN
太棒了,谢谢。这也与此相关:https://dev59.com/wOo6XIcBkEYKwwoYIAgL#59355664 所以我也参考了这个答案。 - mochadwi
在我的情况下,excludeChildren没有起作用,我使用了excludeTarget代替。 - Ali Zarei

8
我用以下方法解决了这个问题:
TransitionManager.beginDelayedTransition(
                        constraintLayout,
                        TransitionSet().apply {
                            ordering = TransitionSet.ORDERING_SEQUENTIAL
                            addTransition(ChangeBounds())
                            addTransition(Fade(Fade.IN))
                        }
                    )

如果我们没有传递自定义的转换,那么AutoTransition里面的代码基本相同,但我删除了addTransition(Fade(Fade.OUT)),因为它似乎是导致崩溃的原因。至少对我来说,动画看起来与原始动画非常相似。


非常感谢。当地图相机移动或空闲时,TransitionSet.ORDERING_TOGETHER 对于在我的 Google 地图上为叠加小部件设置动画效果非常有效。 - Jay

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