25

Is there any way to animate multiple views at the same time?

What I want to do is translate animations:

I have 5 TextViews and 4 coloured strips (plain RelativeLayouts with a background). At the start of the animations, the stips are stacked with the TextViews in a horizontal row. At the end I want all the TextViews stacked between the strips:

enter image description here

This is a very simple drawing, but it demonstrates what I want to do. Is there any way of doing this with animations, or do I have to use canvas animations.

Matthew
  • 4,832
  • 2
  • 29
  • 55

4 Answers4

59

You can use the ObjectAnimator for animating the multiple view as follows:

ArrayList<ObjectAnimator> arrayListObjectAnimators = new ArrayList<ObjectAnimator>(); //ArrayList of ObjectAnimators

ObjectAnimator animY = ObjectAnimator.ofFloat(view, "y", 100f);
arrayListObjectAnimators.add(animY);

ObjectAnimator animX = ObjectAnimator.ofFloat(view, "x", 0f);
arrayListObjectAnimators.add(animX);
...
ObjectAnimator[] objectAnimators = arrayListObjectAnimators.toArray(new ObjectAnimator[arrayListObjectAnimators.size()]);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(objectAnimators);
animSetXY.setDuration(1000);//1sec
animSetXY.start();
Pradeep
  • 1,043
  • 7
  • 12
  • 2
    The only downside is that it is new since version 11 (honeycomb) – Matthew Jan 13 '13 at 17:03
  • 3
    Use nineoldandroids for to bring the API all the way back to 1.0 (http://nineoldandroids.com/) – Tushar Feb 15 '13 at 02:55
  • 3
    This not animate several objects at once, but run several animations at once at same object. – Renascienza Nov 12 '14 at 21:45
  • 1
    I downloaded NineOldAndroids, the library Tushar mentioned, and was about to use it but then found that its readme file notified of the library's deprecation, so I thought I should share it here: "NineOldAndroids is deprecated. No new development will be taking place. Existing versions will (of course) continue to function. New applications should use `minSdkVersion="14"` or higher which has access to the platform animation APIs." – RestInPeace Apr 14 '15 at 07:07
  • Just to add.. you can also avoid the ArralyList and just use playTogher(animY, animY1) for example. – Codeversed Nov 30 '15 at 16:44
7

You can use AnimationSet

AnimatorSet decSet2 = new AnimatorSet();
        decSet2.playTogether(
                ObjectAnimator.ofFloat(view, "x",dX),
                ObjectAnimator.ofFloat(view, "y",dY),
                ObjectAnimator.ofFloat(mTextCancel, "x",dX),
                ObjectAnimator.ofFloat(mTextCancel, "y", dY),
                ObjectAnimator.ofArgb(mBtnOne, "visibility", View.VISIBLE, View.GONE),
                );
        decSet2.setDuration(0);
        decSet2.start();
Nilesh Deokar
  • 2,975
  • 30
  • 53
7

Create your animation objects, then use startAnimation collectively on all views at the same time. So it would be something like this:

TranslateAnimation anim1;
TranslateAnimation anim2;
TranslateAnimation anim3;

// Setup the animation objects

public void startAnimations()
{
   //... collect view objects
   view1.startAnimation(anim1);
   view2.startAnimation(anim2);
   view3.startAnimation(anim3);
}

Just note that the more animations you have going on at once, the slower it's going to be.

Ranjithkumar
  • 16,071
  • 12
  • 120
  • 159
DeeV
  • 35,865
  • 9
  • 108
  • 95
  • 1
    Won't this cause them to be out of sync (not essential here), especially the ones that are started later? – Matthew Feb 09 '12 at 19:58
  • 1
    Not really. Basically what happens is when you use `startAnimation` on a view, it starts invalidating itself until it's reached the desired location. When you call it on all the views at once, they'll all call `invalidate()` on themselves, then on the next drawing pass each view will be drawn in their next frame. Since you're calling all this on the UI thread anyway, none of the animations will start until you've returned from that method. NOTE: Using the pre-Honeycomb animation framework merely moves the visual portion of the view. The user won't be able to click on it. – DeeV Feb 09 '12 at 20:10
  • I apply the animations with anim1.setFillAfter(true); and then to actually move the view by using layout params? (I should just try it, but its late :) – Matthew Feb 09 '12 at 20:19
  • That's generally how it's done, yes. It can be cumbersome though. I've, personally, never had to physically move views after animating them. I believe though with translate animations, you adjust the margins according to the animation, so if you were to move left 30dp, you take whatever marginRight is then add 30dp to it. – DeeV Feb 09 '12 at 20:26
  • 1
    Since you are starting all these animations on the main thread and animations execute on the main thread's cycles too so all animations would start at the same time. – Sharique Abdullah Apr 13 '14 at 16:47
3

I've managed to share a single Animation instance among several Views. At least with an AlphaAnimation. I had a ListView and an animation that should be applied to a particular child of all list item views. In my case it should have been possible for the views to 'join' and 'leave' the shared animation at any time and it should not affect other animated views in any way or interfere with already running animation. To achieve this I had to make an adjusted copy of android's stock AlphaAnimation. My use case is rather special, but let it be here just in case someone has to deal with similar goal with ListView.

/**
 * This class is a copy of android's stock AlphaAnimation with two adjustments:
 * - fromAlpha, toAlpha and duration are settable at any time.
 * - reset() method can be blocked. Reason: view.setAnimation() calls animation's reset() method
 * which is not intended in our use case.
 * For every new list item view we call setAnimation once for it's life time
 * and animation should not be reset because it is shared by all list item views and may be in progress. 
 */
public class MutableAlphaAnimation extends Animation {
    private float mFromAlpha;
    private float mToAlpha;
    private boolean resetBlocked;

    public MutableAlphaAnimation() {
    }

    public void start(float fromAlpha, float toAlpha, long duration) {
        mFromAlpha = fromAlpha;
        mToAlpha = toAlpha;
        setDuration(duration);
        setStartTime(START_ON_FIRST_FRAME);
    }

    public void setResetBlocked(boolean resetBlocked) {
        this.resetBlocked = resetBlocked;
    }

    @Override
    public void reset() {
        if (! resetBlocked) super.reset();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float alpha = mFromAlpha;
        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
    }

    @Override
    public boolean willChangeTransformationMatrix() {
        return false;
    }

    @Override
    public boolean willChangeBounds() {
        return false;
    }
}

To set this animation to a view:

            animation.setResetBlocked(true);
            view.setAnimation(animation);
            animation.setResetBlocked(false);

And to start an animation (previously set by setAnimation) TWO things must be done:

        animation.start(0.0f, 1.0f, FADE_IN_DURATION);

And after that you must manually call invalidate() on every view that is affected by the animation.

The usual startAnimation() does invalidate() for you, but setAnimation doesn't. (read the comment on View.setAnimation() method in android's sources).

Gena Batsyan
  • 736
  • 5
  • 14