Android动画合集之属性动画-初见

Android 动画合集之属性动画 – 初见

属性动画(Property Animation)是 Android 3.0(API 11)引入的一种强大而灵活的动画机制,允许开发者通过动态修改对象的属性值(如 View 的位置、透明度、旋转角度等)实现平滑的动画效果。相比传统的补间动画(Tween Animation),属性动画可以直接改变对象的实际属性,而不仅仅是视觉效果,因此更适合复杂的交互式动画场景。作为 Android 动画合集的一部分(前文已覆盖帧动画和补间动画),本文将介绍属性动画的基础概念、核心类、基本用法及简单示例,适合初次接触属性动画的开发者。后续可深入探讨高级用法(如插值器、组合动画)。

属性动画的原理与作用

  • 原理:属性动画通过定期更新对象的属性值(如 View 的 translationX 或自定义对象的字段),结合插值器(Interpolator)和时间轴,生成平滑的过渡效果。它基于 ValueAnimatorObjectAnimator 类,核心是通过反射或 setter 方法动态修改属性值。
  • 作用:提供通用、灵活的动画框架,支持任何对象的属性动画(不仅限于 View),并能同步更新对象的实际状态(如布局位置),适合交互性强的场景。
  • 应用场景
  • View 的平移、缩放、旋转、透明度变化。
  • 自定义对象属性的动画(如颜色渐变、进度条值)。
  • 复杂 UI 交互(如拖拽释放动画、页面切换过渡)。
  • 结合 ViewPropertyAnimator 实现简洁的 View 动画。

属性动画的核心类

属性动画系统主要依赖以下类:

类名描述主要用途
ValueAnimator核心类,生成数值序列(int/float/Object),需手动监听值变化并应用。通用数值动画
ObjectAnimatorValueAnimator 的子类,自动更新指定对象的属性值(如 View 的 alpha)。针对对象的属性动画
AnimatorSet组合多个动画,支持顺序或同时播放。复杂动画组合
PropertyValuesHolder保存单个属性的动画值和设置器,用于多属性动画。同时动画多个属性
ViewPropertyAnimatorView 的便捷动画接口,简化常见属性动画(如 view.animate().alpha(0f))。快速 View 动画

属性动画的基本用法

属性动画可以通过 代码动态创建XML 定义(较少使用)实现。以下重点介绍代码方式,XML 示例将在最后补充。

1. 使用 ValueAnimator

ValueAnimator 生成数值序列,开发者需监听变化并手动应用到目标对象。

  • 代码示例
  import android.animation.ValueAnimator;
  import android.os.Bundle;
  import android.view.View;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;

  public class PropertyAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_property_animation);

          ImageView imageView = findViewById(R.id.image_view);

          // 创建 ValueAnimator,从 0 到 100
          ValueAnimator animator = ValueAnimator.ofFloat(0f, 100f);
          animator.setDuration(1000); // 持续 1 秒
          animator.addUpdateListener(animation -> {
              float value = (float) animation.getAnimatedValue(); // 获取当前值
              imageView.setTranslationX(value); // 应用到 View 的 X 平移
          });

          // 按钮触发
          findViewById(R.id.start_button).setOnClickListener(v -> animator.start());
      }
  }
  • 布局 XML(res/layout/activity_property_animation.xml):
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:gravity="center"
      android:orientation="vertical">
      <ImageView
          android:id="@+id/image_view"
          android:layout_width="100dp"
          android:layout_height="100dp"
          android:src="@drawable/ic_launcher_foreground" />
      <Button
          android:id="@+id/start_button"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="Start Animation" />
  </LinearLayout>
  • 说明
  • ofFloat(0f, 100f) 创建从 0 到 100 的浮点数值序列。
  • addUpdateListener 监听每次值变化,更新 View 的 translationX
  • 动画结束后,View 的实际位置改变(可响应点击)。
2. 使用 ObjectAnimator

