《Android群英传》读书笔记(8)第七章:Android动画机制与使用技巧

1.视图动画

Android视图动画的分类:
  • 透明度动画——AlphaAnimation
  • 旋转动画——RotateAnimation
  • 位移动画——TranslateAnimation
  • 缩放动画——ScaleAnimation
  • 动画集合——AnimationSet
视图动画的监听方式:
animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                
            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
2.属性动画
视图动画Animation存在一定的局限性,动画改变的只是显示,并不能响应事件,因此属性动画Animator就应运而生。Animator中使用最多的就是AnimatorSet和ObjectAnimator,AnimatorSet用来组合ObjectAnimator,ObjectAnimator只控制一个对象的属性值,而且ObjectAnimator能够自动驱动,可以调用setFrameDelay(longframeDelay);设置动画之间的间隙时间,调整帧率。最重要的是属性动画通过调用属性的set、get方法来真实地控制一个View的属性值,因此强大的属性动画基本可以实现所有的动画效果。
ObjectAnimator的使用方法:
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0, 300);
        animator.setDuration(3000);
        animator.start();

需要注意的是,要操纵的对象的属性必须具有get和set方法,不然属性动画就无法起效。
属性动画常用的属性:
  • translationX和translationY
  • rotation、rotationX和rotationY
  • scaleX和scaleY
  • pivotX和pivotY
  • x和y
  • alpha
如果一个属性没有get和set方法,可以通过对操作对象进行包装来实现set、get方法:
public static class WrapperView {

        private View mTarget;

        public WrapperView(View target){
            mTarget = target;
        }

        public int getWidth(){
            return mTarget.getLayoutParams().width;
        }

        public void setWidth(int width){
            mTarget.getLayoutParams().width = width;
            mTarget.requestLayout();
        }
    }

然后对包装后的对象应用动画:
ObjectAnimator.ofInt(wrapperView, "width",
                        wrapperView.getWidth(), wrapperView.getWidth() * 2).setDuration(3000).start();
PropertyValuesHolder
类似视图动画中的AnimationSet,PropertyValuesHolder可以针对同一个对象的多个属性同时应用多种动画下面是使用示例:
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX",300f);
        PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("scaleX",1f,0f);
        PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("scaleY",1f,0f);
        ObjectAnimator.ofPropertyValuesHolder(view,p1,p2,p3).setDuration(3000).start();

ValueAnimator
ValueAnimator是属性动画的核心所在,它本身不提供任何效果,更像是一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程。ValueAnimator的使用方法如下,通常在AnimatorUpdateListener中监听数值的变换,从而完成动画的变换:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,100);
        valueAnimator.setDuration(1000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float value = (Float) animation.getAnimatedValue();
                //TODO use the value
            }
        });
属性动画的监听
通过AnimatorListener监听动画执行的全过程:
animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                
            }

            @Override
            public void onAnimationEnd(Animator animation) {

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
或者使用AnimatorListener的实现类AnimatorListenerAdapter:
animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {

            }
        });
AnimatorSet
AnimatorSet除了能实现PropertyValuesHolder的功能之外,还能实现更为精确的控制动画播放的顺序,AnimatorSet中有playTogether()、playSequentially()、animSet.play().with()、before()、after()等方法。
在XML中使用属性动画
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="translationX"
    android:interpolator="@android:interpolator/accelerate_cubic"
    android:valueFrom="0dp"
    android:valueTo="100dp"
    android:valueType="intType"
    >

</objectAnimator>
然后在代码中进行加载:
ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(this,R.animator.anim);

除了使用Animator.start()方法启动动画外,在Android3.0之后Google还为View增加了animate方法来启动属性动画,代码如下所示:
view.animate()
                .setDuration(1000)
                .alpha(0)
                .y(300)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {

                    }
                }).withEndAction(new Runnable() {
                    @Override
                    public void run() {
        
                    }
        }).start();
3.Android布局动画

布局动画是指作用在ViewGroup上,给ViewGroup添加View时的一个动画过渡效果,在ViewGroup的布局XML中通过android:animateLayoutChanges="true"来打开布局动画,然后有新view加入时就会显示默认的布局动画效果了。
另外还可以通过LayoutAnimationController类来自定义一个子view的过渡效果:
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
        sa.setDuration(500);
        LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5f);
        lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
        ll.setLayoutAnimation(lac);
