书中的示例代码:github
本章主要介绍的是Android动画机制和使用技巧
1.Android视图动画分析
- 透明度动画:
AlphaAnimation aa=new AlphaAnimation(0,1);
aa.setDuration(1000);
view.startAnimation(aa);
- 旋转动画:
RotateAnimation ra=new RotateAnimation(0,360,100,100);//参数分别为起始角度,旋转到角度,中心xy坐标
ra.setDuration(1000);
view.startAnimation(ra);
也可以绕自身中心旋转:
RotateAnimation ra=new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);
ra.setDuration(1000);
view.startAnimation(ra);
- 位移动画:
TranslateAnimation ta=new TranslateAnimation(0,300,0,200);
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, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
sa.setDuration(1000);
view.startAnimation(sa);
- 动画集合:
AnimationSet as=new AnimationSet(true);
as.setDuration(1000);
AlphaAnimation aa=new AlphaAnimation(0,1);
aa.setDuration(1000);
as.addAnimation(aa);
TranslateAnimation ta=new TranslateAnimation(0,100,0,200);
ta.setDuration(1000);
as.addAnimation(ta);
view.startAnimation(as);
- 动画回调:
as.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时回调
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时回调
}
@Override
public void onAnimationRepeat(Animation animation) {
//重复时回调
}
});
- 总结:视图动画的效果比较局限,并且view本身是不会跟着平移动画移动的,只有在实现简单效果并且不需和用户发生交互时建议使用。
2.Android属性动画分析
属性动画是在3.0以后推出的,如果需兼容之前的版本需使用开源项目:NineOldAndroids(可以通过谷歌查询其用法,此处不做介绍)
属性动画通过调用属性的set、get方法来真实的控制一个View的属性,还可以调用setFrameDelay()
设置动画帧之间的间隙时间,减少动画过程中频繁绘制界面。
- ObjectAnimator:
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(view,"translationX",300);
objectAnimator.setDuration(1000);
objectAnimator.start();//属性动画的简单使用。
下面是些常用的属性:
* translationX
和translationY
:作为一种增量来控制着View对象从它布局容器左上角坐标的偏移位置。
* rotation
、rotationX
和rotationY
:这三个属性控制View对象围绕支点进行2D和3D的旋转。
* scaleX
和scaleY
:这两个属性控制着View对象围绕它的支点进行2D缩放。
* pivotX
和pivotY
:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转缩放,默认情况下是View的中心点。
* x
和y
:它描述了View对象在它容器中的最终位置,它是最初的左上角坐标的translationX
和translationY
值的累积和。
* alpha
:它表示View对象的透明度,1是不透明,0为全透明。
如果一个属性没有get、set方法时就需要用其他解决方案了,Google提供了两种方法来解决这个问题。
①通过一个自定义类来间接的提供这个属性的get、set方法:
private static class WrapperView{
//使用时就new一个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();
}
}
②使用ValueAnimator来实现:本文后面会介绍。
- PropertyValuesHolder:
类似于视图动画中的AnimationSet。这里直接上代码
PropertyValuesHolder holder0 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY", 300);
ObjectAnimator.ofPropertyValuesHolder(view, holder0, holder1, holder2).setDuration(1000);
- ValueAnimator:
ObjectAnimator继承自ValueAnimator。其本身不提供任何动画效果,它更像是一个数值发生器。
ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,100);
valueAnimator.setTarget(view);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//监听数值的变换,从而完成动画的变换
Float value= (Float) animation.getAnimatedValue();
}
});
- 动画事件的监听:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
objectAnimator.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) {
//动画重复
}
});
//大部分时候只需要监听结束事件,因此可以选择AnimatorListenerAdapter。
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
objectAnimator.start();
- AnimatorSet:
AnimatorSet不仅可以实现PropertyValuesHolder这样的效果(同时执行多个动画操作),它还能实现更为精确的顺序控制。(注意这里是AnimatorSet,而不是AnimationSet)
ObjectAnimator objectAnimator0=ObjectAnimator.ofFloat(view,"scaleX",1f,0,1f);
ObjectAnimator objectAnimator1=ObjectAnimator.ofFloat(view,"scaleY",1f,0,1f);
AnimatorSet set=new AnimatorSet();
set.setDuration(1000);
set.playTogether(objectAnimator0,objectAnimator1);
set.start();
控制顺序有以下方法:
* playTogether()
:一起执行。
* playSequentially()
:上个动画结束后下个才会执行。
* animSet.play(anim0).with(anim1)
:anim0和anim1一起执行。
* animSet.play(anim0).before(anim1)
:anim0在anim1之前执行。
* animSet.play(anim0).after(anim1)
:anim0在anim1之后执行。
- 在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"
android:valueTo="0.5">
</objectAnimator>
在代码中的使用:
Animator animator= AnimatorInflater.loadAnimator(getContext(),R.anim.test);
animator.setTarget(view);
animator.start();
- View的animate方法:
在3.0后,google公司给View增加了animate方法来直接驱动属性动画,代码如下:
//withStartAction和withEndAction在api16以上才有。
view.animate().alpha(0).y(300).setDuration(1900).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" //此为固定默认效果
当然我们也能够通过LayoutAnimationController
类来自定义一个子View的过渡效果。
LinearLayout ll=new LinearLayout(getContext());
//创建动画
ScaleAnimation sa=new ScaleAnimation(0,1,0,1);
sa.setDuration(1000);
//设置布局动画的显示属性
//第二个参数是每个子View显示的delay时间。当delay不为0时可以设置子View显示顺序
//LayoutAnimationController.ORDER_NORMAL 顺序
//LayoutAnimationController.ORDER_RANDOM 随机
//LayoutAnimationController.ORDER_REVERSE 反序
LayoutAnimationController controller=new LayoutAnimationController(sa,0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
ll.setLayoutAnimation(controller);
4.Interpolators(插值器)
通过插值器,可以定义动画变换的速率,类似于物理中的加速度。
- AccelerateDecelerateInterpolator --- 在动画开始与结束的地方速率改变比较慢,在中间的时候加速
- AccelerateInterpolator --- 在动画开始的地方速率改变比较慢,然后开始加速
- AnticipateInterpolator --- 开始的时候向后然后向前甩
- AnticipateOvershootInterpolator --- 开始的时候向后然后向前甩一定值后返回最后的值
- BounceInterpolator --- 动画结束的时候弹起
- CycleInterpolator --- 动画循环播放特定的次数,速率改变沿着正弦曲线
- DecelerateInterpolator --- 在动画开始的地方快然后慢
- LinearInterpolator --- 以常量速率改变
- OvershootInterpolator --- 向前甩一定值后再回到原来位置
以上基本能满足常用需求,也可以自定义一个插值器。
public class MyInterpolator implements Interpolator {
private float mFactor;
@Override
public float getInterpolation(float input) {
//通过操作input来改变速率
return mFactor;
}
}
5.自定义动画
创建自定义动画非常简单,只需要实现它的applyTransformation的逻辑,通常情况下还需要覆盖父类的initialize方法来实现一些初始化工作。
public class MyAnim extends Animation {
private int mWidth;
private int mHeight;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mWidth = width;
mHeight = height;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
//这个方法主要就是通过操作transformation中的矩阵对象来呈现动画效果
final Matrix matrix = t.getMatrix();
matrix.preScale(1, 1 - interpolatedTime, mWidth / 2, mHeight / 2);
}
}
6.Android 5.X SVG矢量动画机制
-
SVG是什么:
- 可伸缩矢量图形(Scalable Vector Graphics)
- 定义用于网络的基于矢量的图形
- 使用xml格式定义图形
- 图像在放大或者改变尺寸的情况下其图形质量不会有损失
- 万维网联盟的标准
- 与诸如DOM和XSL之类的W3C标准是一个整体
-
<path>标签:
使用<path>标签创建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):弧形。
RX、RY
为半轴大小,XROTATION
指椭圆的X轴水平方向顺时针方向夹角,FLAG1
为1时表示大角度弧形,为0表小角度,FLAG2
为1时表顺时针,0位逆时针,X、Y
为终点坐标 - Z=closepath():关闭路径。
注意:
① 坐标轴以(0,0)为中心,X轴水平向右,Y轴水平向下。
② 所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系。
③ 指令和数据间的空格可以省略。
④ 同一指令出现多次可以只用一个。
SVG编辑器:
书中介绍了个Inkscape,有兴趣的可以尝试下。-
Android中使用SVG:
谷歌在Android 5.X中提供了VectorDrawable
、AnimatedVectorDrawable
来帮助支出SVG。- VectorDrawable:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"//控制SVG图形的具体大小,height与width的比例需和viewportHeight与viewportWidth的相同,不然会变形
android:viewportHeight="100"
android:viewportWidth="100">//图形划分的比例,这里指划分100*100,绘制图形使用坐标(50,50)即为中心点
<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 1 50 0"
/>
</group>
</vector>
- AnimatedVectorDrawable:
AnimatedVectorDrawable就是给VectorDrawable提供动画效果,通过它来连接静态的VectorDrawable和动态的objectAnimator。
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector_demo">
<target
android:animation="@anim/anim"
android:name="test"/>//这里的name需要和vectordrawable里的name一样
</animated-vector>
动画代码如下:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:propertyName="rotation"//path中的属性
android:valueFrom="0"
android:valueTo="360">
</objectAnimator>
具体使用如下:
((Animatable)imageView.getDrawable()).start();