将动画逐个应用于多个视图

3

我有一个包含三个视图(buttonViews)的活动,它们以垂直线性布局呈现。我正在动态生成(填充)这些视图。我想要应用一种动画效果,使得在活动启动时,第一个按钮滑入 -> 100毫秒延迟 -> 第二个按钮滑入 -> 100毫秒延迟 -> 第三个按钮滑入。

尝试

我尝试以以下方式实现:

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);
}

结果:

上述方法等待所有视图的延迟结束,最后将所有视图一起动画化。我猜测罪魁祸首是线程。实际上,延迟正在阻止UI线程执行任何动画操作。虽然我可能错了。

我还尝试在内部运行动画代码

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

但那也没起作用。

期望:

有人能帮我实现在buttonView上逐个执行动画吗?谢谢。

5个回答

7

动画是有状态的对象,您不应同时多次使用同一实例。在您的情况下,bottomToTopfadeIn 动画在动画集之间共享。当集合开始时(调用 initialize()),它将设置其子项的起始偏移量。

例如,该方法可能如下所示:

//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);
}

这个并没有给我想要的结果,但答案是我在寻找的。完美而且非常准确。我会等待几天看看是否有其他人想回答或者会标记为正确。谢谢。 - Srujan Barai
@SrujanBarai:这个解决方案对我不起作用。你是怎么解决的?请分享一下。 - user1090751

5
可能可以通过过渡效果 API 轻松解决此问题。在使用以下 XML 声明根布局后:Transitions API 可以帮助你实现平滑的布局变化:
<?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"/>

接着在活动中:

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
    }

}

这段代码将会产生以下输出:

您可以分别调整动画和延迟时间。


如果要实现以下行为:

那么可以使用以下代码:

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
我不想听起来很无礼,但如果我在Java中提出问题,我肯定希望得到Java的答案。无论如何,我感谢你抽出时间运行代码并发布结果。然而,这不会给我想要的结果。您正在onTransitionEnd()内调用addNextButton(),它将等待前一个动画结束。如果我的每个动画持续时间为500,并且希望它们以100毫秒的延迟执行,该怎么办? - Srujan Barai

0
创建一个方法,该方法将接受任意数量的Animation,并依次调用它们。这只是一个例子。
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();
}

或者对于动画,可以加上延迟,也很容易。

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);
     }

}

0

我使用了这段代码,它能够正常工作。 希望能对你有所帮助。

// 首先将你的视图放入一个名为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);
    }

0

可能你需要使用的是AnimatorSet而不是AnimationSet。AnimatorSet API允许您以两种方式编排动画: 1. PlaySequentially 2. PlayTogether 使用以下API:

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

您可以使用以下方法进一步添加动画延迟:

animatorSet.setStartDelay();

请访问完整的 API 文档,链接在这里 https://developer.android.com/reference/android/animation/AnimatorSet

希望这能帮到你!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接