You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现三个TextView依次逐个执行动画?

实现三个TextView顺序执行动画的方案

嘿,这个需求很常见,核心就是监听前一个动画的结束事件,再启动下一个。我给你两种可行的实现方式,适配不同的动画类型:


方法一:利用传统Animation的监听器(适配你现有的XML动画)

你已经写好的XML动画没问题,我们只需要给每个动画绑定AnimationListener,在onAnimationEnd回调里触发下一个TextView的动画即可。

步骤1:确保你的动画XML放在res/anim/目录下(比如命名为slide_anim.xml)

你的动画代码如下:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="-5"
        android:toXDelta="15"
        android:repeatCount="15"
        android:repeatMode="reverse"
        android:interpolator="@android:anim/linear_interpolator"
        android:duration="70" />
</set>

步骤2:在Activity/Fragment中编写逻辑

Java版本:

// 获取三个TextView实例
TextView tv1 = findViewById(R.id.textView1);
TextView tv2 = findViewById(R.id.textView2);
TextView tv3 = findViewById(R.id.textView3);

// 封装动画加载方法,避免重复代码
private Animation getSlideAnimation() {
    return AnimationUtils.loadAnimation(this, R.anim.slide_anim);
}

// 启动第一个动画,并设置监听器
Animation firstAnim = getSlideAnimation();
firstAnim.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {}

    @Override
    public void onAnimationEnd(Animation animation) {
        // 第一个动画结束,启动第二个
        Animation secondAnim = getSlideAnimation();
        secondAnim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {}

            @Override
            public void onAnimationEnd(Animation animation) {
                // 第二个动画结束,启动第三个
                tv3.startAnimation(getSlideAnimation());
            }

            @Override
            public void onAnimationRepeat(Animation animation) {}
        });
        tv2.startAnimation(secondAnim);
    }

    @Override
    public void onAnimationRepeat(Animation animation) {}
});

tv1.startAnimation(firstAnim);

Kotlin版本:

val tv1 = findViewById<TextView>(R.id.textView1)
val tv2 = findViewById<TextView>(R.id.textView2)
val tv3 = findViewById<TextView>(R.id.textView3)

// 封装动画加载方法
private fun getSlideAnimation() = AnimationUtils.loadAnimation(this, R.anim.slide_anim)

// 启动第一个动画
val firstAnim = getSlideAnimation().apply {
    setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationStart(animation: Animation?) {}

        override fun onAnimationEnd(animation: Animation?) {
            // 启动第二个动画
            getSlideAnimation().apply {
                setAnimationListener(object : Animation.AnimationListener {
                    override fun onAnimationStart(animation: Animation?) {}

                    override fun onAnimationEnd(animation: Animation?) {
                        // 启动第三个动画
                        tv3.startAnimation(getSlideAnimation())
                    }

                    override fun onAnimationRepeat(animation: Animation?) {}
                })
                tv2.startAnimation(this)
            }
        }

        override fun onAnimationRepeat(animation: Animation?) {}
    })
}

tv1.startAnimation(firstAnim)

方法二:使用属性动画的AnimatorSet(更简洁,推荐Android 3.0+使用)

如果你不需要兼容老版本,属性动画会更灵活,而且能直接改变View的真实属性(比如点击区域会跟着动画移动)。我们可以用AnimatorSet.playSequentially()直接让三个动画按顺序执行:

Java版本:

TextView tv1 = findViewById(R.id.textView1);
TextView tv2 = findViewById(R.id.textView2);
TextView tv3 = findViewById(R.id.textView3);

// 创建每个TextView的平移动画
ObjectAnimator tv1Anim = ObjectAnimator.ofFloat(tv1, "translationX", -5f, 15f);
tv1Anim.setDuration(70);
tv1Anim.setRepeatCount(15);
tv1Anim.setRepeatMode(ValueAnimator.REVERSE);
tv1Anim.setInterpolator(new LinearInterpolator());

ObjectAnimator tv2Anim = ObjectAnimator.ofFloat(tv2, "translationX", -5f, 15f);
tv2Anim.setDuration(70);
tv2Anim.setRepeatCount(15);
tv2Anim.setRepeatMode(ValueAnimator.REVERSE);
tv2Anim.setInterpolator(new LinearInterpolator());

ObjectAnimator tv3Anim = ObjectAnimator.ofFloat(tv3, "translationX", -5f, 15f);
tv3Anim.setDuration(70);
tv3Anim.setRepeatCount(15);
tv3Anim.setRepeatMode(ValueAnimator.REVERSE);
tv3Anim.setInterpolator(new LinearInterpolator());

// 按顺序执行三个动画
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(tv1Anim, tv2Anim, tv3Anim);
animatorSet.start();

Kotlin版本:

val tv1 = findViewById<TextView>(R.id.textView1)
val tv2 = findViewById<TextView>(R.id.textView2)
val tv3 = findViewById<TextView>(R.id.textView3)

// 构建每个动画
val tv1Anim = ObjectAnimator.ofFloat(tv1, "translationX", -5f, 15f).apply {
    duration = 70
    repeatCount = 15
    repeatMode = ValueAnimator.REVERSE
    interpolator = LinearInterpolator()
}

val tv2Anim = ObjectAnimator.ofFloat(tv2, "translationX", -5f, 15f).apply {
    duration = 70
    repeatCount = 15
    repeatMode = ValueAnimator.REVERSE
    interpolator = LinearInterpolator()
}

val tv3Anim = ObjectAnimator.ofFloat(tv3, "translationX", -5f, 15f).apply {
    duration = 70
    repeatCount = 15
    repeatMode = ValueAnimator.REVERSE
    interpolator = LinearInterpolator()
}

// 顺序执行
AnimatorSet().apply {
    playSequentially(tv1Anim, tv2Anim, tv3Anim)
    start()
}

两种方法各有优劣:

  • 传统视图动画(方法一):适合兼容老版本,代码和你的XML动画完全匹配,但不会改变View的真实位置,如果TextView需要响应点击,可能会有问题。
  • 属性动画(方法二):更现代,能改变View的真实属性,交互更准确,代码也更简洁,推荐优先使用。

内容的提问来源于stack exchange,提问作者joker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:40:02