ObjectAnimatorValueAnimator 的子类,直接针对对象的属性(如 View.alpha)执行动画,无需手动更新。

  • 代码示例
  import android.animation.ObjectAnimator;
  import android.os.Bundle;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;

  public class PropertyAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_property_animation);

          ImageView imageView = findViewById(R.id.image_view);

          // 创建 ObjectAnimator,改变 alpha 属性
          ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f);
          animator.setDuration(1000); // 持续 1 秒

          // 按钮触发
          findViewById(R.id.start_button).setOnClickListener(v -> animator.start());
      }
  }
  • 说明
  • ofFloat(imageView, "alpha", 0f, 1f) 指定目标对象、属性名和值范围。
  • 属性名(如 "alpha")对应 View 的 setter 方法(如 setAlpha(float))。
  • 支持常见 View 属性:translationXtranslationYscaleXscaleYrotation 等。
3. 使用 ViewPropertyAnimator

ViewPropertyAnimator 是 View 的便捷动画接口,链式调用更简洁。

  • 代码示例
  import android.os.Bundle;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;

  public class PropertyAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_property_animation);

          ImageView imageView = findViewById(R.id.image_view);

          // 链式调用动画
          findViewById(R.id.start_button).setOnClickListener(v ->
              imageView.animate()
                  .alpha(0f)
                  .scaleX(1.5f)
                  .scaleY(1.5f)
                  .rotation(360f)
                  .setDuration(1000)
                  .start()
          );
      }
  }
  • 说明
  • view.animate() 返回 ViewPropertyAnimator 实例,支持链式设置多个属性。
  • 自动启动动画(调用 start()),更简洁。
  • 支持属性与 ObjectAnimator 类似,但不支持自定义对象。
4. 使用 AnimatorSet 组合动画

AnimatorSet 允许组合多个动画,控制播放顺序(如顺序播放或同时播放)。

  • 代码示例
  import android.animation.AnimatorSet;
  import android.animation.ObjectAnimator;
  import android.os.Bundle;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;

  public class PropertyAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_property_animation);

          ImageView imageView = findViewById(R.id.image_view);

          // 创建多个动画
          ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f);
          ObjectAnimator translate = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 100f);
          ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);

          // 组合动画
          AnimatorSet set = new AnimatorSet();
          set.play(alpha).with(translate).before(rotate); // alpha 和 translate 同时播放,之后 rotate
          set.setDuration(1000);

          // 按钮触发
          findViewById(R.id.start_button).setOnClickListener(v -> set.start());
      }
  }
  • 说明
  • play().with():同时播放多个动画。
  • play().before()/after():顺序播放。
  • setDuration() 统一设置持续时间(或各动画单独设置)。

XML 定义(较少使用)

属性动画支持 XML 定义,放置在 res/animator 目录下,使用 <objectAnimator><set> 标签。

  • XML 示例(res/animator/sample_animator.xml):
  <?xml version="1.0" encoding="utf-8"?>
  <set
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:ordering="together">
      <objectAnimator
          android:propertyName="alpha"
          android:valueFrom="0"
          android:valueTo="1"
          android:duration="1000" />
      <objectAnimator
          android:propertyName="translationX"
          android:valueFrom="0"
          android:valueTo="100"
          android:duration="1000" />
  </set>
  • 加载代码
  Animator animator = AnimatorInflater.loadAnimator(this, R.animator.sample_animator);
  animator.setTarget(imageView);
  animator.start();

关键方法与属性

以下是属性动画的核心方法和属性:

方法/属性描述用法示例
start()启动动画。animator.start();
cancel()取消动画(停止在当前状态)。animator.cancel();
setDuration(long)设置持续时间(毫秒)。animator.setDuration(1000);
setInterpolator(Interpolator)设置插值器(速度曲线)。animator.setInterpolator(new LinearInterpolator());
setRepeatCount(int)设置重复次数(-1 为无限)。animator.setRepeatCount(ValueAnimator.INFINITE);
setRepeatMode(int)设置重复模式(RESTART 或 REVERSE)。animator.setRepeatMode(ValueAnimator.REVERSE);
addListener(AnimatorListener)添加动画监听器(开始、结束、取消等)。animator.addListener(listener);
addUpdateListener(ValueAnimator.AnimatorUpdateListener)监听 ValueAnimator 的值变化(仅 ValueAnimator)。animator.addUpdateListener(listener);

