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.
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);
}
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With