Android动画机制与使用技巧

Android View动画框架

透明度动画

为视图增加透明度的变换动画。

AlphaAnimation aa = new AlphaAnimation(0,1);
aa.setDuration(1000);
view.startAnimation(aa);

旋转动画

为视图增加旋转的变换动画。

RotateAnimation ra = new RotateAnimation(0,360,100,100);
ra.setDurayion(1000);
view.startAnimation(ra);

其参数分别为旋转的起始角度和旋转中心点的坐标,当然,可以通过设置参数来控制旋转动画的参考系,代码如下所示(设置旋转参考系为自身中心点)。

RotateAnimation ra = new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5F,
RotateAnimation.RELATIVE_TO_SELF,0.5F);

位移动画

为视图移动时增加位移动画。

TranslateAnimation ta = new TranslateAnimation(0,200,0,300);
ta.setDuration(1000);
view.startAnimation(ta);

缩放动画

为视图的缩放增加动画效果

ScaleAnimation sa = new ScaleAnimation(0,2,0,2);
sa.setDuration(1000);
view.startAnimation(sa);

缩放动画也可以设置缩放的中心点,代码如下:

ScaleAnimation sa = new ScaleAnimation(0,1,0,1,
Animation.RELATIVE_TO_SELF,0.5F,
Animation.RELATIVE_TO_SELF,0.5F);
sa.setDuration(1000);
view.startAnimation(sa);

动画集合

通过AnimationSet,可以将动画以组合的形式展现出来。

对于动画事件,Android也提供了对应的监听回调,要添加对应的监听方法,代码如下:

animation.setAnimationListener(new Animation.AnimationListener(){

@Override
public void onAnimationStart(Animation animation){
}
@Override
public void onAnimationEnd(Animation animation){}

@Override
public void onAnimationRepeat(Animation animation){}

});

Android属性动画分析

Android3.0之前已有的动画框架Animation存在一些局限性——动画改变的只是显示,并不能响应事件。在3.0之后,提出了属性动画这样一个新的动画框架,用来实现更加丰富的动画效果。

在Animation框架中使用最多的就是AnimationSet和ObjectAnimator配合,ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值,而使用多个ObjectAnimator组合到AnimatorSet形成一个动画。而且ObjectAnimator能够自我驱动,可以调用setFrameDelay(long frameDelay)设置动画帧之间的间隙时间、调整帧率,减少动画过程中频繁绘制界面,而在不影响动画效果的前提下减少CPU的资源消耗。

最重要的是,属性动画通过调用属性的get、set方法来真实地控制了一个View的属性值。

ObjectAnimator

ObjectAnimator是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需要通过静态工厂类直接返回一个ObjectAnimator。参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数,内部会通过Java反射机制来调用set函数修改对象属性值。同样,可以调用setInterpolator设置相应的插值器。

例如:

ObjectAnimator animator = ObjectAnimator.ofFloat(view,"translationX",300);

注意:在使用ObjectAnimator的时候,要注意一点,那就是要操作的属性必须具有get、set方法,不然ObjectAnimator就无法起效。

下面是常用的可以直接使用属性动画的属性值:

  1. translationX和translationY:这两个属性作为一种增量来控制着View对象从它布局容器的左上角坐标偏移的位置。
  2. rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D旋转。
  3. scaleX和scaleY:这两个属性控制着View对象围绕它的支点进行2D缩放。
  4. pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行缩放变换处理。默许情况下,该支点的位置就是View对象的中心点。
  5. x和y:这是两个简单实用的属性,它描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX、translationY值的累计和。
  6. alpha:它表示View对象的alpha透明度。默认是1(不透明),0代表完全透明(不可见)。

如果一个属性没有get、set方法。可以通过以下两种方法来解决。

  1. 通过自定义一个属性类或者包装类,来间接地给这个属性增加get、set方法。
  2. 通过ValueAnimator来实现。

先来看看第一种方法。

private static class WrapperView{

    private View mTarget;

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

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

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

通过以上代码,就给一个属性包装了一层。使用方法如下:

ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();

PropertyValuesHolder

类似视图动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX",300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY",1f,0,1f);

ObjectAnimator.ofPropertyValuesHolder(view,pvh1,pvh2,pvh3).setDuration(1000).start();

