【Android】让 Android 界面 “动” 起来:动画知识点大起底

视图动画(View Animation)

视图动画是 Android 早期的动画框架,主要作用于View,通过改变视觉效果实现动画,但不改变 View 的实际属性(如位置、大小)。

1. 补间动画(Tween Animation)

通过定义动画开始和结束的关键帧,系统自动计算中间帧,实现平移、缩放、旋转、透明度等效果。

  • 核心类型(4 种基础动画 + 组合动画):

    • 平移(translate) :改变 View 的视觉位置,参数包括fromXDelta(起始 X)、toXDelta(结束 X)、fromY Delta(起始 Y)、toYDelta(结束 Y)(单位:px 或 % p,% p 表示相对于父容器的百分比)。
    • 缩放(scale) :改变 View 的视觉大小,参数包括fromXScale(起始 X 缩放比)、toXScale(结束 X 缩放比)、fromYScaletoYScale,以及pivotXpivotY(缩放中心点,默认 View 左上角)。
    • 旋转(rotate) :围绕中心点旋转,参数包括fromDegreestoDegreespivotXpivotY(旋转中心)。
    • 透明度(alpha) :改变 View 的透明度,参数fromAlpha(起始透明度,0-1)、toAlpha(结束透明度)。
    • 组合动画(set) :通过<set>标签组合多个动画,可设置ordering(播放顺序:together同时 /sequentially顺序)。
  • 实现方式

    • XML 定义 :在res/anim/目录下创建动画文件(如translate_anim.xml),通过AnimationUtils.loadAnimation(context, R.anim.xxx)加载,再调用view.startAnimation(animation)启动。
    • 代码定义 :直接创建TranslateAnimationScaleAnimation等对象,设置参数后启动。
  • 局限性

    • 仅改变 View 的视觉效果,不修改 View 的实际属性(如lefttop),因此点击事件仍在原位置响应。
    • 仅支持View对象,无法对非 View 对象执行动画

平移

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0%p"
    android:toXDelta="50%p"
    android:fromYDelta="0%p"
    android:toYDelta="0%p"
    android:duration="1000"
    android:fillAfter="true"/>
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ImageView view = findViewById(R.id.iv_target);
        // 加载XML动画
        Animation translateAnim = AnimationUtils.loadAnimation(this, R.anim.translate);
        view.startAnimation(translateAnim);

        // 纯Java实现
        TranslateAnimation translate = new TranslateAnimation(
                0, 500, // X轴起始值和结束值(像素)
                0, 0    // Y轴起始值和结束值
        );
        translate.setDuration(1000);
        translate.setFillAfter(true);
        view.startAnimation(translate);
    }
}

缩放

这个动画会使指定的 View 从原始大小均匀地放大到原来的 2 倍大小,缩放的中心点在 View 的中心位置,整个动画过程持续 1 秒。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="1.0"
    android:toXScale="2.0"
    android:fromYScale="1.0"
    android:toYScale="2.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="1000"/>
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ImageView view = findViewById(R.id.iv_target);
        // 加载XML动画
        Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.scale);
        view.startAnimation(scaleAnim);

        // 纯Java实现

        ScaleAnimation scale = new ScaleAnimation(
                1.0f, 2.0f, // X轴缩放起始值和结束值
                1.0f, 2.0f, // Y轴缩放起始值和结束值
                Animation.RELATIVE_TO_SELF, 0.5f, // 缩放中心点X
                Animation.RELATIVE_TO_SELF, 0.5f  // 缩放中心点Y
        );
        scale.setDuration(1000);
        view.startAnimation(scale);
    }
}

旋转

这个动画会使指定的 View 绕自身的中心点顺时针旋转一周(360 度),动画过程持续 2 秒,并且整个动画会重复播放 10 次。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="2000"
    android:repeatCount="10"/>
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ImageView view = findViewById(R.id.iv_target);
        // 加载XML动画
        Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate);
        view.startAnimation(rotateAnim);

        // 纯Java实现
        RotateAnimation rotate = new RotateAnimation(
                0, 360, // 起始角度和结束角度
                Animation.RELATIVE_TO_SELF, 0.5f, // 旋转中心点X
                Animation.RELATIVE_TO_SELF, 0.5f  // 旋转中心点Y
        );
        rotate.setDuration(2000);
        rotate.setRepeatCount(10); // 重复10次
        view.startAnimation(rotate);
    }
}

