Android动画合集之属性动画-初见
Android 动画合集之属性动画 – 初见
属性动画(Property Animation)是 Android 3.0(API 11)引入的一种强大而灵活的动画机制,允许开发者通过动态修改对象的属性值(如 View 的位置、透明度、旋转角度等)实现平滑的动画效果。相比传统的补间动画(Tween Animation),属性动画可以直接改变对象的实际属性,而不仅仅是视觉效果,因此更适合复杂的交互式动画场景。作为 Android 动画合集的一部分(前文已覆盖帧动画和补间动画),本文将介绍属性动画的基础概念、核心类、基本用法及简单示例,适合初次接触属性动画的开发者。后续可深入探讨高级用法(如插值器、组合动画)。
属性动画的原理与作用
- 原理:属性动画通过定期更新对象的属性值(如 View 的
translationX
或自定义对象的字段),结合插值器(Interpolator)和时间轴,生成平滑的过渡效果。它基于ValueAnimator
和ObjectAnimator
类,核心是通过反射或 setter 方法动态修改属性值。 - 作用:提供通用、灵活的动画框架,支持任何对象的属性动画(不仅限于 View),并能同步更新对象的实际状态(如布局位置),适合交互性强的场景。
- 应用场景:
- View 的平移、缩放、旋转、透明度变化。
- 自定义对象属性的动画(如颜色渐变、进度条值)。
- 复杂 UI 交互(如拖拽释放动画、页面切换过渡)。
- 结合 ViewPropertyAnimator 实现简洁的 View 动画。
属性动画的核心类
属性动画系统主要依赖以下类:
类名 | 描述 | 主要用途 |
---|---|---|
ValueAnimator | 核心类,生成数值序列(int/float/Object),需手动监听值变化并应用。 | 通用数值动画 |
ObjectAnimator | ValueAnimator 的子类,自动更新指定对象的属性值(如 View 的 alpha )。 | 针对对象的属性动画 |
AnimatorSet | 组合多个动画,支持顺序或同时播放。 | 复杂动画组合 |
PropertyValuesHolder | 保存单个属性的动画值和设置器,用于多属性动画。 | 同时动画多个属性 |
ViewPropertyAnimator | View 的便捷动画接口,简化常见属性动画(如 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
ObjectAnimator
是 ValueAnimator
的子类,直接针对对象的属性(如 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 属性:
translationX
、translationY
、scaleX
、scaleY
、rotation
等。
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)
与补间动画类似,属性动画支持内置插值器(如 LinearInterpolator
、AccelerateDecelerateInterpolator
)或自定义插值器(实现 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*AsState
或 Animatable
实现,语法更简洁:
- 示例(透明度 + 平移):
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 支持更复杂的updateTransition
或AnimatedContent
。
示例:按钮点击缩放反馈
一个常见的按钮点击动画(缩放 + 恢复):
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 动画)。