68

I am changing the left margin of an image view in the following manner :

ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) image.getLayoutParams ();
layoutParams.leftMargin = VALUE;
image.setLayoutParams ( layoutParams );

I would like the change in margin to apply with animation. Any clues ?

What I tried :

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat ( image , "x" , VALUE);
objectAnimator.start();

This works perfectly, as the image is moved to the specified X value with animation, HOWEVER the value of layoutParams.leftMargin remains unchanged !! So I cannot use this method, because if I try to change the value of layoutParams.leftMargin to 100 after using the objectAnimator with the value 100, the value applied is not correct ( 200 is applied instead of 100, the effect if the objectAnimator remains eventhough I am setting the left margin in the following manner :

layoutParams.leftMargin = 100;
Leeeeeeelo
  • 4,333
  • 3
  • 34
  • 44

5 Answers5

142

Use Animation class, not ObjectAnimator.

final int newLeftMargin = <some value>;
Animation a = new Animation() {

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        LayoutParams params = yourView.getLayoutParams();
        params.leftMargin = (int)(newLeftMargin * interpolatedTime);
        yourView.setLayoutParams(params);
    }
};
a.setDuration(500); // in ms
yourView.startAnimation(a);

Please note that you should use correct LayoutParams class i.e. if your view is the child of LinearLayout then params should be LinearLayout.LayoutParams

ofalvai
  • 316
  • 6
  • 13
user1991679
  • 2,109
  • 1
  • 23
  • 19
  • 14
    Don't forget to set the duration, I had some issues with it until I set the duration: `a.setDuration(duration);` – Brandon Romano May 14 '13 at 15:06
  • 1
    I get an error when I try to use params.leftMargin . Apparently it's not a field – Soroush Mar 28 '14 at 23:52
  • 2
    It's because there are a lot of LayoutParams classes. For example - ViewGroup.LayoutParams, FrameLayout.LayoutParams, LinearLayout.LayoutParams, etc. ViewGroup.LayoutParams doesn't have leftMargin field but RelativeLayout.LayoutParams does. – user1991679 Mar 31 '14 at 11:55
  • 1
    According to http://developer.android.com/reference/android/view/animation/Animation.html#Animation(), the default duration is 0ms, so you must set duration as well. – Sean Barbeau Oct 03 '14 at 15:57
  • For packaged Animation implementations and XML resource animations, default duration should be 300ms, right? Anyone have a reference to that? – milosmns Jan 04 '15 at 15:15
  • You can use android.R.integer.config_shortAnimTime. But, I do believe that the value of this variable can differ on different devices or android versions. – user1991679 Mar 18 '15 at 12:18
  • 1
    What if I wanted newLeftMargin to be 0 ? – Ashkan Sarlak May 20 '18 at 23:12
  • Also this is not accounting for pixel density – Neorcisa Nov 21 '18 at 18:16
  • Magic! replace this to make smoother animation. params.leftMargin = (int) (params.leftMargin + (newLeftMargin - params.leftMargin) * interpolatedTime); – Javad Sep 23 '19 at 15:52
98

I came by this question, but I couldn't use it because I want to animate the margin from a negative value to 0, so I used valueAnimater base on user1991679 answer:

final View animatedView = view.findViewById(R.id.animatedView);
final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) animatedView.getLayoutParams();
ValueAnimator animator = ValueAnimator.ofInt(params.bottomMargin, 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator)
    {
        params.bottomMargin = (Integer) valueAnimator.getAnimatedValue();
        animatedView.requestLayout();
    }
});
animator.setDuration(300);
animator.start();

You must change LinearLayout.LayoutParams according to animatedView container. Also you can use nineoldandroids for older version that don't have ValueAnimator.

Ali
  • 21,572
  • 15
  • 83
  • 95
  • You can animate the margin from negative to 0 using Animation class. There are no any limitation. – user1991679 May 20 '14 at 15:10
  • @user1991679 and how do you animate from say 400 to 200 using the `Animation` class? – StuStirling Jul 31 '14 at 10:47
  • 4
    Assume that 200 is newMargin (the margin we should animate to) and 400 is currentMargin (the marging we animete from). In that case the code would look like: @Override protected void applyTransformation(float interpolatedTime, Transformation t) { LayoutParams params = yourView.getLayoutParams(); params.leftMargin = currentMargin - (int)((currentMargin - newMargin) * interpolatedTime); yourView.setLayoutParams(params); } – user1991679 Aug 01 '14 at 08:39
  • 1
    I get strange redraws with this. Looks like my layout bobbles a lot. – Michael Apr 07 '15 at 14:14
  • @MikeMilla Use it inside a FrameLayout, LinearLayout produces bad effect. – Ali Apr 07 '15 at 14:57
  • @Ali I was :/ Ended up using the answer above. – Michael Apr 07 '15 at 15:28
17

The answer from user1991679 is great, but if you need to interpolate a margin from any other value but 0, you need to use it in your calculations:

ViewGroup.MarginLayoutParams params = (MarginLayoutParams) mBottomLayout.getLayoutParams();
final int bottomMarginStart = params.bottomMargin; // your start value
final int bottomMarginEnd = <your value>; // where to animate to
Animation a = new Animation() {
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        ViewGroup.MarginLayoutParams params = (MarginLayoutParams) mBottomLayout.getLayoutParams();
        // interpolate the proper value
        params.bottomMargin = bottomMarginStart + (int) ((bottomMarginEnd - bottomMarginStart) * interpolatedTime);
        mBottomLayout.setLayoutParams(params);
    }
};
a.setDuration(300);
mBottomLayout.startAnimation(a);

In my case I needed to animate an "enter the screen" animation, coming from "-48dp" to 0. Without the start value, the animation is always 0, thus jumping, not animating the view. The solution was to interpolate the offset and add it to the original value.

Björn Kechel
  • 7,933
  • 3
  • 54
  • 57
  • I tried this. And while the result is the correct position, it doesn't do the animation anymore. Any idea? If I go back to the non calculated thing the animation works fine.. – HideAndSeek Aug 16 '18 at 10:45
  • You can set a break point when directly after the start point is set, so you can see what is entered. If it jumps, it would mean, that you are assigning the wrong value. In my code snippet, the bottomMarginStart is read from the view, assuming that the view should start the animation where it is. – Björn Kechel Aug 17 '18 at 12:49
0

Nothing worked for me like I wanted so... I needed to create ToggleMenu from -80 dp (oldLeftMargin) to 0dp. Same with bottomMargin, etc. Now it works:

final int oldLeftMargin = (int) getResources().getDimension(R.dimen.left_menu_button_margin_left);
    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) llMeniuToggle.getLayoutParams();
            params.leftMargin = oldLeftMargin + (int) ((0 - oldLeftMargin) * interpolatedTime);
            llMeniuToggle.setLayoutParams(params);
        }
    };
    a.setDuration(500);
    llMeniuToggle.startAnimation(a);
Marina
  • 317
  • 4
  • 11
-3

You can use the following

image.animate().setDuration(durationIn).translationXBy(offsetFloat).start();

You can also add .setInterpolator(new BounceInterpolator()) to change the look of the animation.

Martin Evans
  • 45,791
  • 17
  • 81
  • 97
Aleksei T
  • 11
  • 1