透明度

这个动画是指定的 View 在 1 秒内从完全不透明逐渐变为部分透明(透明度为 0.3)。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="1.0"
    android:toAlpha="0.3"
    android:duration="1000"/>
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ImageView view = findViewById(R.id.iv_target);
        // 加载XML动画
        Animation alphaAnim = AnimationUtils.loadAnimation(this, R.anim.alpha);
        view.startAnimation(alphaAnim);

        // 纯Java实现

        AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.3f);
        alpha.setDuration(1000);
        view.startAnimation(alpha);
    }
}

组合动画

整个组合动画的执行过程是:平移动画先开始执行,1 秒后缩放动画开始执行,再过 0.5 秒后旋转动画开始执行,每个动画的持续时间都是 1 秒。

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:repeatCount="5"
    android:repeatMode="restart">

    <translate
        android:fromXDelta="0%p"
        android:toXDelta="50%p"
        android:duration="1000"/>

    <scale
        android:fromXScale="1.0"
        android:toXScale="1.5"
        android:fromYScale="1.0"
        android:toYScale="1.5"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="500"
        android:duration="1000"/>

    <rotate
        android:fromDegrees="0"
        android:toDegrees="90"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="1000"
        android:duration="1000"/>
</set>
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ImageView view = findViewById(R.id.iv_target);
        // 加载XML动画
        Animation setAnim = AnimationUtils.loadAnimation(this, R.anim.combined);
        view.startAnimation(setAnim);

        // 纯Java实现

        AnimationSet set = new AnimationSet(true); // true表示共享插值器

        TranslateAnimation translate = new TranslateAnimation(0, 500, 0, 0);
        translate.setDuration(1000);

        ScaleAnimation scale = new ScaleAnimation(
                1.0f, 1.5f, 1.0f, 1.5f,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f
        );
        scale.setStartOffset(500);
        scale.setDuration(1000);

        RotateAnimation rotate = new RotateAnimation(
                0, 90,
                Animation.RELATIVE_TO_SELF, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f
        );
        rotate.setStartOffset(1000);
        rotate.setDuration(1000);

        set.setRepeatMode(Animation.RESTART);
        set.setRepeatCount(5);
        set.addAnimation(translate);
        set.addAnimation(scale);
        set.addAnimation(rotate);
        view.startAnimation(set);
    }
}

2. 帧动画(Frame Animation)

通过逐帧播放一系列图片实现动画(类似胶片动画),本质是Drawable的一种(AnimationDrawable)。

  • 实现方式

    • XML 定义 :在res/drawable/目录下创建animation-list资源(如frame_anim.xml),每个<item>指定图片和显示时长,通过view.setBackgroundResource(R.drawable.xxx)设置,再强转为AnimationDrawable启动(start())。
    • 代码定义 :直接创建AnimationDrawable,调用addFrame(drawable, duration)添加帧,再启动。
  • 注意事项

    • 图片资源过大易导致OOM(内存溢出),需控制图片数量和分辨率。
    • 必须在onWindowFocusChangedpost(Runnable)中调用start()(避免视图未初始化完成),且在Activity销毁前调用stop(),防止内存泄漏。
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false"> <!-- oneshot 属性:true 表示动画只播放一次,false 表示循环播放 -->

    <!-- 帧 1 -->
    <item
        android:drawable="@drawable/frame1"
        android:duration="200" /> <!-- 每帧显示的持续时间(毫秒) -->

    <!-- 帧 2 -->
    <item
        android:drawable="@drawable/frame2"
        android:duration="200" />

    <!-- 帧 3 -->
    <item
        android:drawable="@drawable/frame3"
        android:duration="200" />

    <!-- 帧 4 -->
    <item
        android:drawable="@drawable/frame4"
        android:duration="200" />

</animation-list>
xml 复制代码
<ImageView
    android:id="@+id/imageView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true" />
java 复制代码
ImageView imageView = findViewById(R.id.imageView);
// 加载帧动画资源
AnimationDrawable frameAnimation = (AnimationDrawable) getResources().getDrawable(R.drawable.frame_animation);
imageView.setImageDrawable(frameAnimation);
// 启动帧动画
frameAnimation.start();

属性动画

API 11(Android 3.0)引入,解决视图动画的局限性,直接修改对象的属性值(包括 View 和非 View 对象),支持更灵活的动画效果。

1. 核心原理

