在应用的开发中,动画是必不可少的一部分,否则很多视觉效果会显得特别突兀。今天我们就来了解一下Android中的动画库。
View的alpha动画直接通过setAlpha(float al)函数来修改透明度实现,缩放、平移、旋转等都是通过图形矩阵变换来实现。图形变换是图形学中基本知识,简单来说就是,每种变换都是一次矩阵运算。在Android中,Canvas类包含当前矩阵,当前调用Canvas.drawBitmap(bm,x,y,Paint)绘制时,android会先把bmp做一次矩阵运算,然后将运算结果显示在Canvas上。这样编程人员只需不断修改Canvas的矩阵并刷新屏幕,View里对象就会不停的做图形变换,动画就形成了。
View设置动画,设置动画属性,在动画启动时,会调用invalidate方法重绘View。从而触发onDraw函数;在onDraw函数中,调用动画的getTransformation方法,得到当前时间点的矩阵,getTransformation函数又会调用动画的applyTransformation函数,applyTransformation函数通过一个计算因子 (由插值器通过动画执行的百分比计算出来的因子)来计算当前时刻View属性的值,修改View的属性达到图形变换。View会判断getTransformation的返回值,若为真,说明动画未完成,调用invalidate方法刷新屏幕,重绘View,从而进入下一帧动画,再次执行上述过程;若为假,说明动画完成。
AlphaAnimation alphaAnimation = new AlphaAnimation(0.3f, 1.0f) ; alphaAnimation.setDuration(1000);
<?xml version="1.0" encoding="utf-8"?> <!-- 透明度 --> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:fromAlpha="1" android:toAlpha="0" android:duration="1000" />然后执行通过view的setAnimation或者startAnimation设置动画即可。
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.5f, 1.0f, 0.5f,0.5f, 0.5f); scaleAnimation.setDuration(1000);xml 定义动画:
<?xml version="1.0" encoding="utf-8"?> <!-- 缩放 --> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:fromXScale="1" android:fromYScale="1" android:toXScale="0.5" android:toYScale="0.5" android:pivotX="0.5" android:pivotY="0.5" android:duration="3000" />
TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 100) ; translateAnimation.setDuration(1000);
<?xml version="1.0" encoding="utf-8"?> <!-- 移动 --> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="100" android:toYDelta="100" android:duration="000" />
RotateAnimation rotateAnimation = new RotateAnimation(0, 360) ; rotateAnimation.setDuration(1000);
<?xml version="1.0" encoding="utf-8"?> <!-- 旋转 --> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="360" android:pivotX="50%" android:pivotY="50%" android:duration="1000" />
帧动画可以想象为一部很短的电影,他由数量有限的关键帧组成,当这个帧动画时这些关键帧快速的切换,从而达到动画的效果。
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/p1" android:duration="200" /> <item android:drawable="@drawable/p2" android:duration="200" /> <item android:drawable="@drawable/p3" android:duration="200" /> <item android:drawable="@drawable/p4" android:duration="200" /> <item android:drawable="@drawable/p5" android:duration="200" /> <item android:drawable="@drawable/p6" android:duration="200" /> <item android:drawable="@drawable/p7" android:duration="800" /> <item android:drawable="@drawable/p8" android:duration="200" /> <item android:drawable="@drawable/p9" android:duration="200" /> <item android:drawable="@drawable/p10" android:duration="200" /> <item android:drawable="@drawable/p11" android:duration="200" /> </animation-list>使用帧动画与补间动画不太一样,示例如下 :
// 通过逐帧动画的资源文件获得AnimationDrawable示例 AnimationDrawable frameAnim = (AnimationDrawable) getResources().getDrawable(R.drawable.my_frame_anim); // 把AnimationDrawable设置为myView的背景 myView.setBackgroundDrawable(frameAnim);
在补间动画中,我们一般只定义首帧和尾帧,然后由系统自动生成中间帧,生成中间帧的这个过程可以成为“插值”。插值器的作用是告诉动画某个属性(比如颜色的渐变)如何随时间变化 ,在duration周期内每个时刻对应的属性值。下面是几种常见的插值器:
android 系统默认支持AccelerateDecelerateInterpolator 、 AccelerateInterpolator 、 AnticipateInterpolator 、AnticipateOverShootInterpolator 、 BounceInterpolator 、 CycleInterpolator 、DecelerateInerpolator 、 LinearInterpolator 、 OverShootInterpolator 共 9 种插值器(若这些插值器不能满足需求,则可以自定义插值器)。
例如,一个alpha动画在一秒内从0.0f到1.0f变换,线性插值器的动画为匀速执行的。但是加速插值器的动画会随着时间的增加而越来越快。
插值器通过getInterpolation方法获得插值因子,插值因子的计算也在此函数中完成。例如线性插值器,它的动画则是匀速运动的,因此在它的getInterpolation函数中插值因子只跟时间相关,与其他因素无关。getInterpolation(int inpput)函数中的参数即动画执行的百分比,也可以认为是动画已经执行时间和动画周期的百分比。该值为0.0f 到 1.0f,即动画的开始到结束。
/** * An interpolator where the rate of change is constant * */ public class LinearInterpolator implements Interpolator { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; } }再看加速插值器,即动画的速度会随着时间的推移而越来越快。我们看看它的 getInterpolation实现:
public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } }默认情况下mFactor会为1.0f, 随着动画的执行,input慢慢增大, getInterpolation函数返回的数值的大小变化范围会越来越大,从而导致动画变快。
自定义动画时,我们主要就是覆写applyTransformation(float interpolatedTime, Transformation t) 方法,在方法是在动画执行时就是调用该方法来实现动画操作,我们在这里需要实现修改alpha, 缩放,平移等动画。interpolatedTime是由插值器计算出来的因子,由动画的执行百分比与插值器的策略计算而来,值为0.0f 到 1.0f。参数t为矩阵变换的封装类。在这里我们只讨论使用矩阵实现的自定义动画。例如,我们需要定制一个动画,是它能够在低于API 11 版本的系统里实现Y轴的旋转动画,android中的RorateAnimation动画只支持在Z轴上的旋转动画,这是我们就可以通过矩阵来实现。关于矩阵的资料,请移步,Android中图像变换Matrix的原理、代码验证和应用(一),Android Matrix理论与应用详解,Android--Matrix图片变换处理。
/** * @author mrsimple */ public class RotateYAnimation extends Animation { private int halfWidth; private int halfHeight; // 使用Camera实现3D旋转 Camera mCamera = new Camera(); // protected float mRotateY = 0.0f; /** * width,height为执行该动画的view的宽度、高度。后面两个参数为执行该动画的view的parent的宽度、高度 */ @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); setDuration(2000); setFillAfter(true); halfWidth = width / 2; halfHeight = height / 2; setInterpolator(new LinearInterpolator()); } // 设置旋转角度 public void setRotateY(float rorateY) { mRotateY = rorateY; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); mCamera.save(); // 使用Camera设置旋转的角度 mCamera.rotateY(mRotateY * interpolatedTime); // 将旋转变换应用到matrix上 mCamera.getMatrix(matrix); mCamera.restore(); // 在变换前,将动画的中心点从默认的(0,0)移动(0, -halfHeight) matrix.preTranslate(0, -halfHeight); // 变换后再从(-halfWidth, -halfHeight)移动halfWidth,halfHeight,使之再次回到(0,0) matrix.postTranslate(0, halfHeight); // View view; } }
Button btn = new Button(MainActivity.this) ;
btn.setText("RotateY");
MyAnimation animation = new MyAnimation();
animation.setRotateY(45f);
animation.setDuration(2000);
// 启动动画
btn.startAnimation(animation);
效果 (效果图中的view并不是button,是我另外例子中的view):