animateLayoutChanges does not work well with nested layout?
AndroidAndroid LayoutAndroid AnimationAndroid Problem Overview
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?
Android Solutions
Solution 1 - Android
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.
Solution 2 - Android
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:
-
Make sure you add
android:animateLayoutChanges="true"
to the will-be-resizedViewGroup
(LinearLayout
/RelativeLayout
/FrameLayout
/CoordinatorLayout
). -
Use
setVisibility()
to control the visibility of your targetView
. -
Listen carefully from here, add
android:animateLayoutChanges="true"
to the outerViewGroup
of your will-be-resizedViewGroup
, this outerViewGroup
must be the one who wraps all the position-changingView
affected by the animation. -
Add following code in your
Activity
before thesetVisibility()
, here therootLinearLayout
is the outerViewGroup
I mentioned above:LayoutTransition layoutTransition = rootLinearLayout.getLayoutTransition(); layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
Before:
After:
Reminder: If you miss the 3rd step, you will get null pointer exception.
Good luck!
Solution 3 - Android
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);
}
Solution 4 - Android
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.
Solution 5 - Android
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
.
Solution 6 - Android
I had a similar issue:
I was using a animatelayoutchanges in one of my activity with a recycler view, I also added some custom layout transition because I wanted to increase speed of the animation while an item disappears in the list. It was working fine when it was not in a nested layout.
I had used the same adapter for another recyclerview which was in a nested layout. It was not working and I tried all the above solutions, None worked for me.
The real reason was, I forgot to set
mTicketsListAdapter.setHasStableIds(true);
in the nested layout activity. And after setting setHasStableIds to true, the animations was working perfectly in the nested layout.