通过不断修改对象的属性值(如xyalpha),并在属性变化时触发视图重绘,实现动画效果。核心是 "值的变化"+"属性的更新"。

2. 核心类

  • ValueAnimator :属性动画的核心,负责计算动画过程中的中间值,通过addUpdateListener监听值变化,手动更新目标属性。

    java 复制代码
    ProgressBar progressBar = findViewById(R.id.progressBar);
    
    ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
    valueAnimator.setDuration(3000);
    
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(@NonNull ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            progressBar.setProgress(value);
        }
    });
    
    valueAnimator.start();
  • ObjectAnimatorValueAnimator的子类,可直接操作对象的属性(需属性有setter方法,若从非默认值开始,还需getter)。

    java 复制代码
    Button button = findViewById(R.id.btn);
    
    // 创建一个 ObjectAnimator,让按钮在 X 轴上从当前位置移动到 200px 的位置
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(button, "translationX", 0f, 200f);
    objectAnimator.setDuration(1000); // 动画持续时间 1000 毫秒
    objectAnimator.start(); // 启动动画
  • AnimatorSet :组合多个AnimatorValueAnimator/ObjectAnimator),支持顺序、并行或延迟执行。

    java 复制代码
    ImageView imageView = findViewById(R.id.imageView);
    ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f)
            .setDuration(1000);
    
    ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(imageView,"Rotation", 0f, 360f)
            .setDuration(1000);
    
    AnimatorSet animatorSet = new AnimatorSet();
    // 动画按顺序
    animatorSet.playSequentially(
            translateAnimator,
            rotateAnimator
    );
    // 动画同时
    animatorSet.playTogether(
            translateAnimator,
            rotateAnimator
    );
    
    animatorSet.start();

3. 关键概念

  • 插值器(Interpolator) :控制动画的速率曲线(如加速、减速),常用系统插值器:

    • LinearInterpolator:匀速。
    • AccelerateInterpolator:加速。
    • DecelerateInterpolator:减速。
    • AccelerateDecelerateInterpolator:先加速后减速。
    • BounceInterpolator:弹跳效果(类似小球落地)。
    • OvershootInterpolator:超过目标值后回弹。
  • 估值器(TypeEvaluator) :计算属性值的具体变化方式,系统提供:

    • IntEvaluator:整数类型。
    • FloatEvaluator:浮点类型。
    • ArgbEvaluator:颜色值(int类型的 ARGB)。
    • 自定义估值器:实现TypeEvaluator<T>接口,如实现坐标点(PointF)的贝塞尔曲线动画。
  • 常用属性

    • duration:动画时长(毫秒)。
    • repeatCount:重复次数(ValueAnimator.INFINITE表示无限重复)。
    • repeatMode:重复模式(RESTART从头开始 /REVERSE反向播放)。
    • startDelay:延迟启动时间(毫秒)。

4. 实现方式

  • 代码实现:推荐,更灵活(如动态计算属性值)。
  • XML 实现 :在res/animator/目录下定义(如object_anim.xml),通过AnimatorInflater.loadAnimator(context, R.animator.xxx)加载。

5. 优势与注意事项

  • 优势

    • 可操作任意对象的任意属性(不仅限于 View)。
    • 真正修改属性值,解决视图动画的点击事件问题。
    • 支持更复杂的动画(如路径动画、颜色渐变)。
  • 注意事项

    • 操作自定义属性时,需确保属性有setter(和getter)方法,或通过包装类间接操作。

    • 避免在动画中频繁触发layoutmeasuredraw(如修改widthheight),优先使用translationX/YscaleX/Yalpha等属性(硬件加速优化)。

    • 动画结束后及时取消(cancel()),避免内存泄漏(尤其在Activity销毁时)。

转场动画(Transition Animation)

API 21(Android 5.0)引入,用于页面(Activity/Fragment)切换或视图集合(ViewGroup)的布局变化时的动画,增强页面跳转的连贯性。

1. 核心概念

  • 场景(Scene) :表示布局的一种状态(如初始状态、目标状态),通过Scene.getSceneForLayout(viewGroup, layoutId, context)创建。

  • 转场(Transition) :定义场景切换时的动画规则,系统提供常用转场:

    • Explode:爆炸效果(视图从中心向外扩散或向内聚集)。
    • Slide:滑动效果(从边缘滑入 / 滑出)。
    • Fade:淡入淡出效果。
    • ChangeBounds:布局变化时,View 的位置和大小变化动画。
    • ChangeTransform:View 的旋转、缩放变化动画。
    • ChangeImageTransform:图片的缩放、裁剪变化动画。

