animateLayoutChanges在嵌套布局中无法正常工作?

70

我有一个类似下面的嵌套布局:

 <LinearLayout>     <!----Parent layout--->
    <LinearLayout>    <!-----child 1--->
       ...
    </LinearLayout>   <!----child 1 ended--->
    <LinearLayout>    <!-----child 2--->
       ...
    </LinearLayout>   <!----child 2 ended--->
 </LinearLayout>    <!----Parent endded--->

我现在遇到的问题是,由于所有数据项都在子视图1或子视图2的Linearlayout中,如果我添加或删除一个项目,子线性布局将使用animateLayoutChanges的效果进行动画处理,但父布局不会做任何动画。 (我对所有线性布局设置了android:animateLayoutChangestrue)。特别是当我删除子视图1中的一个项目时,动画效果变得很奇怪(基本上子视图2会跳起来,而子视图1仍在执行其动画)。

有人有什么解决方法吗?

谢谢

更新

在发布这个问题后不久,我在 Android 开发者网站的 LayoutTransition API 中找到了以下内容。

在嵌套视图层次结构的多个级别上使用 LayoutTransition 可能无法正常工作,因为各个级别的布局之间存在相互关系。

那么有没有人对此问题有解决方法建议?

6个回答

98

animateLayoutChanges属性使用LayoutTransitions,可以对布局的子元素进行动画处理,并且从Android 4.0开始,对布局层次结构中的祖先元素一直到树的顶部都会进行动画处理。在Honeycomb中,只有布局的子元素会进行动画处理。详见此Android开发者博客文章

不幸的是,目前似乎没有简单的方法让布局的同级元素对其LayoutTransitions做出反应。您可以尝试使用TransitionListener来获取通知,以便在更改布局边界时使用Animators相应地移动同级视图。请参见Chet Haase在此Google+帖子中的第二个答案。

编辑 - 结果证明,有一种方法可以做到这一点。在Android 4.1+ (API level 16+)中,您可以使用布局转换类型CHANGING,它默认处于禁用状态。要在代码中启用它,请使用:

ViewGroup layout = (ViewGroup) findViewById(R.id.yourLayout);
LayoutTransition layoutTransition = layout.getLayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);

所以,在您的示例中,如果想要对子项2进行布局动画,您需要为其启用CHANGING布局转换。当其父级的边界发生更改时,该转换将被应用。

有关更多详细信息,请参见此DevBytes视频


34
将layoutTransition.enableTransitionType(LayoutTransition.CHANGING);添加到父布局,对于嵌套布局确实有效。但是,消失的视图将不同步,当您去掉一个视图时,父布局将先完成,然后才是子布局。要解决这个问题,您还需要修改子布局过渡并设置layoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0)。通过这样做,出现和消失将与父级和子级同步。 - GDA
陈,你介意告诉我们你读了哪个文件吗? 我在这里情况非常相似 :) - Daniele Segato
@DanieleSegato 这是在 developer.android.com 上的那个页面 :). 这里是链接 http://developer.android.com/reference/android/animation/LayoutTransition.html。 - Chen
3
@chronogenre setStartDelay确实在两个转换之间进行同步,但是消失的视图正在逐渐消失在其旁边移动的子元素上。有没有一种方法可以在淡出完成后开始CHANGING运动? - AsafK
@AsafK 你可以创建一个自定义的LayoutTransition并设置:lt.setAnimator(LayoutTransition.DISAPPEARING, null)。这将解决淡出问题。 - Evgeni Roitburg
显示剩余7条评论

53

好的,在消化了第一个答案之后,我将在这里为那些在使用嵌套布局时没有获得适当动画效果的人们简化此过程:

  1. 确保您将android:animateLayoutChanges="true"添加到将被调整大小的ViewGroupLinearLayout/RelativeLayout/FrameLayout/CoordinatorLayout)。

  2. 使用setVisibility()来控制您的目标View的可见性。

  3. 从这里开始认真听,将android:animateLayoutChanges="true"添加到包装您的将被调整大小的ViewGroup外部ViewGroup中,这个外部ViewGroup必须是包裹所有受动画影响的位置更改View的那个。

  4. setVisibility()之前,在您的Activity中添加以下代码,这里的rootLinearLayout是我上面提到的外部ViewGroup

     LayoutTransition layoutTransition = rootLinearLayout.getLayoutTransition();
     layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
    

Before:

enter image description here

After:

enter image description here


温馨提示:如果您错过了第三步,将会出现空指针异常。

祝你好运!


感谢您的详细解释! - Adi Azarya
5
感谢您的努力加入GIF,非常感谢。+1 - Extremis II
你太棒了,非常感谢你 <3 - Gustavo Navarro
如果您快速点击“视图”会怎么样?在不闪烁的情况下,转换动画是否仍然按预期工作? - marticztn
谢谢您的回答。在我的情况下,我必须像您建议的那样,在嵌套的ViewGroup以及其父级中添加android:animateLayoutChanges="true"。但是,与您不同的是,我必须在嵌套的ViewGroup上调用.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)而不是在其父级上调用。 - Adil Hussain
我想补充一下,如果你有多个wrap_content容器,有时候需要对几个容器调用enableTransitionType(LayoutTransition.CHANGING) - Borzh

10
作为 Kotlin 扩展。
fun ViewGroup.forceLayoutChanges() {
    layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
}

使用方法

someContainer.forceLayoutChanges()

注意:

根据我的经验,当容器嵌套得很深时,就会发生这种情况。由于某种原因,android:animateLayoutChanges="true" 不起作用,但使用此函数将强制其起作用。


1
让我成为第四个点赞你的答案的人。工作得非常好!! :) - Makari Kevin
@MakariKevin 很高兴能帮忙! - Michael
1
我发现layoutTransition可能为空。因此,我在Kotlin扩展函数中添加了if(layoutTransition == null)layoutTransition = LayoutTransition()以防止意外的NPE。 - Mario Huizinga

7

我们在LinearLayout中添加了android:animateLayoutChanges属性,但更改没有触发动画。为了解决这个问题,请使用以下代码:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
((ViewGroup) findViewById(R.id.llRoot)).getLayoutTransition()
      .enableTransitionType(LayoutTransition.CHANGING);
}

更多详细信息请参见:

此处链接.


3

看起来在父元素上延迟过渡同样适用于动画效果。至少对我来说,以下代码可以实现正确的展开/折叠动画。

expandTrigger.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        TransitionManager.beginDelayedTransition(parentLayout);
        expanded = !expanded;

        child1.setVisibility(expanded ? View.VISIBLE : View.GONE);
    }
});

对于深度嵌套的布局,您有时需要在调用TransitionManager时使用层次结构中较高的父级。


这实际上是我能找到的唯一可以通过其更新方法调整弹出窗口大小的方法。 - Daniel Wilson

1
我遇到了类似的问题:
我在一个活动中使用animatelayoutchanges和recyclerview,同时添加了一些自定义布局转换,因为我想在列表中某个项消失时增加动画速度。当它不在嵌套布局中时运行良好。
我曾将相同的适配器用于另一个嵌套布局中的recyclerview。但是它无法正常工作,我尝试了以上所有解决方案,都没有效果。
真正的原因是我忘记设置了。
mTicketsListAdapter.setHasStableIds(true);

在嵌套布局活动中。将setHasStableIds设置为true后,动画在嵌套布局中完美运行。

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