在应用的开发中,动画是必不可少的一部分,否则很多视觉效果会显得特别突兀。今天我们就来了解一下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);
然后执行通过view的setAnimation或者startAnimation设置动画即可。
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.5f, 1.0f, 0.5f,0.5f, 0.5f);
scaleAnimation.setDuration(1000);
xml 定义动画:
TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 100) ;
translateAnimation.setDuration(1000);
RotateAnimation rotateAnimation = new RotateAnimation(0, 360) ;
rotateAnimation.setDuration(1000);
帧动画可以想象为一部很短的电影,他由数量有限的关键帧组成,当这个帧动画时这些关键帧快速的切换,从而达到动画的效果。
使用帧动画与补间动画不太一样,示例如下 :
// 通过逐帧动画的资源文件获得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):