3

I have an activity with 3 views (buttonViews) in a vertical linear layout. I am generating (inflating) these views dynamically. I want to apply an animation such that, on activity start, the first buttons slide in -> 100 ms delay -> second button slide in -> 100 ms delay -> Third button slide in.

Attempt

I tried implementing it in this way:

private void setMainButtons() {
    ArrayList<String> dashboardTitles = DashboardUtils.getDashboardTitles();
    ArrayList<Integer> dashboardIcons = DashboardUtils.getDashboardIcons();

    final ViewGroup root = findViewById(R.id.button_container);

    for (int i = 0; i < (dashboardTitles.size() < dashboardIcons.size() ? dashboardTitles.size() : dashboardIcons.size()); i++){
        final View buttonView = DashboardButtonInflater.getDashboardButton(root, dashboardTitles.get(i), dashboardIcons.get(i), this);
        if (buttonView == null) continue;
        buttonView.setOnClickListener(this);
        root.addView(buttonView);
        animateBottomToTop(buttonView, (long) (i*50)); // Calling method to animate buttonView
    }
}

//The function that adds animation to buttonView, with a delay.
private void animateBottomToTop(final View buttonView,long delay) {
    AnimationSet animationSet = new AnimationSet(false);
    animationSet.addAnimation(bottomToTop);
    animationSet.addAnimation(fadeIn);
    animationSet.setStartOffset(delay);
    buttonView.setAnimation(animationSet);
}

Result:

The above method waits for the total delay of all the views and at the end, aminates all the views together. I can guess the culprit here is the thread. The dealy is actually stopping the UI thread from doing any animation. I could be wrong though.

I also tried running the animation code inside

new Thread(new Runnable(){...}).run()

but that didn't work either.

Expectations:

Can somebody help me achieve the one-by-one animation on buttonView? Thank you.

5 Answers 5

7
+25

Animations are statefull objects, you should not use the same instance multiple times simultaneously. In your case the bottomToTop and fadeIn animations are shared between the animation sets. When the set starts (initialize() is called) it will set the start offset of its children.

For example the method could look like :

//The function that adds animation to buttonView, with a delay.
private void animateBottomToTop(final View buttonView,long delay) {
    AnimationSet animationSet = new AnimationSet(false);
    // create new instances of the animations each time
    animationSet.addAnimation(createBottomToTop());
    animationSet.addAnimation(createFadeIn());
    animationSet.setStartOffset(delay);
    buttonView.setAnimation(animationSet);
}
Sign up to request clarification or add additional context in comments.

2 Comments

This didn't give the result I wanted but the answer is what I was looking for. Perfect and very accurate. I will wait for a couple of days if anybody else wants to answer or will mark this correct. Thank you.
@SrujanBarai: This solution is not working for me. How did you solved it? Please share
5

The problem might be easily solved with Transitions API. Having declared a root layout with this xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/content_frame"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"/>

Then inside activity:

class MainActivity : AppCompatActivity() {

    lateinit var content: LinearLayout
    private var counter = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        content = findViewById(R.id.content_frame)
        // wait this view to be laid out and only then start adding and animating views
        content.post { addNextChild() }
    }

    private fun addNextChild() {
        // terminal condition
        if (counter >= 3) return
        ++counter

        val button = createButton()
        val slide = Slide()
        slide.duration = 500
        slide.startDelay = 100
        slide.addListener(object : TransitionListenerAdapter() {
            override fun onTransitionEnd(transition: Transition) {
                addNextChild()
            }
        })
        TransitionManager.beginDelayedTransition(content, slide)
        content.addView(button)
    }

    private fun createButton(): Button {
        val button = Button(this)
        button.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        button.text = "button"
        return button
    }

}

This chunk of code will result in following output:

You can adjust animation and delay times respectively.


If you want following behavior:

Then you can use following code:

class MainActivity : AppCompatActivity() {

    lateinit var content: LinearLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        content = findViewById(R.id.content_frame)
        content.post { addChildren() }
    }

    private fun addChildren() {
        val button1 = createButton()
        val button2 = createButton()
        val button3 = createButton()

        val slide1 = Slide()
        slide1.duration = 500
        slide1.addTarget(button1)

        val slide2 = Slide()
        slide2.duration = 500
        slide2.startDelay = 150
        slide2.addTarget(button2)

        val slide3 = Slide()
        slide3.duration = 500
        slide3.startDelay = 300
        slide3.addTarget(button3)

        val set = TransitionSet()
        set.addTransition(slide1)
        set.addTransition(slide2)
        set.addTransition(slide3)

        TransitionManager.beginDelayedTransition(content, set)
        content.addView(button1)
        content.addView(button2)
        content.addView(button3)
    }

    private fun createButton(): Button {
        val button = Button(this)
        button.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
        button.text = "button"
        return button
    }
}

1 Comment

I do not want to sound rude but if I ask a question in Java, I definitely expect an answer in Java. Anyways, I appreciate you to have taken time to actually run the code and post the results. Tho, this won't give the results I wanted. You are calling the addNextButton() inside the onTransitionEnd() which will wait for previous anim to end. What if my duration of each anim is 500 and want them to execute at a delay of 100 ms?
0

Create method, which will accept Any number of Animation to invoke one after another. Just as example.

private void playOneAfterAnother(@NonNull Queue<Animation> anims) {
     final Animation next = anims.poll();

     /* You can set any other paramters, 
     like delay, for each next   Playing view, if any of course */

     next.addListener(new AnimationListener() {
            @Override
            public void onAnimationEnd(Animator a) {
                if (!anim.isEmpty()) {
                    playOneAfterAnother(anims);
                }
            }
            @Override
            public void onAnimationStart(Animator a) {
            }
            @Override
            public void onAnimationCancel(Animator a) {
            }
            @Override
            public void onAnimationRepeat(Animator a) {
            }
        });
     next.play();
}

Or with delay for animations, it's easy too.

private void playOneAfterAnother(@NonNull Queue<Animation> anims, 
                  long offsetBetween, int nextIndex) {
     final Animation next = anims.poll();

     /* You can set any other paramters, 
     like delay, for each next   Playing view, if any of course */

     next.setStartOffset(offsetBetween * nextIndex);
     next.play();

     if (!anim.isEmpty()) {
         playOneAfterAnother(anims, 
              offsetBetween, nextIndex +1);
     }

}

Comments

0

Probably, what you need to use is AnimatorSet instead of AnimationSet. The AnimatorSet API allows you to choreograph animations in two ways: 1. PlaySequentially 2. PlayTogether using the apis:

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(anim1, anim2, anim3, ...);
animatorSet.playTogether(anim1, anim2, anim3, ...);

You can further add delays to your animation using

animatorSet.setStartDelay();

Visit the complete API docs here https://developer.android.com/reference/android/animation/AnimatorSet

Hope this helps!

Comments

0

I Use This Code And its work correctly Hope to help you

// first put your views in an array with name arrayViews

 final int[] time = {0};
    for (int i = 0; i < arrayViews.size(); i++) {
        Animation zoom = AnimationUtils.loadAnimation(this, R.anim.fade_in);
        zoom.setDuration(250);
        zoom.setStartOffset(time[0] += 250);
        arrayViews.get(i).startAnimation(zoom);
        arrayViews.get(i).setVisibility(View.VISIBLE);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.