背景
属性动画view属性
常见的属性动画属性:
alpha 透明度
scaleX 缩放X方向
scaleY 缩放Y方向
translationX 平移X方向
translationY 平移Y方向
rotation 旋转
rotationX X轴翻转
rotationY Y轴翻转
pivotX 设置旋转,翻转效果中心x坐标 (默认是在view的中心x坐标)
pivotY 设置旋转,翻转效果中心y坐标 (默认是在view的中心y坐标)
一般跟rotation,scale配合使用
补间动画只有移动、缩放、旋转和淡入淡出这四种动画操作,并没有x轴,y轴翻转的功能。更不用说任何属性了,同时只是改变了View的显示效果而已,而不会真正去改变View的属性
ObjectAnimator内部的工作机制寻找这个属性名对应的get和set方法,反射调用
类继承关系
Animation
RotateAnimation ScaleAnimation AlphaAnimation AnimationSet…… 补间动画
–public void setAnimationListener(AnimationListener listener)
Animator
ObjectAnimator ValueAnimator AnimatorSet…… 属性动画
–public void addListener(AnimatorListener listener)
注意不要弄混淆了
属性动画的XML声明文件存放在res/animator目录下,而补间动画则存放在res/anim目录
对应代码中的ValueAnimator
对应代码中的ObjectAnimator
对应代码中的AnimatorSet
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
为什么需要NOA
属性动画需要API11(Honeycomb(Android3.0))的支持,而补间动画则是更早期的版本,为什么早期的版本不支持,因为早期的版本view并没有set/get Alpha/Rotation/Scale等方法,所以发射调用set/get方法,会提示找不到set/get方法,但是NOA解决了版本兼容性问题
使用方法
使用NOA库的ObjectAnimator/ValueAnimaor,而不是原生的ObjectAnimator/ValueAnimaor
这样的话,代码中动画就不需要区分api11之前之后分别实现动画了,实现了统一用属性动画api的方式去访问,虽然api11之前实际上内部还是通过补间动画Transformation/Matrix实现各种动画效果,注意属性并没有变化!
NOA原理
首先来看看怎么做到的
必须同时了解补间动画和属性动画原理
1. Android 补间动画原理
2. Android 属性动画 源码解析 深入了解其内部实现
ValueAnimator的缺点是需要通过实现AnimatorUpdateListener
自己手动去更新属性值,它的子类ObjectAnimator为用户实现了自动更新动画,但是对于自定义的属性,需要提供标准JavaBean的 setter
和getter
方法,以便获取和更新属性值。NOA 也是遵循了这样的实现思路,对于3.0
之前的系统来说,属性动画中所提供的属性都是新的,在实现的时候也就都属于自定义的。NOA 在 PreHoneycombCompat中定义了这些属性,并在get和setValue 中提供了标准的 setter 和 getter 方法用于设置和获取属性值,这里的 setter 和 getter 其实是直接调用 AnimatorProxy 类的方法。
NOA的架构实现和系统属性动画实现架构其实是一样的。 只是兼容的那一部分采用了Transformation和Matrix实现了各种动画效果,原理图:
下面以设置透明度alpha举例
PropertyValuesHolder类的setAnimatedValue的setAnimatedValue方法反射invoke中调用的都是ViewHelper的set/get方法,如果api>11,那么直接调用view的get/set方法,否则通过Transformation和Matrix实现了动画效果
//ViewHelper.java
public final class ViewHelper {
private ViewHelper() {}
//反射调用invoke该get方法
public static float getAlpha(View view) {
return NEEDS_PROXY ? wrap(view).getAlpha() : Honeycomb.getAlpha(view);
}
//反射调用invoke该set方法
public static void setAlpha(View view, float alpha) {
if (NEEDS_PROXY) { //API11以下
wrap(view).setAlpha(alpha);
} else { //API11以上直接调用view的setAlpha方法
Honeycomb.setAlpha(view, alpha);
}
}
private static final class Honeycomb {
static float getAlpha(View view) {
return view.getAlpha(); //API11以上直接调用view的get方法
}
static void setAlpha(View view, float alpha) {
view.setAlpha(alpha); //API11以上直接调用view的set方法
}
}
}
//PreHoneycombCompat为api11之前定义ALPHA属性
final class PreHoneycombCompat {
static Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setAlpha(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getAlpha();
}
};
//AnimatorProxy.java 自定义动画,代理动画
public final class AnimatorProxy extends Animation {
private AnimatorProxy(View view) {
setDuration(0); //perform transformation immediately
setFillAfter(true); //persist transformation beyond duration
view.setAnimation(this); //为该view设置动画,this
mView = new WeakReference<View>(view);
}
public static AnimatorProxy wrap(View view) {
AnimatorProxy proxy = PROXIES.get(view);
// This checks if the proxy already exists and whether it still is the animation of the given view
if (proxy == null || proxy != view.getAnimation()) {
proxy = new AnimatorProxy(view); //PROXIES集合没有的话就new一个AnimatorProxy
PROXIES.put(view, proxy);
}
return proxy;
}
public void setAlpha(float alpha) {
if(this.mAlpha != alpha) {
this.mAlpha = alpha;
View view = (View)this.mView.get();
if(view != null) {
view.invalidate(); //重绘的时候会调用applyTransformation方法
}
}
}
//重写Animation的applyTransformation方法
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
View view = mView.get();
if (view != null) {
t.setAlpha(mAlpha);//设置t对象的alpha属性,重绘的时候会使用该Transformation
transformMatrix(t.getMatrix(), view); //设置t对象的Matrix属性
}
}
//mPivotX,mRotationX,mScaleX,mTranslationX应用到了Transformation的Matrix属性上了
private void transformMatrix(Matrix m, View view) {
final float w = view.getWidth();
final float h = view.getHeight();
final boolean hasPivot = mHasPivot;
final float pX = hasPivot ? mPivotX : w / 2f;
final float pY = hasPivot ? mPivotY : h / 2f;
final float rX = mRotationX;
final float rY = mRotationY;
final float rZ = mRotationZ;
if ((rX != 0) || (rY != 0) || (rZ != 0)) {
final Camera camera = mCamera;
camera.save();
camera.rotateX(rX);
camera.rotateY(rY);
camera.rotateZ(-rZ);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-pX, -pY);
m.postTranslate(pX, pY);
}
final float sX = mScaleX;
final float sY = mScaleY;
if ((sX != 1.0f) || (sY != 1.0f)) {
m.postScale(sX, sY);
final float sPX = -(pX / w) * ((sX * w) - w);
final float sPY = -(pY / h) * ((sY * h) - h);
m.postTranslate(sPX, sPY);
}
m.postTranslate(mTranslationX, mTranslationY);
}
}
更详细的介绍见:NineOldAnimations 源码解析
最后思考:
很多api在低版本上都不兼容,所以这个开源项目对如何实现在低版本兼容提供了一种思路,思想很值得借鉴啊