2. 应用场景

  • Activity 转场

    • 普通转场:通过getWindow().setEnterTransition(transition)设置进入 / 退出转场。
    • 共享元素转场:两个 Activity 中相同的元素(如图片)平滑过渡,需在布局中设置android:transitionName,启动时通过ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement, transitionName)传递。
  • Fragment 转场 :类似 Activity,通过setEnterTransition()setSharedElementEnterTransition()等方法设置。

  • 布局变化(Layout Transition) :通过TransitionManager.beginDelayedTransition(viewGroup, transition),在 ViewGroup 的布局变化(如添加 / 移除子 View)时自动应用动画。

矢量动画(AnimatedVectorDrawable, AVD)

用于对矢量图(VectorDrawable)的路径(path)或属性(如fillColor)执行动画,优点是缩放不失真、文件体积小。

1. 组成部分

  • VectorDrawable :定义矢量图(res/drawable/vector.xml),通过<path>标签描述图形轮廓。
  • AnimatedVectorDrawable :关联矢量图和动画(res/drawable/animated_vector.xml),指定需要动画的pathgroup,以及对应的动画资源。
  • 动画定义 :通过ObjectAnimatorValueAnimator定义具体动画(如路径变形、颜色渐变)。

2. 适用场景

简单图标动画(如加载指示器、开关状态切换、菜单展开)。

特殊场景动画

  • RecyclerView Item 动画 :通过ItemAnimator控制 Item 的添加、移除、移动动画,默认使用DefaultItemAnimator,可自定义(继承SimpleItemAnimator)。
  • 列表滚动动画 :Item 随滚动渐入(如alpha从 0 到 1,translationY从 50 到 0),通过RecyclerView.OnScrollListener监听滚动状态,结合ViewPropertyAnimator实现。
  • 下拉刷新动画SwipeRefreshLayout自带刷新动画,可通过setProgressDrawable()自定义。
  • 加载动画 :自定义View绘制循环动画(如圆形进度条、脉冲效果),使用ValueAnimator驱动绘制参数变化。

动画优化与性能

  1. 减少过度绘制 :避免动画视图叠加过多,使用android:clipChildren="false"减少裁剪开销。
  2. 控制动画数量:避免同时运行大量动画,优先保证关键动画流畅。
  3. 硬件加速 :开启硬件加速(android:hardwareAccelerated="true")提升渲染性能,但注意部分绘制操作(如Canvas.clipPath)在硬件加速下可能异常。
  4. 避免耗时操作 :动画回调(如onAnimationUpdate)中不执行耗时任务(如 IO、复杂计算),避免掉帧。
  5. 内存管理 :帧动画及时停止并释放资源,Activity销毁时取消所有动画(animator.cancel())。

兼容性与扩展

  • 兼容性 :API 11 以下使用视图动画,API 11 + 使用属性动画;通过ViewPropertyAnimator(API 12+)简化属性动画调用(view.animate().translationX(100).start())。

  • 第三方库

    • Lottie:Airbnb 开源库,支持 After Effects 导出的动画(JSON 格式),无需手动编写代码,适合复杂动画(如节日主题、广告动画)。
    • Glide:加载 GIF 或视频作为动画,适合简单循环动画。
相关推荐
不一样的少年_4 小时前
女朋友炸了:刚打开的网页怎么又没了?我反手甩出一键恢复按钮!
前端·javascript·浏览器
Asort4 小时前
JavaScript设计模式(十四)——命令模式:解耦请求发送者与接收者
前端·javascript·设计模式
小茴香3534 小时前
Vue 脚手架(Vue CLI)
前端·javascript·vue.js
午安~婉4 小时前
ESLint
前端·eslint·检查
“抚琴”的人4 小时前
C#中获取程序执行时间
服务器·前端·c#
掘金一周4 小时前
Flex 布局下文字省略不生效?原因其实很简单| 掘金一周 10.16
前端
Stringzhua4 小时前
Vue的Axios介绍【9】
前端·javascript·vue.js
摸着石头过河的石头4 小时前
JavaScript 垃圾收集:内存管理的艺术
前端·javascript
前端小崽子4 小时前
🔥 踩坑实录:Fabric 在 Intel Iris Xe 显卡上 CPU 飙升 100%
前端