标签: android中级 读书笔记
Android动画机制与使用技巧
包括:
Android视图动画
Android属性动画
还包括下一篇的 Android5.X引入的 SVG 矢量动画
7.1 Android View动画框架
Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View
视图动画使用很简单,提供了AlphaAnimation,RotateAnimation,TranslateAnimation,ScaleAnimation四种动画方式,并且提供了AnimationSet动画集合,混合使用多种动画
可以使用XML文件来描述一个动画过程,同样也可以使用代码来控制整个动画过程
透明度动画
AlphaAnimation aa=new AlphaAnimation(0,1);
aa.setDuration(1000);
view.starAnimation(aa);
旋转动画
RotateAnimation ra=new RotateAnimation(0,360,100,100);
ra.setDuration(1000);
view.startAnimation(ra);
也可以将旋转中心设置为自身中心点
RotateAnimation ra=new RotateAnimation(0,360,RorateAnimation,RELATIVE_TO_SELF,0.5F,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);
也可以设置旋转中心为自身中心
动画集合
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);
对于动画事件,可以设置对应的监听回调
animation.setAnimationListener(new Animation.AnimationListener(){
@Override
public void onAnimationStart(Animation animation){
}
@Override
public void onAnimationEnd(Animation animation){
}
@Override
public void onAnimationRepeat(Animation animation){
}
});
7.2 Android属性动画分析
Animation(即视图动画)存在的一些局限性—例如改变的只是显示,不能相应事件,所以引入了属性动画
Animator框架中使用最多就是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更加精细化的控制。而且可以调用setFrameDelay(longframDelay)设置动画帧之间的间隙时间,减少CPU资源的消耗。
7.2.1 ObjectAnimator
使用ObjectAnimator控制的对象的相应属性,该属性必须具有get和set函数。
ObjectAnimator animator =ObjectAnimator.ofFloat(view,”translationX”,300);
animator.setDuration(300);
animator.start();
常用的属性动画的属性值:
translateX和translationY:控制着View对象从它布局容器的左上角坐标的偏移量
rotation,rotationX和rotationY:控制View对象围绕支点进行2D和3D旋转
scaleX和scaleY:围绕支点进行2D缩放
pivotX和pivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认情况下就是View对象的中心点
x和y:描述了View对象在它的容器中的最终位置,它是translationX,translationY值得累计和
alpha:表示View对象的alpha透明度
如果一个属性没有get,set方法,可以采用包装类的方式,或者使用ValueAnimator来实现
private static WrapperView{
private View mTarget;
public WrapperView(View target){
mTarget=target;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(){
mTarget.getLayoutParams().width=width;
mTarget.requestLayout();
}
}
然后 就可以通过包装类来间接使用ObjectAnimator来使用属性动画
ViewWrapper wrapper=new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
7.2.2 PropertyValuesHolder
类似于视图动画中的AnimationSet,在属性动画中,可以使用PropertyValuesHolder来实现对同一个对象的多个属性,同时作用多种动画。例如在平移动画中,在平移的过程中同时改变X,Y轴的缩放
PropertyValuesHolder phv1=PropertyValuesHolder.ofFloat("translationX",300f);
PropertyValuesHolder phv2=PropertyValuesHolder.ofFloat("scaleX",1f,0,1f);
PropertyValuseHolder.ofFloat("scaleY",1f,0,1f);
ObjectAnimator.ofPropertyValuesHolder(view,phv1,phv2,phv3).setDuration(1000).start();
7.2.3 ValueAnimator
ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数值从而让调用者来控制动画的实现过程
ValueAnimator animator=ValueAnimator.ofFloat(0,100);
animator.setTarget(view);
animator.setDuration(3000).start();
animator.addUpdateListener(new AnimatorUpdateListener(){
@Override
public void onAnimatorUpdate(ValueAnimator animation){
Float value=(Float)animation.getAnimatedValue();
//TODO use the value
}
});
7.2.4 动画事件的监听
一个完整的动画具有 Start, Repeat,End,Cancel四个过程,
ObjectAnimator anim=ObjectAnimator.ofFloat(view,"alpha",0.5f);
anim.addListener(new AnimatorListener(){
//some Methods to Override...
});
不过大多数时候我们只关心onAnimationEnd事件,所以只需要通过AnimatorListenerAdapter进行监听就行
anim.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
}
});
7.2.5 AnimatorSet
AnimatorSet相比PropertyValuesHolder能实现更加精确的控制
ObejectAnimator animator1=ObjectAnimator.ofFloat(view,"translationX",300f);
ObejectAnimator animator2=ObjectAnimator.ofFloat(view,"scaleX",1f,0f,1f);
ObejectAnimator animator3=ObjectAnimator.ofFloat(view,"scaleY",1f,0f,1f);
AnimatorSet set=new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1,animator2,animator3);
set.start();
AnimatorSet可以通过playTogether(), playSequentially(), animSet.play().with(), before(), after()这些方法来控制多个动画的协同工作方式
7.2.6 在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>
然后直接在程序中使用
public void scaleX(View view){
Animator anim=Animator.loadAnimator(this,R.animator.scalex);
anim.setTarget(mMv);
anim.start();
}
7.2.7 View的animate方法
Android 3.0之后,可以使用View的animate方法直接驱动属性动画
view.animate().alpha(0).y(300).setDuration(300).withStartAction(new Runable(){
@Override
public void run(){ }
}).withEndAction(new Runable(){
@Override
public void run(){
runOnUiThread(new Runable(){
@Override
public void run(){ }
}
});
}
}).start();
7.3 Android布局动画
布局动画就是指ViewGroup增加View时添加一个动画过度效果。
最简单的布局动画就是在ViewGroup的XML中,使用以下代码来打开布局动画,从而实现默认的动画效果
android:animateLayoutChanges=”true”
另外还可以使用LayoutAnimationController类来自定义一个子View的过度效果
LinerLayout ll=(LinearLayout)findViewById(R.id.ll);
//设置过度动画
ScaleAnimation sa=new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);
//设置布局动画的显示属性
LayoutAnimatorController lac=new LayoutAnimationController(sa,0.5f);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//为ViewGroup设置布局动画
ll.setLayoutAnimation(lac);
LayoutAnimationController的第一个参数是需要作用的动画,第二个参数是每个子View显示的delay时间。当delay不为0时,可以设置子View的显示顺序
Interpolators(插值器)
通过插值器可以定义动画的变换速率,非常类似于物理中的加速度,其主要作用是控制目标变量的变化值进行对应的变化。在不同的插值器的作用下同一个动画的起始值,每个单位时间内所达到的变化值是不一样的。
7.5 自定义动画、
创建自定义动画非常简单,只需要实现它的applyTransformation的逻辑就可以了,不过通常情况下,还需要覆盖父类的initialize方法来进行一些初始化工作。
applyTransformation(float interpolateTime,Transformation t)//第一个参数为时间因子,取值范围0到1.0;第二个参数为一个矩阵的封装类,一般通过这个类来获得当前的矩阵对象:final Matrix matrix=t.getMatrix();
通过改变获得的matrix对象,就可以将动画效果实现出来
下面的代码示例实现一个类似于电视机关闭的动画
import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class CustomTV 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(1000);
// 动画结束后保留状态
setFillAfter(true);
// 设置默认插值器
setInterpolator(new AccelerateInterpolator());
mCenterWidth = width / 2;
mCenterHeight = height / 2;
}
// 暴露接口-设置旋转角度
public void setRotateY(float rorateY) {
mRotateY = rorateY;
}
@Override
protected void applyTransformation(
float interpolatedTime,
Transformation t) {
final Matrix matrix = t.getMatrix();
matrix.preScale(1,
1 - interpolatedTime,
mCenterWidth,
mCenterHeight);
}
}
也可以结合矩阵,并使用Camera类来实现一个自定义的3D动画。
这里的Camera类指的是android.graphics.Camera中的Camera类,它封装课openGL的3D动画,从而可以非常方便的创建3D动画效果
import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.animation.Animation;
import android.view.animation.BounceInterpolator;
import android.view.animation.Transformation;
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();
// 使用Camera设置旋转的角度
mCamera.rotateY(mRotateY * interpolatedTime);
// 将旋转变换作用到matrix上
mCamera.getMatrix(matrix);
mCamera.restore();
// 通过pre方法设置矩阵作用前的偏移量来改变旋转中心
matrix.preTranslate(mCenterWidth, mCenterHeight);
matrix.postTranslate(-mCenterWidth, -mCenterHeight);
}
}