I'm trying to animate the height of a ConstraintLayout using the Animation class, and the setDuration method doesn't seem to be working. The height is just instantly changed to the desired height value. I've seen posts about animations being disabled in the developer options, but that's not the problem, it's set to 1x. Anyway, code is below:
public static void scaleUp() {
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) profileLayout.getLayoutParams();
// modify your layout params here
params.height = 10;
profileLayout.setLayoutParams(params);
}
};
animation.setDuration(300); // in ms
profileLayout.startAnimation(animation);
}
Instead of params.height = 10; use params.height = (int)(10.0 * interpolatedTime).
Append profileLayout.requestLayout(); at the end of your applyTransformation method. This is used to update the layout on a screen.
Possible Solutions:
Answered Here
Medium Article About Animating
Related
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ViewGroup.LayoutParams params = slidingPane.getLayoutParams();
int calculatedHeight = expandedHeight - ((int) (expandedHeight * interpolatedTime));
if (calculatedHeight <= collapsedHeight) {
calculatedHeight = collapsedHeight;
}
params.height = calculatedHeight;
slidingPane.setLayoutParams(params);
slidingPane.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
animation.setDuration(ANIMATION_DURATION);
animation.setAnimationListener(animationListener);
slidingPane.startAnimation(animation);
SlidingPane is a LinearLayout and it has a ListView as a child. ListView contains images in every row.
Now this code is working absolutely fine, but animation is not smooth. If I remove images from listview then it works fine.
I have already tried following things based on the answers on SO on similar questions -
1. Hide the content of sliding pane before animation and then again make them visible on animation end. It helps but behavior looks very odd
2. Set android:animateLayoutChanges="true" in xml, but its not animating anything
Is there anyway to solve this problem?
Instead of manually changing the height on each animation step, you should try to use the more systematic animation, like ValueAnimator, or ObjectAnimator:
slidingPane.animate().scaleY(0f).setInterpolator(new AccelerateDecelerateInterpolator()).setDuration(ANIMATION_DURATION);
You can use pivotY/pivotY on the view to control the anchor point of the scale animation.
Related docs:
https://developer.android.com/reference/android/animation/ValueAnimator.html
https://developer.android.com/reference/android/animation/ObjectAnimator.html
https://developer.android.com/guide/topics/graphics/prop-animation.html
Is it possible to animate a layout_weight change of a View?
I have tried:
ObjectAnimator anim = ObjectAnimator.ofFloat(headerRoot,
"layout_weight", ws, 1f);
anim.setDuration(1500);
anim.addUpdateListener(this);
anim.start();
This has no effect on my layout. Is the objectAnimator able to manipulate the layout_weight property of a view?
Just use a class provided here: https://stackoverflow.com/a/20334557/1763138
private class ExpandAnimation extends Animation {
private final float mStartWeight;
private final float mDeltaWeight;
public ExpandAnimation(float startWeight, float endWeight) {
mStartWeight = startWeight;
mDeltaWeight = endWeight - startWeight;
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContent.getLayoutParams();
lp.weight = (mStartWeight + (mDeltaWeight * interpolatedTime));
mContent.setLayoutParams(lp);
}
#Override
public boolean willChangeBounds() {
return true;
}
}
and change mContent to your headerRoot
This has no effect on my layout. Is the objectAnimator able to manipulate the layout_weight property of a view?
No, it is not. ObjectAnimator uses the setter method of an object to update its value. A LayoutParam has not setWeight method, it only has the weight property you can change.
It's all explained here: http://developer.android.com/guide/topics/graphics/prop-animation.html#object-animator.
Here's the animation:
public class WidthAnimation extends Animation {
protected final int originalWidth;
protected final View view;
protected float perValue;
public WidthAnimation(View view, int fromWidth, int toWidth) {
this.view = view;
this.originalWidth = fromWidth;
this.perValue = (toWidth - fromWidth);
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
view.getLayoutParams().width = (int) (originalWidth + perValue * interpolatedTime);
view.requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
}
When called by this (animating the View to be zero width), it works fine:
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, getWindowWidthInPixels(), 0);
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
But when called by this (animating the View to being displayed), applyTransform is not called, and the animation is not shown:
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, 0, getWindowWidthInPixels());
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
Both animations are being triggered by screen clicks. The getWindowWidthInPixels() method works correctly. I've seen several other questions on SO that suggest calling invalidate() or requestLayout() on the View, or its parent, can resolve this, but for me those solutions do not work.
Wow, that is weird, that it would work to shrink but not grow. Maybe Android is discarding the startAnimation call because the view is width 0. I suppose one quick fix would be to set the width to 1 before starting the grow animation.
dashboardContainerView.getLayoutParams().width = 1;
WidthAnimation widthAnim = new WidthAnimation(dashboardContainerView, 0, getWindowWidthInPixels());
widthAnim.setDuration(500);
dashboardContainerView.startAnimation(widthAnim);
Another option would be to switch to ObjectAnimator. My animations got a lot more straightforward when I did that. Yours would look something like this:
ObjectAnimator anim = ObjectAnimator.ofInt(this, "dashboardWidth", fromWidth, toWidth);
anim.setDuration(500);
dashboardContainerView.startAnimation(anim);
public void setDashboardWidth(int width) {
ViewGroup.LayoutParams params = dashboardContainerView.getLayoutParams();
params.width = width;
dashboardContainerView.setLayoutParams(params);
}
That is the pattern I usually use with success. I don't have to call requestLayout in that case, but maybe setLayoutParams is doing that. Maybe just the getLayoutParams and requestLayout that you are using would be fine too.
Anyway, I really like the ObjectAnimator because it is so simple. One thing you do have to watch out for is if you are using ProGuard, you have to make sure it doesn't clean out the setDashboardWidth method, because it is not directly called in your code. It is called from within ObjectAnimator, which finds the method using reflection. Thus the exact name and signature of the method has to match the "dashboardWidth" property name in the call to ofInt.
I am trying to achieve the effect of a piece of paper coming out of a slot with a list of items on it. I came up with one approach but as I am working on it I can't keep on thinking there must be a better way.
My approach:
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/orderDetailListView">
</ListView>
</LinearLayout>
Animation code:
final int newMargin = <some value>;
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LayoutParams params = listView.getLayoutParams();
params.leftMargin = (int)(newMargin * interpolatedTime);
yourView.setLayoutParams(params);
}
};
listView.startAnimation(a);
My question:
Is there a better way? I am not asking a suggestive question I merely want to know if there is a built in function of some sort that will make a view appear gradually from a set point.
It seems like you are trying to achieve a simple translation animation. One way of approaching this situation is to use the Property Animation Api introduced in Api level 10 (and generously backported to api level 1 with Jake Wharton's nine old androids library).
A simple example could be the following
ObjectAnimator.ofFloat(theViewObject, "translationY", -theViewObject.getHeight(), 0)
.setDuration(2000) // 2 seconds
.start();
sometimes, calls like getHeight() or getRight() etc, return 0. This is because the view hasn't actually been drawn yet. To accomodate for this you can register a callback to know when the view has been drawn.
// Listen for when the view has been drawn so we can get its dimensions
final ViewTreeObserver viewTreeObserver = theViewObject.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
ObjectAnimator.ofFloat(
theViewObject, "translationY", -theViewObject.getHeight(), 0)
.setDuration(2000) // 2 seconds
.start();
// stop listening for updates to the view so that this is only called once
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewTreeObserver.removeOnGlobalLayoutListener(this);
} else {
viewTreeObserver.removeGlobalOnLayoutListener(this);
}
}
});
}
I have a menu I am trying to animate. I am splitting a menu by changing margins
and inserting a new menu. When I want to insert the menu, the animation is:
level3Height = level3Frame.getHeight();
final int newBottomMargin = (int)(origBottomMargin + level3Height/2);
final int newTopMargin = (int)(origTopMargin + level3Height/2);
splitUp = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(newBottomMargin * interpolatedTime);
btnShopWireless.setLayoutParams(params);
}
};
joinDown = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(origBottomMargin * interpolatedTime);
btnShopWireless.setLayoutParams(params);
}
};
splitUp.setDuration(1000);
splitUp.setInterpolator(new BounceInterpolator());
joinDown.setDuration(500);
joinDown.setInterpolator(new BounceInterpolator());
After getting the height of the inserted menu, the animation moves the Views upward very nicely:
btnShopWireless.startAnimation(splitUp);
All that works great! But....
When I want to remove the inserted level menu and move things back down I use the below, and the animation doesn't happen - the Views simply slam back into
their original place with no smooth motion.
btnShopWireless.startAnimation(joinDown);
I have AnimationListeners set up to setVisibility to VISIBLE onAnimationStart and also setVisibility to GONE onAnimationEnd. They are doing their job, so I know the animation is getting invoked or the visibility would never occur within the AnimationListeners for joinDown. But the animated movement backdown just never happens. I can only animate the first one, splitUp.
Anyone have any clues as to what I am missing in order to get the second animation working?
I figured out what my problem was - pure IQ underflow.
The originalMargin was 0. I increased them to about 190dp to fit in a 3rd menu. When it time to bring the menu together and remove the 3rd menu, the transformation used the original margins again multiplied by the time value. No matter how much time or which interpolator, the originalMargin*interpolatedTime would always be 0.
To fix it, I used this calculation:
joinDown = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) btnShopWireless.getLayoutParams();
params.bottomMargin = (int)(newBottomMargin -(level3Height/2 * interpolatedTime));
btnShopWireless.setLayoutParams(params);
}
};