LayoutAnimationController构造方法的第一个参数是需要作用的动画,第二个参数是每个子view显示的间隔时间。当间隔不为0时可以为子view设置显示顺序:
  • LayoutAnimationController.ORDER_NORMAL
  • LayoutAnimationController.ORDER_RANDOM
  • LayoutAnimationController.ORDER_REVERSE
4.自定义动画
创建自定义动画需要继承自Animation类,然后重写applyTransformation方法就可以了,有时还要重写initialize()方法来完成一些初始化的工作。applyTransformation方法有以下几个参数:
applyTransformation(float interpolatedTime,Transformation t);
interpolatedTime这个值就是插值器(Interpolator)的时间因子,这个值有当前动画完成的百分比和当前时间对应的插值计算得来,范围是0到1.0,第二个参数Transformation是矩阵的封装类,一般用它来获取当前的矩阵对象:
final Matrix matrix = t.getMatrix();
通过改变matrix来实现动画效果,下面代码实现了模拟电视机关闭屏幕的动画效果:
public class CustomAnimation extends Animation {
    private int mCenterWidth;
    private int mCenterHeight;
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCenterWidth = width / 2;
        mCenterHeight = height / 2;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        final Matrix matrix = t.getMatrix();
        matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight);

    }
}
结合 android.graphics.Camera类还可以实现3D的切换效果,下面是一个3D的旋转动画的代码:
public class CustomAnimation extends Animation {
    private int mCenterWidth;
    private int mCenterHeight;
    private Camera mCamera;
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCenterWidth = width / 2;
        mCenterHeight = height / 2;
        mCamera = new Camera();
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        final Matrix matrix = t.getMatrix();
        mCamera.save();
        mCamera.rotateY(interpolatedTime * 360);
        mCamera.getMatrix(matrix);
        mCamera.restore();
        matrix.preTranslate(-mCenterWidth, -mCenterHeight);
        matrix.postTranslate(mCenterWidth, mCenterHeight);

    }
}
5.SVG矢量动画
在Android5.0开始,增加了矢量图形的支持,来看一下什么是SVG
  • 可伸缩矢量图形(Scalable Vector Graphics)
  • 使用XML格式定义图形
  • 图像在放大或改变尺寸的情况下其图形质量不会损失
  • 万维网联盟标准
  • 与诸如DOM和XSL之类的W3C标准是一个整体。
path标签指令:
  • M = moveto(M X,Y)——将画笔移动到指定的坐标位置
  • H = horizontal lineto(L X,Y)——画直线到指定的坐标位置
  • V = vertical lineto(V,Y)——画垂直线到指定的坐标位置
  • C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY)——三次贝塞尔曲线
  • S = smooth  curveto(S X2,Y2,ENDX,ENDY)——三次贝塞尔曲线
  • Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY)——二次贝塞尔曲线
  • T = smooth quadratic Belzier curve(T ENDX,ENDY)——映射前面路径后的终点
  • A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y)——弧线
  • Z = closepath()——关闭路径
在使用以上命令时要注意:
  • 坐标以(0,0)为中心,X轴水平向右,Y轴竖直向下。
  • 所有指令大小写均可,大写绝对定位,参照全局坐标系,小写相对定位,参照父容器坐标系。
  • 指令和数据间的空格可以省略。
  • 同一指令出现多次可以只用一个。
下面来看SVG常用命令的使用方法:
  • L
绘制直线的指令是L,L之后的参数是一个坐标如“L 200,400”还可以使用H和V来绘制水平和竖直线
  • M
M类似绘图中的path类的moveTo方法,即代表将画笔移动到某一点,并不发生绘制动作
  • A
A指令用来绘制一段弧线,且允许弧线不闭合。可以把A命令绘制的弧线想象成是椭圆的某一段,A指令有以下几个参数:
  1. RX,RY指所在椭圆的半轴大小
  2. XROTATION指椭圆的X轴与水平方向顺时针夹角,可以想象成一个水平的椭圆绕中心点顺时针旋转XROTATION的角度。
  3. FLAG1只有两个值,1表示大角度弧线,0表示小角度弧线
  4. FLAG2只有两个值,确定从起点到终点的方向,1为顺时针,0为逆时针。
  5. X,Y为终点坐标
