13.1 TypeEvaluator<T>
职责:在两个动画值之间进行线性插值,将 fraction 映射为实际动画值。
接口定义:
java
public interface TypeEvaluator<T> {
T evaluate(float fraction, T startValue, T endValue);
}
核心方法:
| 方法 | 说明 |
|---|---|
evaluate(float fraction, T startValue, T endValue) |
根据 fraction 在 startValue 和 endValue 之间线性插值,返回计算结果 |
内置实现:
| 实现类 | 值类型 | 插值方式 |
|---|---|---|
IntEvaluator |
Integer |
startInt + fraction * (endInt - startInt) |
FloatEvaluator |
Number |
startFloat + fraction * (endFloat - startFloat) |
ArgbEvaluator |
Object (int color) |
分离 ARGB 四通道,在 sRGB 线性空间中分别插值后合并(单例模式) |
PointFEvaluator |
PointF |
对 x/y 分别线性插值 |
RectEvaluator |
Rect |
对 left/top/right/bottom 分别插值 |
FloatArrayEvaluator |
float[] |
对数组每个元素分别插值 |
IntArrayEvaluator |
int[] |
对数组每个元素分别插值 |
ArgbEvaluator 详解:
ArgbEvaluator 对颜色通道使用 gamma 校正(Math.pow(x, 2.2)),在 sRGB 线性空间中进行插值,而非直接对 sRGB 值线性插值。
sRGB [0..255]
│ / 255.0f ← 归一化到 [0,1]
▼
sRGB [0,1]
│ Math.pow(x, 2.2) ← sRGB → 线性光(gamma 解码)
▼
线性光 [0,1]
│ start + fraction * (end - start) ← 线性插值
▼
插值后线性光 [0,1]
│ Math.pow(x, 1.0/2.2) * 255 ← 线性光 → sRGB(gamma 编码)
▼
sRGB [0..255]
- RGB 通道:做 pow 转换,因为人眼对颜色的感知是非线性的,直接在 sRGB 空间插值会导致中间色偏暗
- Alpha 通道:不做 pow,直接线性插值,因为 Alpha 表示透明度而非光强度
- 单例模式 :无状态,
getInstance()返回共享实例,可被多个 Animator 复用
TypeConverter<T, V>:
职责:在动画值类型 T 与 setter 参数类型 V 之间转换。当动画计算的值类型与目标属性 setter 接受的参数类型不一致时使用。
java
public abstract class TypeConverter<T, V> {
public abstract V convert(T value); // 动画值 → setter 参数
Class<V> getTargetType(); // setter 参数类型(用于查找 setter)
Class<T> getSourceType(); // 动画值类型
}
使用场景 :例如动画沿 Path 运动(产生 PointF),但目标属性 setter 接受 float,需要 TypeConverter<PointF, Float> 提取 x 或 y 分量。
BidirectionalTypeConverter<T, V>:
职责 :双向类型转换器,继承自 TypeConverter,额外支持反向转换(setter 参数类型 → 动画值类型)。
java
public abstract class BidirectionalTypeConverter<T, V> extends TypeConverter<T, V> {
public abstract T convertBack(V value); // setter 参数 → 动画值(从目标对象读取当前值时使用)
public BidirectionalTypeConverter<V, T> invert(); // 返回反转的 Converter
}
使用场景 :当只提供了动画终值(未提供起始值)时,ObjectAnimator 需要从目标对象 getter 读取当前值,再通过 convertBack 转换为动画值类型。invert() 返回一个反转的 Converter,将 convert 和 convertBack 互换。
使用方式:
ValueAnimator.ofObject(evaluator, values)工厂方法传入ValueAnimator.setEvaluator(evaluator)后设置PropertyValuesHolder.init()中自动分配:Integer→IntEvaluator,Float→FloatEvaluator,其他类型需手动设置
13.2 TimeInterpolator
职责:控制动画的"节奏",将原始时间 fraction 映射为插值 fraction,实现非线性运动(加速、减速、弹跳等)。
接口定义:
java
public interface TimeInterpolator {
float getInterpolation(float input);
}
核心方法:
| 方法 | 说明 |
|---|---|
getInterpolation(float input) |
输入 [0,1] 的原始 fraction,返回插值后的 fraction。返回值可超出 [0,1](如过冲、弹跳) |
与 TypeEvaluator 的关系:
原始 fraction (0~1)
│
▼ TimeInterpolator.getInterpolation()
插值 fraction (可能超出 0~1)
│
▼ TypeEvaluator.evaluate()
实际动画值
内置实现 (位于 android.view.animation 包):
| 实现类 | 效果 |
|---|---|
LinearInterpolator |
匀速 |
AccelerateInterpolator |
加速 |
DecelerateInterpolator |
减速 |
AccelerateDecelerateInterpolator |
先加速后减速(默认) |
BounceInterpolator |
弹跳 |
OvershootInterpolator |
过冲后回弹 |
AnticipateInterpolator |
先回拉再前进 |
AnticipateOvershootInterpolator |
先回拉,过冲后回弹 |
CycleInterpolator |
正弦周期循环 |
PathInterpolator |
基于 Path 自定义曲线 |
在 ValueAnimator 中的调用位置 :animateValue() 方法中(第1644行):
java
fraction = mInterpolator.getInterpolation(fraction);
在 Keyframe 中的局部 Interpolator :每个 Keyframe 可携带独立的 TimeInterpolator,仅作用于该 Keyframe 到下一个 Keyframe 之间的区间。若为 null 则使用线性插值。
13.3 PropertyValuesHolder
职责 :持有一个属性的名称、值集合、Evaluator 和 KeyframeSet,是 ValueAnimator 与具体属性值之间的桥梁。支持多属性并行动画。
核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
mPropertyName |
String |
属性名(用于反射查找 setter/getter) |
mProperty |
Property |
属性对象(优先于反射方式) |
mSetter |
Method |
setter 方法(反射缓存) |
mGetter |
Method |
getter 方法(用于自动获取缺失值) |
mValueType |
Class |
值类型 |
mKeyframes |
Keyframes |
关键帧集合 |
mEvaluator |
TypeEvaluator |
类型求值器 |
mConverter |
TypeConverter |
值类型转换器(动画值 → setter 参数类型) |
mAnimatedValue |
Object |
最近计算的动画值 |
工厂方法:
| 方法 | 说明 |
|---|---|
ofInt(String/Property, int...) |
创建 int 类型动画值持有器 |
ofFloat(String/Property, float...) |
创建 float 类型动画值持有器 |
ofObject(String/Property, TypeEvaluator, Object...) |
创建自定义对象类型动画值持有器 |
ofObject(String/Property, TypeConverter, Path) |
沿 Path 动画,通过 Converter 转换为属性值 |
ofKeyframe(String/Property, Keyframe...) |
通过 Keyframe 数组创建 |
ofMultiInt(String, int[][]/Path/...) |
多参数 int setter |
ofMultiFloat(String, float[][]/Path/...) |
多参数 float setter |
核心方法:
| 方法 | 说明 |
|---|---|
calculateValue(float fraction) |
调用 mKeyframes.getValue(fraction) 计算当前值,再经 mConverter 转换 |
setAnimatedValue(Object target) |
将计算出的值通过 mProperty.set() 或 mSetter.invoke() 设置到目标对象 |
setupSetterAndGetter(Object target) |
动画启动前,通过反射或 Property 自动查找 setter/getter,并为缺失值的 Keyframe 填充值 |
init() |
初始化 Evaluator(自动分配 Int/Float Evaluator)并传递给 KeyframeSet |
setEvaluator(TypeEvaluator) |
设置自定义 Evaluator |
setConverter(TypeConverter) |
设置类型转换器 |
内部子类:
| 子类 | 说明 |
|---|---|
IntPropertyValuesHolder |
int 专用,使用 IntKeyframes 避免装箱,支持 JNI setter 加速 |
FloatPropertyValuesHolder |
float 专用,使用 FloatKeyframes 避免装箱,支持 JNI setter 加速 |
MultiFloatValuesHolder |
多参数 float setter(如 setLocation(float x, float y)) |
MultiIntValuesHolder |
多参数 int setter |
Setter 查找策略(优先级从高到低):
IntProperty/FloatProperty(直接调用,无反射)Property.set()(通用 Property 接口)- JNI 方法 ID(
nGetIntMethod/nGetFloatMethod,仅 int/float) - 反射
Method.invoke()(回退方案)
Setter 类型匹配:当值类型与 setter 参数类型不完全匹配时,按类型变体表依次尝试:
- Float 变体:
float → Float → double → int → Double → Integer - Integer 变体:
int → Integer → float → double → Float → Double
Setter/Getter 查找详解
查找时机 :ObjectAnimator 启动时调用 setupSetterAndGetter(target),在动画开始前完成 setter/getter 的绑定。
查找流程:
setupSetterAndGetter(target):
│
├─ 若 mProperty != null(优先使用 Property 接口)
│ │
│ ├─ 尝试 mProperty.get(target) 验证属性存在
│ │ ├─ 成功 → 遍历 Keyframe,为无值帧填充从 target 读取的值
│ │ └─ ClassCastException → mProperty = null,降级到反射 ↓
│
└─ 若 mProperty == null(反射方式)
│
├─ setupSetter(targetClass)
│ │
│ ├─ 确定 setter 参数类型:
│ │ mConverter != null → mConverter.getTargetType()
│ │ mConverter == null → mValueType
│ │
│ ├─ getPropertyFunction(targetClass, "set", paramType)
│ │ │
│ │ ├─ 方法名 = "set" + 首字母大写(mPropertyName)
│ │ │ 例: mPropertyName="alpha" → "setAlpha"
│ │ │
│ │ ├─ 按类型变体表依次尝试:
│ │ │ paramType=Float → 尝试 [float, Float, double, int, Double, Integer]
│ │ │ paramType=Integer → 尝试 [int, Integer, float, double, Float, Double]
│ │ │ 其他 → 仅尝试 paramType 本身
│ │ │
│ │ └─ 找到后缓存到 mSetter,并更新 mValueType 为实际匹配类型
│ │
│ └─ 缓存到 sSetterPropertyMap(Class → HashMap<propName, Method>)
│
└─ 遍历 Keyframe,为无值帧填充:
│
├─ 若该帧 !hasValue() 或 valueWasSetOnStart()
│ │
├─ setupGetter(targetClass) ← 按需延迟加载
│ │ └─ getPropertyFunction(targetClass, "get", null)
│ │ 方法名 = "get" + 首字母大写(mPropertyName)
│ │ 例: mPropertyName="alpha" → "getAlpha"
│ │
└─ mGetter.invoke(target) 获取当前值 → 填入 Keyframe
Setter 调用链 (setAnimatedValue(target) 时):
setAnimatedValue(target):
│
├─ IntPropertyValuesHolder / FloatPropertyValuesHolder:
│ ├─ mIntProperty / mFloatProperty != null → 直接调用 setValue(target, value)
│ ├─ mProperty != null → mProperty.set(target, value)
│ ├─ mJniSetter != 0 → nCallIntMethod / nCallFloatMethod(JNI 加速)
│ └─ mSetter != null → mSetter.invoke(target, value)(反射回退)
│
└─ 通用 PropertyValuesHolder:
├─ mProperty != null → mProperty.set(target, getAnimatedValue())
└─ mSetter != null → mSetter.invoke(target, getAnimatedValue())
Getter 用途 :仅用于 ObjectAnimator 场景,当 Keyframe 未提供值时(如 ofFloat("alpha", 1f) 只给了终值),从目标对象 getter 读取当前值作为起始值。ValueAnimator 不使用 getter。
反射缓存 :sSetterPropertyMap 和 sGetterPropertyMap 是 HashMap<Class, HashMap<String, Method>> 静态缓存,避免对同一个 Class + propertyName 组合重复反射查找。IntPropertyValuesHolder 和 FloatPropertyValuesHolder 额外维护 sJNISetterPropertyMap 缓存 JNI 方法 ID。
13.4 Keyframe
职责 :持有一个时间/值对,定义动画在某个时间点的目标值。每个 Keyframe 可携带独立的 TimeInterpolator 控制该帧到下一帧之间的插值方式。
核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
mFraction |
float |
时间点,[0, 1] 表示动画进度的比例 |
mValueType |
Class |
值类型 |
mInterpolator |
TimeInterpolator |
该帧到下一帧之间的插值器(null = 线性) |
mHasValue |
boolean |
是否有有效值(无值时由 ObjectAnimator 从目标对象自动获取) |
mValueWasSetOnStart |
boolean |
值是否在动画启动时从目标对象读取(目标变化时需重新获取) |
工厂方法:
| 方法 | 说明 |
|---|---|
ofInt(float fraction, int value) |
创建带值的 int Keyframe |
ofInt(float fraction) |
创建无值的 int Keyframe(值从目标对象获取) |
ofFloat(float fraction, float value) |
创建带值的 float Keyframe |
ofFloat(float fraction) |
创建无值的 float Keyframe |
ofObject(float fraction, Object value) |
创建带值的 Object Keyframe |
ofObject(float fraction) |
创建无值的 Object Keyframe |
核心方法:
| 方法 | 说明 |
|---|---|
getValue() |
获取该帧的值(抽象方法,子类实现) |
setValue(Object) |
设置该帧的值 |
getFraction() |
获取时间点 |
setFraction(float) |
设置时间点 |
getInterpolator() |
获取该帧的插值器 |
setInterpolator(TimeInterpolator) |
设置该帧的插值器 |
getType() |
获取值类型 |
hasValue() |
是否有有效值 |
内部子类:
| 子类 | 值存储 | 说明 |
|---|---|---|
IntKeyframe |
int mValue |
避免装箱,提供 getIntValue() 直接返回原始类型 |
FloatKeyframe |
float mValue |
避免装箱,提供 getFloatValue() 直接返回原始类型 |
ObjectKeyframe |
Object mValue |
通用对象类型 |
13.5 Keyframes(接口)
职责 :抽象 Keyframe 集合,由 ValueAnimator 调用来计算给定 fraction 对应的动画值。
接口定义:
java
public interface Keyframes extends Cloneable {
void setEvaluator(TypeEvaluator evaluator);
Class getType();
Object getValue(float fraction);
List<Keyframe> getKeyframes();
Keyframes clone();
// 特化子接口,避免装箱
interface IntKeyframes extends Keyframes {
int getIntValue(float fraction);
}
interface FloatKeyframes extends Keyframes {
float getFloatValue(float fraction);
}
}
核心方法:
| 方法 | 说明 |
|---|---|
getValue(float fraction) |
根据 fraction 在 Keyframe 之间插值,返回动画值(Object 类型) |
setEvaluator(TypeEvaluator) |
设置 Evaluator(仅 Object 类型需要,int/float 有内置实现) |
getType() |
返回值类型 |
getKeyframes() |
返回 Keyframe 列表 |
特化子接口 :IntKeyframes 和 FloatKeyframes 提供直接返回原始类型的方法,避免装箱开销。
13.6 KeyframeSet
职责 :Keyframes 接口的基本实现,持有一组 Keyframe 并负责在相邻 Keyframe 之间插值。
核心字段:
| 字段 | 类型 | 说明 |
|---|---|---|
mNumKeyframes |
int |
Keyframe 数量 |
mFirstKeyframe |
Keyframe |
首帧引用(快速访问) |
mLastKeyframe |
Keyframe |
末帧引用(快速访问) |
mInterpolator |
TimeInterpolator |
仅 2 帧时使用的插值器(取自末帧) |
mKeyframes |
List<Keyframe> |
Keyframe 列表 |
mEvaluator |
TypeEvaluator |
类型求值器 |
工厂方法:
| 方法 | 说明 |
|---|---|
ofInt(int...) |
从 int 值数组创建 IntKeyframeSet,自动均匀分配 fraction |
ofFloat(float...) |
从 float 值数组创建 FloatKeyframeSet,自动均匀分配 fraction |
ofObject(Object...) |
从 Object 值数组创建 KeyframeSet |
ofKeyframe(Keyframe...) |
从 Keyframe 数组创建,自动识别类型并返回对应子类 |
ofPath(Path) |
从 Path 创建 PathKeyframes |
getValue(float fraction) 插值逻辑:
- 2 帧优化 :直接用末帧的 Interpolator 插值后调用
mEvaluator.evaluate() - fraction <= 0:在首帧和第二帧之间插值
- fraction >= 1:在倒数第二帧和末帧之间插值
- 中间值 :遍历找到 fraction 落入的区间
[prevKeyframe, nextKeyframe],计算区间内比例intervalFraction,应用该区间 Interpolator,调用mEvaluator.evaluate()
子类:
| 子类 | 说明 |
|---|---|
IntKeyframeSet |
int 专用,实现 getIntValue() 直接使用原始类型计算,避免装箱 |
FloatKeyframeSet |
float 专用,实现 getFloatValue() 直接使用原始类型计算,避免装箱 |
PathKeyframes |
基于 Path 的关键帧,将 fraction 映射为 Path 上的 PointF |
PathKeyframes 详解
职责 :将 android.graphics.Path 近似为一组线段,通过线性插值在 Path 上定位点。不使用 Keyframe 对象,而是将所有点数据存储在一个紧凑的 float[] 中以减少对象分配。
数据结构:
java
// 每个点存储 3 个 float:[fraction, x, y]
private static final int FRACTION_OFFSET = 0;
private static final int X_OFFSET = 1;
private static final int Y_OFFSET = 2;
private static final int NUM_COMPONENTS = 3;
private float[] mKeyframeData; // 紧凑数组,由 path.approximate(error) 生成
private PointF mTempPointF; // 复用对象,避免每次创建
path.approximate(0.5f) 递归地将 Path 细分为线段,直到线段与曲线的误差小于 0.5 像素,返回 [fraction0, x0, y0, fraction1, x1, y1, ...] 数组。
getValue(float fraction) 查找方式:
与 KeyframeSet 的线性遍历不同,PathKeyframes 使用二分查找定位区间:
getValue(fraction):
│
├─ fraction < 0 → interpolateInRange(fraction, 0, 1) ← 边界外:首段外推
├─ fraction > 1 → interpolateInRange(fraction, numPoints-2, numPoints-1) ← 边界外:末段外推
├─ fraction == 0 → pointForIndex(0) ← 精确起点
├─ fraction == 1 → pointForIndex(numPoints-1) ← 精确终点
│
└─ 0 < fraction < 1 → 二分查找:
│
├─ low=0, high=numPoints-1
│
├─ while (low <= high):
│ mid = (low + high) / 2
│ midFraction = mKeyframeData[mid * 3]
│ │
│ ├─ fraction < midFraction → high = mid - 1
│ ├─ fraction > midFraction → low = mid + 1
│ └─ fraction == midFraction → return pointForIndex(mid) ← 精确命中
│
└─ 循环结束后: high < fraction < low
→ interpolateInRange(fraction, high, low) ← 区间内插值
区间内插值:
java
PointF interpolateInRange(float fraction, int startIndex, int endIndex) {
float startFraction = mKeyframeData[startIndex * 3];
float endFraction = mKeyframeData[endIndex * 3];
// 区间内比例
float intervalFraction = (fraction - startFraction) / (endFraction - startFraction);
// 对 x、y 分别线性插值
float x = startValue + (endValue - startValue) * intervalFraction;
float y = startValue + (endValue - startValue) * intervalFraction;
mTempPointF.set(x, y); // 复用对象
return mTempPointF;
}
与 KeyframeSet 的对比:
| 特性 | KeyframeSet | PathKeyframes |
|---|---|---|
| 数据存储 | List<Keyframe> 对象列表 |
紧凑 float[] 数组 |
| 查找方式 | 线性遍历 | 二分查找 |
| 每个点的 Interpolator | 支持(每段独立) | 不支持(仅线性) |
| Evaluator | 使用 mEvaluator |
不使用(直接计算 x/y) |
| 对象分配 | 每个 Keyframe 一个对象 | 仅复用一个 PointF |
getKeyframes() |
返回实际列表 | 返回 EMPTY_KEYFRAMES |
X/Y 分量提取:
PathKeyframes 可通过 createXFloatKeyframes() / createYFloatKeyframes() 创建只返回 x 或 y 分量的 FloatKeyframes,通过 createXIntKeyframes() / createYIntKeyframes() 创建 IntKeyframes(四舍五入)。这些适配器内部委托 PathKeyframes.getValue() 再提取分量。
13.7 类关系总览
ValueAnimator
│
├── mInterpolator: TimeInterpolator ← 全局时间插值
│
├── mValues: PropertyValuesHolder[] ← 多属性并行
│ │
│ ├── mKeyframes: Keyframes ← 关键帧集合
│ │ │
│ │ └── KeyframeSet ← 默认实现
│ │ │
│ │ ├── IntKeyframeSet ← int 特化
│ │ ├── FloatKeyframeSet ← float 特化
│ │ └── PathKeyframes ← Path 特化
│ │
│ ├── mEvaluator: TypeEvaluator ← 类型求值器
│ │
│ ├── mConverter: TypeConverter ← 值类型转换
│ │
│ └── mSetter / mGetter: Method ← 反射 setter/getter
│
└── Keyframe ← 单个关键帧
├── IntKeyframe
├── FloatKeyframe
└── ObjectKeyframe
数据流:
ValueAnimator.animateValue(fraction)
│
├─ mInterpolator.getInterpolation(fraction) → 插值 fraction
│
└─ mValues[i].calculateValue(interpolatedFraction)
│
└─ mKeyframes.getValue(fraction)
│
├─ 找到区间 [prevKf, nextKf]
├─ nextKf.getInterpolator().getInterpolation(intervalFraction)
└─ mEvaluator.evaluate(intervalFraction, prevValue, nextValue)
│
▼
mAnimatedValue (最终动画值)