57

I have a nested layout like the following:

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

The problem I am having now is that since all my data items are within child 1 or child 2 Linearlayout, if I add or delete a item the child linearlayout will animated with the effect of animateLayoutChanges but the parent layout will not do any animation. (I have android:animateLayoutChanges set to true for all linear layouts). Especially when I delete an item within child 1 the animation effect becomes weird (basically child 2 will jump up while child 1 is still doing its animation).

Does anybody have any idea how to solve this?

Thanks

UPDATE

Shortly after I posted this question, I found this on android developer's site in the LayoutTransition API.

Using LayoutTransition at multiple levels of a nested view hierarchy may not work due to the interrelationship of the various levels of layout.

So does anyone have any work around suggestions for this issue?

Benoit Duffez
  • 9,889
  • 11
  • 69
  • 114
Chen
  • 1,416
  • 1
  • 12
  • 16

5 Answers5

92

The animateLayoutChanges property makes use of LayoutTransitions, which animate both the layout's children and, from Android 4.0 onward, ancestors in the layout hierarchy all the way to the top of the tree. In Honeycomb, only the layout's children will be animated. See this Android Developers Blog post for details.

Unfortunately, it seems that there's currently no simple way to have the siblings of a layout react to its LayoutTransitions. You could try using a TransitionListener to get notified when the layout's bounds are being changed, and move the sibling views accordingly using Animators. See Chet Haase's second answer in this Google+ post.

EDIT - Turns out there is a way. In Android 4.1+ (API level 16+) you can use a layout transition type CHANGING, which is disabled by default. To enable it in code:

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

So, in your example, to have child 2 layout animated, you'd need to enable the CHANGING layout transformation for it. The transformation would then be applied when there is a change in the bounds of its parent.

See this DevBytes video for more details.

Timo Hildén
  • 1,090
  • 9
  • 8
  • I did try this and it is not really working well in my situation, I think set enableTransitionType to changing has a similar effect as set the animateLayoutChanges flag to be true. Anyway, I moved to another approach so animating nested layout is no longer needed. – Chen Jul 07 '14 at 16:01
  • @Vijju I moved away from this design and took a different approach. From the document I have read, I don't think this can be easily done so I guess you either have to build with out this small animation or write the transition yourself. – Chen Jul 24 '14 at 02:32
  • Using LayoutTransition.enableTransitionType(LayoutTransition.CHANGING) worked like a charm! Thanks! – wdziemia Oct 31 '14 at 00:19
  • 33
    layoutTransition.enableTransitionType(LayoutTransition.CHANGING); works indeed for nested layout just add it to your parent layout. however disappearing views will be out of sync you will see that when you gone a view the parent layout will finish first before the child. to fix this you also need to modify the child layout transition and set layoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); by doing this appearing and disappearing will be in sync with parent and child. – GDA Mar 01 '15 at 13:21
  • Chen do you mind telling us which is this document you read? I'm in a very similar situation here :) – Daniele Segato Mar 05 '15 at 11:25
  • @DanieleSegato Its the one on developer.android.com :). http://developer.android.com/reference/android/animation/LayoutTransition.html here is the link. – Chen Mar 11 '15 at 20:01
  • This along with @chronogenre 's solution worked for me. However, I had to apply this to multiple children in my case. – akarthik10 Jan 19 '16 at 17:37
  • Hi. When I do enableTransitionType(int) on the parent LinearLayout, whose child I want to animate when making it (child) visible/gone, it's giving 'NullPointerException... enableTransitionType(int) on a null object reference'. Any hint? – Abu Ruqaiyah May 18 '16 at 12:01
  • 3
    @chronogenre setStartDelay indeed syncs between the two transitions, but the disappearing view is fading over the moving child next to it. is there a way to start the CHANGING movement after the fade out is done ? – AsafK Jul 19 '16 at 11:10
  • @AbuRuqaiyah maybe you are missing `android:animateLayoutChanges="true"` in your layout container? It was happening to me and after reading the docs it seems that if you don't set the flag then no `LayoutTransition` is instantiated. – saiyancoder Aug 23 '17 at 03:08
  • @AsafK you can create a custom LayoutTransition and set: lt.setAnimator(LayoutTransition.DISAPPEARING, null). This is solving the fading issue – Evgeni Roitburg Sep 23 '17 at 12:24
  • I found that LayoutTransition.CHANGE_DISAPPEARING is required in Android 6.0 but not in 9. On Android 9 adding LayoutTransition.CHANGING to the parent fixed the issue and was enough, whereas on Android 6.0 the animation still acted weirdly without adding this to the child. Great answer and important comment! – Hadas Kaminsky Jan 23 '19 at 17:00
25

Ok, after digesting the first answer, I make it simple here, for those who don't get proper animation result when using android:animateLayoutChanges="true" in NESTED layout:

  1. Make sure you add android:animateLayoutChanges="true" to the will-be-resized ViewGroup (LinearLayout/RelativeLayout/FrameLayout/CoordinatorLayout).

  2. Use setVisibility() to control the visibility of your target View.

  3. Listen carefully from here, add android:animateLayoutChanges="true" to the outer ViewGroup of your will-be-resized ViewGroup, this outer ViewGroup must be the one who wraps all the position-changing View affected by the animation.

  4. Add following code in your Activity before the setVisibility(), here the rootLinearLayout is the outer ViewGroup I mentioned above:

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

Before:

enter image description here

After:

enter image description here


Reminder: If you miss the 3rd step, you will get null pointer exception.

Good luck!

Sam Chen
  • 2,491
  • 1
  • 17
  • 31
7

We had added the android:animateLayoutChanges attribute to our LinearLayout but the change didn’t trigger an animation. To fix that, use this code:

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

More details.

Nathan Tuggy
  • 2,239
  • 27
  • 28
  • 36
beokh
  • 121
  • 1
  • 3
3

As a Kotlin Extension

fun ViewGroup.forceLayoutChanges() {
    layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
}

Usage

someContainer.forceLayoutChanges()

Notes:

In my experience, this happens when the container is a deep nested layout. For some reason android:animateLayoutChanges="true" just doesn't work, but using this function will force it to work.

Michael
  • 8,464
  • 2
  • 59
  • 62
2

It seems that a delayed transition on the parent also works for animating. At least for me the following code gives a proper expand/collapse animation.

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

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

For deeply nested layouts you sometimes might need to use a parent higher up in the hierarchy in the call to the TransitionManager.

sihaya
  • 907
  • 6
  • 12