How to bring Animation to front: bringToFront and zAdjustment/Z-ordering - android

I have a LinearLayout with two views in it, next to each other. When a view is tapped upon, it animates to full screen. This goes fine for the view on the right, but it fails for the left view:
I am unable to animate the left view on top of the right view
Without corrupting my layout with bringToFront(). Adjusting the Z-order of my animation does not seem to work.
Not a solution: The problem is gone when I use "brintToFront()" on the left view, but this causes my layout to be completely corrupted afterwards, and there is no brintToBack() function or whatsoever. => brintToFront = not a good solution?
Adjusting the Z-order of my animation does not seem to work (does not change anything).
scaleAnimation.setZAdjustment(Animation.ZORDER_TOP);
translateAnimation.setZAdjustment(Animation.ZORDER_TOP);
AnimationSet set = new AnimationSet(true);
set.addAnimation(scaleAnimation);
set.addAnimation(translateAnimation);
set.setZAdjustment(AnimationSet.ZORDER_TOP);
myFrameLayout.startAnimation(set);
Why does Z-ordering not work as expected?

This should be possible if you extend LinearLayout and override the following method:
getChildDrawingOrder (int childCount, int i)
To make sure layout uses your function you need to call setChildrenDrawingOrderEnabled(true)
See ViewGroup javadoc

For your z reordering will apply you gonna need to request a new layout on the view, but try doing it before starting your animation:
AnimationSet set = new AnimationSet(true);
// init animation ...
scaledView.bringToFront();
scaledView.requestLayout();
myFrameLayout.startAnimation(set);

I guess there is no good answer to this.
I am now animating everything else on the screen too, so if view 1 has to grow larger on top of view 2 than I animate view 2 also, in such a way that it looks like view 1 is growing on top of view 2, while actually view 2 is getting smaller while view 1 is getting larger.
Quite a lot of work and a bad solution, but I guess the only solution.

Related

How to change animations used by animateLayoutChanges mechanism?

I have a RecyclerView in which user selects an item. Upon selection certain view becomes visible before the text (see image). I want to animate its appearance.
animateLayoutChanges property on the item's layout does the job perfectly, except for the animation type for view appearance - it fades in. I want it to scale from 0 to 100% size, I believe it's called 'pop up' animation.
If I disable animateLayoutChanges and use XML animation for that, it works, but the nearby text is no longer animated (it should slide to accomodate space for the view and its margin). It instantly shifts to the right and then animation is played. This is worse with reverse animation, since the text overlaps the view before it has disappeared.
So I need to combine default mechanism and my own animation somehow.
What would be the simplest way to accomplish that without delving into custom animations?
After much digging in the wrong places, found the answer myself. Might help somebody.
animateLayoutChanges property if enabled utilizes an instance of LayoutTransition to do its job in ViewGroup.
So in order to change animations, you can create an instance of LayoutTransition, set Animator for the transition that you need to change and then assign that instance to ViewGroup via setLayoutTransition().
In my case the result is:
Animator scaleDown = ObjectAnimator.ofPropertyValuesHolder((Object)null, PropertyValuesHolder.ofFloat("scaleX", 1, 0), PropertyValuesHolder.ofFloat("scaleY", 1, 0));
scaleDown.setDuration(300);
scaleDown.setInterpolator(new OvershootInterpolator());
Animator scaleUp = ObjectAnimator.ofPropertyValuesHolder((Object)null, PropertyValuesHolder.ofFloat("scaleX", 0, 1), PropertyValuesHolder.ofFloat("scaleY", 0, 1));
scaleUp.setDuration(300);
scaleUp.setStartDelay(300);
scaleUp.setInterpolator(new OvershootInterpolator());
LayoutTransition itemLayoutTransition = new LayoutTransition();
itemLayoutTransition.setAnimator(LayoutTransition.APPEARING, scaleUp);
itemLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, scaleDown);
ViewGroup av = (ViewGroup)v.findViewById(R.id.animated_layout);
av.setLayoutTransition(itemLayoutTransition);
More info in LayoutTransition reference documentation.
You can do same thing with animateLayoutChange just see following article
https://proandroiddev.com/the-little-secret-of-android-animatelayoutchanges-e4caab2fddec

Can I change the behavior of Android's LayoutTransition to eliminate the fade but keep the height animation?

