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
Related
http://developer.android.com/training/animation/layout.html
<LinearLayout
animateLayoutChanges="true">
<View1/>
<View2/>
<View3/>
</LinearLayout>
Let's say View2's height increases at runtime. I would expect View3 to animate downward. Instead, it snaps directly into its new position.
Why does my desired animation not occur?
According to https://developer.android.com/reference/android/animation/LayoutTransition.html
By default, all transition types except CHANGING are enabled.
So if you want to animate when a child changes its size. You can do so by
LayoutTransition layoutTransition = new LayoutTransition();
layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
parentView.setLayoutTransition(layoutTransition);
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);
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.
Say I have a "main screen" Layout which has to move up, to reveal a "history bar".
if ( HistoryBar == false )
{
HistoryBar = true;
TranslateAnimation MainScreenPullUp = new TranslateAnimation(0, 0, 0, -200 );
MainScreenPullUp.setDuration(350);
MainScreenPullUp.setFillAfter(true);
LinLayMain.startAnimation(MainScreenPullUp);
}
Right now I am doing it like that. But the problem is, that while the main screen layout itself moves up, all the button places keep the same, i.e. the button picture moves, but the click-spot remains in place. Any ideas how to fix that?
I tried useing an animationListener, instead setFillAfter(true) , which would TanslateY the main screen layout, once the animation stops, but it makes the layout jump and flicker. Are there any other ways?
edit:
Thanks, Vineet.
Got it working with:
ObjectAnimator pullUp = ObjectAnimator.ofFloat(LinLayMain, "translationY", 0f, -200f);
pullUp.setDuration(350);
pullUp.start();
I faced the same problem. From 3.0 android has property animation. Below 3.0 I tackled this issue with the help of view's method offsetLeftAndRight (int offset) and offsetTopAndBottom (int offset). You can refer this link for more details.
For Property animation refer this link.
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.