android动画拆析(高级)

2015-06-12 党洁 更多博文 » 博客 » GitHub »

原文链接 https://jiedang.github.io/2015/06/12/%E5%8A%A8%E7%94%BB%E6%8B%86%E6%9E%90-%E9%AB%98%E7%BA%A7.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


细说Android动画

动画感觉说白了就是一句话
在触发范围内 循环的修改view特点后 使view重新绘制 展示新的效果

所以只要有满足三个条件就能完成一个效果

  • 触发范围

比如给个时间 在范围内做个事,或者是给个滑动距离 手指在范围内跟随滑动做个事。

  • 能改变一个值

比如View基本属性 或者布局的某个属性,或者自己写的某个效果 画个水波,翻页,做个扩散

  • 循环触发绘制

只要能循环触发 View.invalidate就可以了

触发条件 范围 一般都是产品,交互给定的,触发绘制是系统的, 所以关键是 怎么去改好这个值


属性动画

要说属性动画必须提到 nineOld( JakeWharton 大牛作品)。nineOld和Android系统里面的类 名称,功能也一样,所以使用起来 没啥区别。同事还支持3.0以下版本的,还提供了ViewHelper支持通过set,get方法修改View基本属性。(3.0以上 可以支持set get方法去修改)

基本使用

  1. 最简单写法

  1. 支持的基本属性 可以在 nineOld源码中得 ObjectAnimator 查看支持的属性

  1. viewHelper支持低版本 可以直接set,get操作VIEW属性

  2. AnimatorSet 控制多个动画设置执行顺序

  AnimatorSet  animation = new AnimatorSet();
  1 animation.playTogether(AA,BB); 
  2 animation.playSequentially(AA,BB);
  3 animation.play(AA).with(BB).after(CC).before(DD);

代码块1 同时执行AA和BB

代码块2 线性执行AA和BB

代码块3 自己构建执行顺序

  1. 一个view多个属性同时变化完成动画
  • PropertyValuesHolder使用

 PropertyValuesHolder.ofFloat("width", A,B;

 PropertyValuesHolder.ofFloat("height", c,D);

 通过objectAnimator的 PropertyValues借口实现多个属性同时修改
 ObjectAnimator.ofPropertyValuesHolder(view, PropertyValuesHolder... )


  • ViewPropertyAnimator.animate连缀写

    左右翻转着 平移到(A,B)

   ViewPropertyAnimator.animate(view).rotationYBy(720).x(xValue).y(yValue)

nineOld实现原理及其自定义实现

  1. 自定义实现 demo挤出效果

有时候完成一个效果 已经不是view基本属性的范围 就需要自定义一个“属性”。

例如 做一个挤出 收回 VIEW的动画 需要动态调整属性是 布局属性 layout_marginBottom

自定属性包含三部分

  • viewHolder 被操作的VIEW

  • dataHolder 要操作的数据

  • Evaluator 数据怎么变

  1. nineOld怎么改值 怎么实现的
  • API 11以上 通过反射修改

    核心类 PropertyValuesHolder

    PropertyValuesHolder来反射修改属性值 最关键的 是获取方法

     private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
        // TODO: faster implementation...
        Method returnVal = null;
        String methodName = getMethodName(prefix, mPropertyName);
        Class args[] = null;
        if (valueType == null) {
            try {
                returnVal = targetClass.getMethod(methodName, args);
            } catch (NoSuchMethodException e) {
                /* The native implementation uses JNI to do reflection, which allows access to private methods.
                 * getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
                 */
                try {
                    returnVal = targetClass.getDeclaredMethod(methodName, args);
                    returnVal.setAccessible(true);
                } catch (NoSuchMethodException e2) {
                    Log.e("PropertyValuesHolder",
                            "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
                }
            }
        } else {
            args = new Class[1];
            Class typeVariants[];
            if (mValueType.equals(Float.class)) {
                typeVariants = FLOAT_VARIANTS;
            } else if (mValueType.equals(Integer.class)) {
                typeVariants = INTEGER_VARIANTS;
            } else if (mValueType.equals(Double.class)) {
                typeVariants = DOUBLE_VARIANTS;
            } else {
                typeVariants = new Class[1];
                typeVariants[0] = mValueType;
            }
            for (Class typeVariant : typeVariants) {
                args[0] = typeVariant;
                try {
                    returnVal = targetClass.getMethod(methodName, args);
                    // change the value type to suit
                    mValueType = typeVariant;
                    return returnVal;
                } catch (NoSuchMethodException e) {
                    /* The native implementation uses JNI to do reflection, which allows access to private methods.
                     * getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
                     */
                    try {
                        returnVal = targetClass.getDeclaredMethod(methodName, args);
                        returnVal.setAccessible(true);
                        // change the value type to suit
                        mValueType = typeVariant;
                        return returnVal;
                    } catch (NoSuchMethodException e2) {
                        // Swallow the error and keep trying other variants
                    }
                }
            }
            // If we got here, then no appropriate function was found
            Log.e("PropertyValuesHolder",
                    "Couldn't find setter/getter for property " + mPropertyName +
                            " with value type "+ mValueType);
        }
    
        return returnVal;
    }
    
    
    • API 11以下 view矩阵变化

    核心类 AnimatorProxy

    AnimatorProxy矩阵变换

     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);
    }
    
    
    1. 触发绘制

    关键类 ValueAnimator

    关键方法 animationFrame 控制run

    关键方法 handleMessage 处理一些状态 start stop