It's easy to add layout transitions with this attribute:
android:animateLayoutChanges="true"
However, the animation you get does not create a pleasing user experience. When elements are added to the layout (I'm using a simple vertical LinearLayout) or change from gone to visible there's a 2-stage process that I think is rather annoying. First, room is prepared for the new element (everything else is pushed down). Then when there's enough room, the new view fades into existence. Likewise, when a view is removed or changes from visible to gone, first it fades out, then the room claimed by it gradually shrinks to zero.
I would really like a way to change the animation to what I really think is the natural way to do it: When adding a view its height gradually changes from zero to its full size, so that first you see just the top, without ever changing the alpha. When removing a view its height gradually changes to its full size to zero, so that near the end of the animation you see just the top, without ever changing the alpha.
How can I accomplish this in Android? (Note: the user can tap on several buttons together and cause several elements to appear / disappear in quick succession, before the animation for the other views ended - or even make something appear while it's still appearing).
Another question that this is perhaps not the place to ask: why isn't this the default?
(And if it's possible, can a slightly different behavior be specified in which first just the bottom of the view appears, rather than the top, like the new view slides down from under the one above it?)
You have to write your own animator and set it.
Code:
final ViewGroup profileParent = (ViewGroup) view.findViewById(R.id.profileParent);
LayoutTransition transition = new LayoutTransition();
Animator appearingAnimation = ObjectAnimator.ofFloat(null, "translationY", 600/*profileParent.getHeight()*/, 0);
appearingAnimation.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setTranslationY(0f);
}
});
Animator disappearingAnimation = ObjectAnimator.ofFloat(null, "translationY", 0, 600/*profileParent.getHeight()*/);
appearingAnimation.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator anim) {
View view = (View) ((ObjectAnimator) anim).getTarget();
view.setTranslationY(0f);
}
});
transition.setAnimator(LayoutTransition.APPEARING, appearingAnimation);
transition.setDuration(LayoutTransition.APPEARING, 300);
transition.setStartDelay(LayoutTransition.APPEARING, 0);
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnimation);
transition.setDuration(LayoutTransition.DISAPPEARING, 300);
transition.setStartDelay(LayoutTransition.DISAPPEARING, 0);
profileParent.setLayoutTransition(transition);

animateLayoutChanges does not work well with nested layout?

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?
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.
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-resized ViewGroup (LinearLayout/RelativeLayout/FrameLayout/CoordinatorLayout).
Use setVisibility() to control the visibility of your target View.
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.
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:
After:
Reminder: If you miss the 3rd step, you will get null pointer exception.
Good luck!
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.
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.
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.
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.

Setting LayoutParams twice, no effect?

I'm doing this in Monodroid(C#) but I'm sure most of you understand anyway. I need to move a certain textview in intervalls. The first aligning to its parent left works fine. Then i wait 4 seconds and want it to align right of the parent, which gives no effect at all. It stays left. How come?
Example (C#)
RelativeLayout.LayoutParams newParams = (RelativeLayout.LayoutParams)textView.LayoutParameters;
newParams.AddRule(LayoutRules.AlignParentLeft);
textView.LayoutParameters = newParams;
System.Threading.Thread.Sleep(4000);
RelativeLayout.LayoutParams newParams2 =(RelativeLayout.LayoutParams)textView.LayoutParameters;
newParams2.AddRule(LayoutRules.AlignParentRight);
textView.LayoutParameters = newParams2;
Really odd since it works flawless the 1st time..
after setting new layout parameters, you need to call requestLayout() on your view to take effects.
requestLayout()
Call this when something has changed which has invalidated the layout
of this view. This will schedule a layout pass of the view tree.
so you should call:
textViewToAnimate.requestLayout();

How to bring a view to front without calling bringToFront()?

There is apparently a bug in Android which breaks View.bringToFront.
If I have Views arranged in a GridView and want to bring a View to front by calling bringToFront(), it may get moved to the bottom right position in the GridView.
Something shady is going on there, so I can't use bringToFront(). When I don't call it, everything works fine. But the Views overlap - if I use scale animation to scale up a View, it's partially obscured by the Views to the bottom and to the right.
I've checked out bringToFront's source and it calls parent.bringChildToFront(...)
it's this method
public void bringChildToFront(View child) {
int index = indexOfChild(child);
if (index >= 0) {
removeFromArray(index);
addInArray(child, mChildrenCount);
child.mParent = this;
}
}
it apparently removes the View from itself! Without mercy! Is a ViewGroup so dumb that it can't manage Z-indexes in any other way that shuffling controls around? Obviously when GridView removes its child and then adds it back again, it gets placed at the end of the Grid!
Is there anything I can do to show a View on top of others without resorting to some hacking? One thing that comes to my mind is to create another View above the GridView, which will appear to be above the one I'm trying to bringToFront(), but I don't like this solution.
apparently I missed the android:clipChildren="false" property in GridView. It solves my problem.
Have you tried making the View focusable and just calling requestFocus()?
Calling bringToFront() changes the ordering in the GridView. Try creating an ImageView with an image of the view you want to animate and animate that instead.
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
final ImageView imageView = new ImageView(getActivity());
imageView.setImageBitmap(bitmap);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(view.getWidth() , view.getHeight());
imageView.setLayoutParams(params);
rootview.addView(imageView);
Add an animation listener to your animation and remove the ImageView at the end of the animation.
From API 21 you can call:
view.setZ(float)
If you target API above 21, you can simply add the attribute android:translationZ="xxx dp" to your XML.
Please note that if you add elevation in views like cardview, you go into the Z axis. And if you want a view come to foreground using this way, you just have to make it higher than the elevation you set.
Example : 6 dp elevation in cardview will require a 7dp in the attribute translationZ of the view you want foreground.

Categories

Resources