Android 开源项目NineOldAnimations详解

背景

属性动画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的 settergetter方法,以便获取和更新属性值。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在低版本上都不兼容,所以这个开源项目对如何实现在低版本兼容提供了一种思路,思想很值得借鉴啊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值