ValueAnimator

ValueAnimator在属性动画中占据着非常重要的地位,是属性动画的核心。ObjectAnimator也是继承自ValueAnimator。

ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程,ValueAnimator的一般使用方法如下所示,通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变换,从而完成变换。

ValueAnimator animator = ValueAnimator.ofFloat(0,100);
   animator.setTarget(mImageView);
   animator.setDuration(1000).start();
   animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){

       @Override
       public void onAnimationUpdate(ValueAnimator animation){
           Float value = (Float)animation.getAnimatedValue();
           Log.d("yhp","value:" + value);
       }

   });

动画事件的监听

一个完成的动画具有Start、Repeat、End、Cancel四个过程。

 ObjectAnimator anim = ObjectAnimator.ofFloat(mImageView,"alpha",1f,0.5f);
        anim.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) {

            }
        });

当然,大部分的时候,只关心onAnimationEnd事件,所以AnimatorListenerAdapter来让我们选择必要的事件进行监听。


anim.addListener(new AnimatorListenerAdapter(){

    @Override
    public void onAnimationEnd(Animator animation){}

});

AnimatorSet

对一个属性同时作用于多个属性动画效果,用PropertyValuesHolder来实现,而AnimatorSet不仅能实现这样的效果,同时也能实现更为精确的顺序控制。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,"translationX",300f);

ObjectAnimator animator2 = ObjectAnimator.ofFloat(view,"scaleX",1f,0f,1f);

ObjectAnimator animator3 = ObjectAnimator.ofFloat(view,"scaleY",1f,0f,1f);

AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1,animator2,animator3);
set.start();

通过playTogether()、playSequentially()控制。

在XML中使用属性动画

例如:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType"
    >

</objectAnimator>

在代码中使用的时候也非常简单:

Animator anim = AnimatorInflater.loadAnimator(this,R.animator.scalex);
anim.setTarget(view);
anim.start();

View的animate方法

在Android 3.0之后,View增加了animate方法来直接驱动属性动画,可以认为是属性动画的一种简写方式。代码如下:

view.animate().alpha(0).y(300).setDuration(300)
               .withStartAction(new Runnable() {
                   @Override
                   public void run() {

                   }
               }).withEndAction(new Runnable() {
           @Override
           public void run() {
               runOnUiThread(new Runnable() {
                   @Override
                   public void run() {

                   }
               });
           }
       });

Android布局动画

所谓布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加一个动画过渡效果。最简单的布局动画是在ViewGroup的XML中,使用如下代码来打开布局动画。

android:animateLayoutChanges="true"

还可以通过使用LayoutAnimationController类来自定义一个子View的过渡效果,代码如下:

LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
//设置过渡动画
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);

//设置布局动画的显示属性
LayoutAnimationController lac = new LayoutAnimationController(sa,0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);

//为ViewGroup设置布局动画
ll.setLayoutAnimation(lac);

LayoutAnimationController的第一个参数,是需要作用的动画。而第二个参数,则是每个子View显示的delay时间。当delay时间不为0时,可以设置子View显示的顺序。

LayoutAnimationController.ORDER_NORMAL(顺序)
LayoutAnimationController.ORDER_RANDOM(随机)
LayoutAnimationController.ORDER_REVERSE(反序)

Interpolators(插值器)

插值器是动画中一个非常重要的概念,通过插值器,可以定义动画变换速率。

自定义动画

创建自定义动画很简单,继承Animation。只需要实现它的applyTransformation的逻辑就可以,还需要覆盖父类的initialize方法来实现一些初始化工作。

applyTransformation(float interpolatedTime, Transformation t)

第一个参数interpolatedTime就是插值器的时间因子,这个因子是由动画当前完成的百分比和当前时间所对应的插值所计算得来的,取值范围为0~1.0。

第二个参数Transformation,它是矩阵的封装类,一般使用这个矩阵对象,代码如下:

final Matrix matrix = t.getMatrix();

通过改变获得的matrix对象,将动画效果实现出来。对于matrix的变换操作,基本可以实现任何效果的动画。

@Override
protected void applyTransformation(){

    final Matrix matrix = t.getMatrix();
    //通过matrix的各种操作来实现动画
    matrix.***;
}

Android 5.X SVG矢量动画机制