插值器(Interpolator)

与补间动画类似,属性动画支持内置插值器(如 LinearInterpolatorAccelerateDecelerateInterpolator)或自定义插值器(实现 TimeInterpolator)。常见内置插值器:

  • LinearInterpolator:匀速。
  • AccelerateDecelerateInterpolator:先加速后减速。
  • OvershootInterpolator:超出后回弹。
  • AnticipateInterpolator:反向启动后回弹。

优缺点

  • 优点
  • 灵活性高,支持任意对象属性的动画(View 或自定义类)。
  • 修改实际属性值,动画后状态可交互(如点击区域随位置移动)。
  • 支持复杂组合(AnimatorSet)和动态调整。
  • 缺点
  • 代码复杂度高于补间动画(尤其是组合动画)。
  • 性能略低于补间动画(因涉及属性修改)。
  • API 11+(需兼容库 AnimationCompat 支持低版本)。
  • 替代方案
  • 补间动画:适合简单视觉效果。
  • Lottie:复杂矢量动画。
  • Jetpack Compose 动画:现代化声明式动画。

注意事项

  • 性能:避免在主线程执行复杂计算,尽量用 ViewPropertyAnimator 优化简单 View 动画。
  • 兼容性:属性动画需 API 11+,低版本可用 NineOldAndroids 兼容库(已废弃,建议迁移到 Compose)。
  • 属性支持ObjectAnimator 要求属性有 getter/setter 方法(如 setAlpha(float))。自定义对象需提供公开的 setter。
  • 生命周期:在 Activity/Fragment 暂停时(如 onPause)调用 cancel() 释放资源。
  • 调试:使用 AnimatorListener 监听动画状态,或通过 Log 输出当前值。

扩展:Jetpack Compose 中的等效实现

在 Jetpack Compose 中,属性动画通过 animate*AsStateAnimatable 实现,语法更简洁:

  • 示例(透明度 + 平移):
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp

@Composable
fun PropertyAnimation() {
    var enabled by remember { mutableStateOf(false) }
    val alpha by animateFloatAsState(
        targetValue = if (enabled) 1f else 0f,
        animationSpec = tween(durationMillis = 1000)
    )
    val translationX by animateFloatAsState(
        targetValue = if (enabled) 100f else 0f,
        animationSpec = tween(durationMillis = 1000)
    )

    Image(
        painter = painterResource(id = R.drawable.ic_launcher_foreground),
        contentDescription = "Property Animation",
        modifier = Modifier
            .size(100.dp)
            .graphicsLayer(alpha = alpha, translationX = translationX)
            .clickable { enabled = !enabled }
    )
}
  • 说明animateFloatAsState 自动处理值变化,graphicsLayer 应用动画效果。Compose 支持更复杂的 updateTransitionAnimatedContent

示例:按钮点击缩放反馈

一个常见的按钮点击动画(缩放 + 恢复):

Button button = findViewById(R.id.start_button);
button.setOnClickListener(v -> {
    v.animate()
        .scaleX(1.2f)
        .scaleY(1.2f)
        .setDuration(200)
        .withEndAction(() -> v.animate().scaleX(1f).scaleY(1f).setDuration(200).start())
        .start();
});

如果需要更深入的属性动画内容(如自定义插值器、多属性组合、TypeEvaluator)、与补间动画的详细对比、Lottie 替代方案,或 Compose 中的高级动画,请告诉我!后续可探讨 Android 动画合集的其他部分(如过渡动画、Lottie 动画)。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注