组合使用 flyRefrsh

flyRefrsh 使用系统属性动画来组合做出来的 没有使用nineOld做低版本支持,拿来主要看组合使用。

github项目地址 传送门

实现分析传送门


自定义动画

我们也可以像Android官方提供动画一样 自己去复写一个类似于ScaleAnimation一样

主要方法

  • applyTransformation 实现动画的效果变化(官方都是改矩阵 也可以改其他)
  • initialize 初始化数据

对于属性操作

demo挤出效果 自定义动画实现

ObjectAnimation 关键方法


 @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float temp = startValue + (endValue - startValue) * interpolatedTime;
        if (temp != value) {
            value = temp;
            if(onInvalidateImp != null){
                onInvalidateImp.onInvalidate(view,value);
            }
            view.invalidate();
        }
        //super.applyTransformation(interpolatedTime,t);
    }

在实现效果方法里面 丢出借口 onInvalidate(view,value),外部使用中间量 实现对应效果(改属性或者改矩阵)

关于动画绘制过程 有哪些方法 都是干什么的 可以关注啊下

android 动画执行流程分析 传送门

复写draw实现

说到draw就必须说这三个类

最关键类 canvas画布 paint画笔 matrix矩阵

  • canvas 整体性操作 类型 翻转,平移,缩放,扭曲,裁剪

canvas操作中 不受到其他操作影响 一定要 save()、restore()

  • paint 效果细节 颜色,粗细,重叠方式,空实心等 比较多写不过来

  • matrix 存储view基本特征属性(位置,大小,2D/3D旋转角度,倾斜角度)

可以通过修改这些属性实现效果

可以百度下 很多基础功能类介绍,

对于3D翻转,使用camera构建一个矩阵添加到画布上去 类似于nineOld中对于API 11以下版本实现方式

demo简单出票

主要完成一个画布的裁剪 实现一个“吐出来”效果 有使用一些动画上 原有功能 RepeatCount RepeatMode 实现往复


            canvas.save();
            canvas.translate(0,currentHeight - ticketH);
            canvas.clipRect(ticketL, ticketT + ticketH - currentHeight,ticketR,ticketB);
            ticket.draw(canvas);
            canvas.restore();

demo圆环

复杂点 2个状态切换的 圆环翻转 和 圆环弧度变化

翻转

生成翻转矩阵 添加到画布上

XX 矩阵操作生成翻转 matrix  XX
canvas.concat(matrix);

跟弧度 画对应长度圆弧标示百分比

canvas.drawArc(mBigOval, -90, mCurrentSweep, false, mPaint);

优化点

讲两个view合成为一个 在objectionAnimation 存储状态值 判断状态值 进行不同操作

  @Override
    public float getValue() {
        return value;
    }

    @Override
    public int getStep() {
        return 0;
    }

控件动画实现

控件每个都不一样 主要是找到你要进行动画的view 添加对应的效果

  1. listView item添加效果

demo jazzyListView 实现核心代码

  1. viewGourp item 添加效果

demoe sortGridView 实现核心代码


interpolator 插值器

原来收藏过的 比较详细的一个 图比较多 很清楚直观

相关地址

介绍文档传送门

一个github项目传送门

项目完整 demo 传送门