SVG:

  1. 可伸缩矢量图形
  2. 定义用于网络的基于矢量的图形
  3. 使用XML格式定义图形
  4. 图形在放大或改变尺寸的情况下其图形质量不会所有损失

SVG比Bitmap(位图)的好处:

Bitmap(位图)通过在每个像素点上存储色彩信息来表达图像,而SVG是一个绘图标准。SVG最大的优点就是放大不会失真。而且Bitmap需要为不同分辨率设计许多套图标,而矢量图形则不需要。

标签

使用创建SVG,标签所支持的指令有以下几种。

M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制
L = lineto(L X,Y):画直线到指定的坐标位置
H = horizontal lineto(H X):画水平线到指定的X坐标位置
V = vertical lineto(V Y):画垂直线到指定的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 curveto(T ENDX,ENDY):映射前面路径后的终点
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
Z = closepath() : 关闭路径

在使用上面的指令时,需要注意以下几点:

坐标轴以(0,0)为中心,X轴水平向右,Y轴水平向下。

所有指令大小写均可。大写绝对定位,参照全局坐标系。小写相对定位,参照父容器坐标系。

指令和数据间的空格可以省略。

同一个指令出现多次可以只用一个。

Android中使用SVG

在Android 5.X中提供了下面两个新的API来帮助支持SVG:

VectorDrawable (创建基于XML的SVG图)

AnimatedVectorDrawable(实现动画效果)

VectorDrawable

在XML中创建一个静态的SVG图形。path是SVG树形结构中的最小单位,而通过Group可以将不同的path进行组合。

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100"
>
</vector>

height,width表示该SVG图形的具体大小,而viewportHeight,viewportWidth表示SVG图形划分的比例。比如上面的代码。将200dp话费为100份,如果在绘制时使用坐标(50,50),则意味着该坐标位于该SVG图形正中间。

所以,width、height的比例与viewportWidth,viewportHeight的比例保持一致,不然图形就会发生压缩,形变。

标签增加显示path,代码如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100"
    <group
        android:name="test"
        android:rotation="0">
        <path
            android:fillColor="@android:color/holo_blue_light"
            android:pathData="
                M 25 50
                a 25,25 0 1,0 50,0
            "/>

    </group>

</vector>

通过添加标签和标签来绘制一个SVG图形,其中pathData就是绘制SVG图形所用到的指令。

AnimatedVectorDrawable

AnimatedVectorDrawable的作用就是给VectorDrawable提供动画效果。AnimatedVectorDrawable比喻为一个胶水,通过AnimatedVectorDrawable来连接静态的VectorDrawable和动态的objectAnimator。

AnimatedVectorDrawable的使用通过以下几步:

  1. 在XML文件中通过标签来声明对AnimatedVectorDrawable的使用,并指定其作用的path或者group。


对应的vector即为静态的VectorDrawable。

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="200dp"
    android:width="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100"
    <group
        android:name="test"
        android:rotation="0">
        <path
            android:strokeColor="@android:color/holo_blue_light"
            android:strokeWidth="2"
            android:pathData="
                M 25 50
                a 25,25 0 1,0 50,0
            "/>

    </group>

</vector>

AnimatedVectorDrawable中指定的target的name属性,必须与VectorDrawable中需要作用的name属性保持一致,这样系统才能找到要实现动画的元素。最后通过AnimatedVectorDrawable中target的animation属性,将一个动画作用到了对应name的元素上,objectAnimator代码如下:

<objectAnimator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360"
/>

对动画效果的实现,通过属性动画来实现的,只是属性捎有不同。

标签和标签中添加了rotation、fillColor、pathData等属性,那么在objectAnimator中就可以通过指定android:propertyName=”XXXX”属性,可以控制动画的起始值。

如果指定属性为pathData,那么需要添加一个属性 android:valueType=”pathType”来告诉系统进行pathData变换。

当所有的xml文件准备好了以后,就可以在代码中控制SVG动画,可以非常方便地将一个AnimatedVectorDrawable XML文件设置给一个ImageView作为背景。

<ImageView
    android:id="@+id/image"
    android:src="@drawable/anim_vector"
    ......
/>

在程序中,使用以下代码,即可开始SVG动画。

((Animatable)imageView.getDrawable()).start();