Android中使用SVG
Android中提供了VectorDrawable和AnimatedVectorDrawable来支持SVG。
VectorDrawable用于创建基于XML的SVG图形,并结合AnimatedVectorDrawable来实现动画效果。
下面是VectorDrawable的使用示例:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="100dp"
    android:height="100dp"
    android:viewportHeight="100"
    android:viewportWidth="100"
    >
    <group
        android:name="test"
        android:rotation="0"
        android:pivotX="50"
        android:pivotY="50"
        >
        <path
            android:strokeColor="@android:color/holo_orange_dark"
            android:strokeWidth="2"
            android:pathData="
            M 25,50
            a 25,25 0 1,0 50,0"
            >

        </path>
    </group>

</vector>

其中的fillColor属性用来绘制填充的图形,绘制非填充的图形使用strokeColor结合strokeWidth来实现。

AnimatedVectorDrawable的作用是给VectorDrawable提供动画效果,它将VectorDrawable和属性动画进行结合,就可以实现具有动画效果的Vector了。
下面是AnimatedVectorDrawable使用示例:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_drawable"
    >
    <target
        android:animation="@animator/anim"
        android:name="test"/>

</animated-vector>
其中的animation属性指向的是属性动画Animator。来看一下这个动画:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:interpolator="@android:interpolator/accelerate_cubic"
    android:valueFrom="0"
    android:valueTo="360"
    >

</objectAnimator>

AnimatedVectorDrawable创建好后,可以通过给ImageView等空间设置src属性进行设置然后在代码中通过((Animatable)imageView.getDrawable()).start();来开启动画。

线图动画

通过将属性动画的propertyName属性设置为pathData,就可以根据VectorDrawable的pathData属性来进行动画了。下面是使用示例,展示里两条平行线变换成叉的动画,

这是VectorDrawable文件:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100"
    >
    <group>
        <path android:name="path1"
            android:strokeWidth="2"
            android:strokeColor="@android:color/holo_orange_dark"
            android:strokeLineCap="round"
            android:pathData="
            M 20,80
            L 50,80 80,80"
            />
        <path android:name="path2"
            android:strokeWidth="2"
            android:strokeColor="@android:color/holo_orange_dark"
            android:strokeLineCap="round"
            android:pathData="
            M 20,20
            L 50,20 80,20"
            />
    </group>

</vector>
属性动画文件,只列出了其中的一个,两个动画类似:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="pathData"
    android:duration="500"
    android:valueFrom="
        M 20,80
        L 50,80 80,80"
    android:valueTo="
        M 20,80
        L 50,50 80,80"
    android:valueType="pathType"
    android:interpolator="@android:anim/accelerate_interpolator"
    >
</objectAnimator>
AnimatedDrawable文件:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_cross"
    >
    <target
        android:animation="@animator/anim_path1"
        android:name="path1"/>
    <target
        android:animation="@animator/anim_path2"
        android:name="path2"/>
</animated-vector>

需要注意的是当属性动画的propertyName指定为pathData时,valueType属性也要指定为pathType的类型。

下面是一个搜索框的动画效果:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="60dp"
    android:width="320dp"
    android:viewportHeight="30"
    android:viewportWidth="160"
    >
    <group>
        <path android:name="search"
            android:strokeWidth="1.5"
            android:strokeAlpha="0.0"
            android:strokeColor="#ff3570be"
            android:strokeLineCap="round"
            android:pathData="
                M141,17
                A9,9 0 1,1 142,16
                L149,23"
            />
        <path android:name="bar"
            android:strokeWidth="1.5"
            android:strokeAlpha="0.8"
            android:strokeColor="#ff3570be"
            android:strokeLineCap="square"
            android:pathData="
                M0,23
                L149,23"
            />
    </group>

</vector>

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="trimPathEnd"
    android:valueFrom="0"
    android:valueTo="1"
    android:duration="1000"
    android:interpolator="@android:interpolator/accelerate_decelerate"
    android:valueType="floatType"
    >

</objectAnimator>
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_search">
    <target
        android:animation="@animator/anim_search"
        android:name="search"/>
    <target
        android:animation="@animator/anim_alpha"
        android:name="search"/>
</animated-vector>



 
 
















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值