Android动画机制与使用技巧
《Android群英传》第七章读书笔记
View动画 (视图动画)
视图动画(Animation)框架定义了透明度(AlphaAnimation)、旋转(RotateAnimation)、缩放(ScaleAnimation)和位移(TranslateAnimation)几种常见的动画,控制的是View的内容,所以视图动画的缺陷就在于当某个元素发生视图动画后,其响应事件的位置还依然停留在原来的地方!
实现原理是每次绘制视图时View所在的ViewGroup中的drawChild方法获取该View的Animation的Transformation值,然后调用canvas.concat(transformationToApply.getMatrix()),通过矩阵运算完成动画帧。如果动画没有完成,就继续调用invalidate方法,启动下次绘制来驱动动画,从而完成整个动画的绘制。
- 动画集合(AnimationSet):将多个视图动画组合起来
- 动画监听器(AnimationListener):提供动画的监听回调方法
属性动画
Android 3.0之后添加了属性动画(Animator)框架,其中核心类ObjectAnimator能够自动驱动,在不影响动画效果的情况下减少CPU资源消耗。
ObjectAnimator
创建ObjectAnimator只需通过它的静态工厂方法直接返回一个ObjectAnimator对象,参数包括view对象,以及view的属性名字,这个属性必须要有get/set方法,因为ObjectAnimator内部会通过反射机制来修改属性值。
常用的可以直接使用属性动画的属性包括:
- translationX和translationY:控制view从它布局容器左上角坐标偏移的位置;
- rotation、rotationX和rotationY:控制view围绕支点进行2D和3D旋转;
- scaleX和scaleY:控制view围绕着它的支点进行2D缩放;
- pivotX和pivotY:控制支点位置,围绕这个支点进行旋转和缩放处理。默认情况下,支点是view的中心点;
- x和y:控制view在它的容器中的最终位置,它是最初的左上角坐标和translationX、translationY的累计和;
- alpha:控制透明度,默认是1(不透明)。
ObjectAnimator的常见使用方式如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"translationX", 300);
animator.setDuration(1000);
animator.start();
属性动画集合AnimatorSet:控制多个动画的协同工作方式,常用方法animatorSet.play().with().before().after()、playTogether、playSequentially等方法来精确控制动画播放顺序。使用PropertyValueHolder也可以实现简单的动画集合效果。
动画监听器:监听动画事件可以使用AnimatorListener或者简易的适配器AnimatorListenerAdapter
如果一个属性没有get/set方法怎么办?
(1)自定义包装类,间接地给属性提供get/set方法,下面就是一个包装类的例子,为width属性提供了get/set方法
public class WrapperView {
private View mView;
public WrapperView(View mView){
this.mView = mView;
}
public int getWidth(){
return mView.getLayoutParams().width;
}
public void setWidth(int width){
mView.getLayoutParams().width = width;
mView.requestLayout();
}
}
(2)使用ValueAnimator
ObjectAnimator就是继承自ValueAnimator的,它是属性动画的核心,ValueAnimator不提供任何动画效果,它就是一个数值产生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程,控制的方式是使用AnimatorUpdateListener来监听数值的变换。
ValueAnimator animator = ValueAnimator.ofFloat(0,100);
animator.setTarget(view);
animator.setDuration(1000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
//do the animation!
}
});
在XML中使用属性动画
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
在代码中使用方式如下:
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_rotation);
animator.setTarget(view);
animator.start();
View的animate方法
Android 3.0之后View新增了animate方法直接驱动属性动画,它其实是属性动画的一种简写方式
imageView.animate().alpha(0).y(100).setDuration(1000)
.setListener(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) {
}
});
布局动画
布局动画是作用在ViewGroup上的,给ViewGroup添加view时添加动画过渡效果。
(1)简易方式(但是没有什么效果):在xml中添加如下属性 android:animateLayoutChanges=”true
(2)通过LayoutAnimationController来自定义子view的过渡效果,下面是一个常见的使用例子:
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll);
ScaleAnimation scaleAnimation = new ScaleAnimation(0,1,0,1);
scaleAnimation.setDuration(2000);
LayoutAnimationController controller = new LayoutAnimationController(scaleAnimation, 0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);//NORMAL 顺序 RANDOM 随机 REVERSE 反序
linearLayout.setLayoutAnimation(controller);
自定义动画
创建自定义动画就是要实现它的applyTransformation的逻辑,不过通常还需要覆盖父类的initialize方法来实现初始化工作。 下面是一个模拟电视机关闭的动画
public class CustomTV 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);
setDuration(1000);// 设置默认时长
setFillAfter(true);// 动画结束后保留状态
setInterpolator(new AccelerateInterpolator());// 设置默认插值器
mCenterWidth = width / 2;
mCenterHeight = height / 2;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight);
}
}
applyTransformation方法的第一个参数interpolatedTime是插值器的时间因子,取值在0到1之间;第二个参数Transformation是矩阵的封装类,一般使用它来获得当前的矩阵Matrix对象,然后对矩阵进行操作,就可以实现动画效果了
实现3D动画效果
使用android.graphics.Camera中的Camera类,它封装了OpenGL的3D动画。可以把Camera想象成一个真实的摄像机,当物体固定在某处时,只要移动摄像机就能拍摄到具有立体感的图像,因此通过它可以实现各种3D效果。
下面是一个3D动画效果的例子
public class CustomAnim extends Animation {
private int mCenterWidth;
private int mCenterHeight;
private Camera mCamera = new Camera();
private float mRotateY = 0.0f;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(2000);// 设置默认时长
setFillAfter(true);// 动画结束后保留状态
setInterpolator(new BounceInterpolator());// 设置默认插值器
mCenterWidth = width / 2;
mCenterHeight = height / 2;
}
// 暴露接口-设置旋转角度
public void setRotateY(float rotateY) {
mRotateY = rotateY;
}
@Override
protected void applyTransformation( float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
mCamera.save();
mCamera.rotateY(mRotateY * interpolatedTime);// 使用Camera设置旋转的角度
mCamera.getMatrix(matrix);// 将旋转变换作用到matrix上
mCamera.restore();
// 通过pre方法设置矩阵作用前的偏移量来改变旋转中心
matrix.preTranslate(mCenterWidth, mCenterHeight);
matrix.postTranslate(-mCenterWidth, -mCenterHeight);
}
}
SVG矢量动画机制
SVG是什么:
- 可伸缩矢量图形(Scalable Vector Graphics)
- 定义用于网络的基于矢量的图形
- 使用xml格式定义图形
- 图像在放大或者改变尺寸的情况下其图形质量不会有损失
- 万维网联盟的标准
- 与诸如DOM和XSL之类的W3C标准是一个整体