**
1. Android View 动画框架
**
Animation框架定义了透明度、旋转、缩放和位移几种常用的动画,而且控制的是整个View,实现原理是每次绘制视图时View所在的ViewGroup中的drawChild函数获取该View的Animaion的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧。如果动画没有完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
1.1 透明动画
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
as.addAnimation(aa);
1.2 旋转动画
//设置旋转参数以自身中心点
RotateAnimation ra = new RotateAnimation(0, 360,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F);
ra.setDuration(1000);
view.startAnimation(ra);
1.3 位移动画
//设置以自身中心点
TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
ta.setDuration(1000);
view.startAnimation(ta);
1.4 缩放动画
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1,
Animation.RELATIVE_TO_SELF, 0.5F,
Animation.RELATIVE_TO_SELF, 0.5F);
sa.setDuration(1000);
view.startAnimation(sa);
1.5 动画集合
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);
对于动画时间,Android也提供了对应的监听回调,要添加相应的监听方法
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
view.startAnimation(aa);
aa.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
通过这个监听回调,可以获取到动画的开始、结束和重复事件,并针对相对事件做不同的处理。
2.Android 属性动画分析
属性动画的出现,是因为Android3.0之前的动画框架Animation存在着局限性,改变显示,没有响应事件。
2.1 ObjectAnimator
ObjectAnimator通过他的静态工厂类直接返回一个ObjectAnimator对象。
例子:
ObjectAnimator animator = ObjectAnimator.ofFloat(
view,
"translationX",
300);
animator.setDuration(300);
animator.start();
第一个参数,需要操纵的View,第二个参数则是要操纵的属性,最后一个参数是一个可变数组参数。
使用ObjectAnimator的时候,要操纵的属性必须具有get、set方法,不然ObjectAnimator就无法起效。下面就是常用的可以直接使用属性动画的属性值。
1.translationX和translationY:这两个属性作为一种增量来控制着View对象从它布局容器的左上角坐标偏移的位置.
2.roation、ratationX和rationY:这三个属性控制View对象围绕支点进行2D和3D旋转。
3.scaleX和scaleY:这两个属性控制着View对象围绕支点进行2D缩放
4.pivotX和pivotY:围绕支点进行旋转和缩放变换处理,默认支点的位置就是View对象的中心点。
5.x和y:描述View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX、translationY值的累积和。
6.alphabet:它表示View对象的alpha透明度。默认值是1(不透明),0代表透明
2.2 PropertyValuesHolder
类似AnimationSet,同时作用多种动画。
例子:
PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofFloat("translationX",300);
PropertyValuesHolder propertyValuesHolder1 = PropertyValuesHolder.ofFloat("ScaleX",1.0f,0.2f,1.0f);
ObjectAnimator.ofPropertyValuesHolder(view,propertyValuesHolder,propertyValuesHolder1).setDuration(300).start();
2.3 ValueAnimator
ValueAnimator本身不提供动画过程,让调用者来控制动画的实现过程。
通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变换,从而完成动画变换。
final ValueAnimator animator1 = ValueAnimator.ofFloat(0,100);
animator1.setTarget(view);
animator1.setDuration(1000).start();
animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animator1.getAnimatedValue();
}
});
2.4 动画事件的监听
一个完整的动画具有Start、Repeat、End、Cancel四个过程。
ObjectAnimator anim = ObjectAnimator.ofFloat(view,"alpha",0.5f);
anim.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) {
}
});
anim.start();
大部分时候,我们只关心onAnimationEnd事件,所以Android也提供了一个AnimationListenerAdapter来让我们选择必要的事件进行监听
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
2.6 AnImatorSet
多个属性动画效果,AnimatorSet比PropertyValuesHolder的优势在于实现更为精准的顺序控制。
ObjectAnimator propertyValuesHolder = ObjectAnimator.ofFloat(view,"translationX",300);
ObjectAnimator propertyValuesHolder1 = ObjectAnimator.ofFloat(view,"ScaleX",1.0f,0.2f,1.0f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(propertyValuesHolder,propertyValuesHolder1);
set.start();
在属性动画中,AnimatorSet正是通过playTogether()、playSequentially()、animSet.play()、with()、before()、after()这些方法来控制多个动画的协同工作方式。
2.6 在XML中使用属性动画
属性动画同视图动画一样,也可以直接写在XML文件中,代码如下
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:propertyName="trimPathStart"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" />
在程序中使用XML定义的属性动画
public void scaleX(View view){
Animator animator = AnimatorInflater.loadAnimator(this,R.animator.scalex);
animator.setTarget(mMv);
animator.start();
}
2.7 View的animate方法
可以认为是属性动画的一种简写方式
imageView.animate().alpha(0).y(300).setDuration(300).withStartAction(new Runnable() {
@Override
public void run() {
}
}).withEndAction(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
});
**
3.Android布局动画
**
所谓布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加一个动画过度效果。
最简单的布局动画是在ViewGroup的XML中,使用一下代码来打开布局动画。
android:animateLayoutChanges="true"
通过以上代码设置,当ViewGroup添加View时,子View会呈现逐渐显示的过度效果,这个是默认效果,且无法用自定义的动画来替换这个效果。
另外,还可以通过使用LayoutAnimationController类来定义一个子View的过度效果,代码如下
LinearLayout ll = (LinearLayout)findViewById(R,id.ll);
//设置过度动画
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);
//设置动画的显示属性
LayoutAnimationController lac = new LayoutAnimationController(sa,0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//为ViewGroup设置布局动画
ll.setLayoutAnimation(lac);
这是为子View设置一个缩放的动画效果
LayoutAnimationController的第一个参数,是需要作用的动画,而第二个参数,则是umeige子View显示的delay时间。当delay时间不为0时。可以设置子View显示的顺序,如下所示:
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);//顺序
lac.setOrder(LayoutAnimationController.ORDER_RANDOM);//随机
lac.setOrder(LayoutAnimationController.ORDER_REVERSE);//反序
```
**Interpolator(插值器)**
主要作用是控制目标变量的变化值进行对应的变化。
4.自定义动画
创建自定义动画,只需要实现它的applyTransformation的逻辑就可以了,不过通常情况下,还需要覆盖父类的iniaialize方法来实现一些初始化工作。applyTransformation方法有如下所示的两个参数。
applyTransformation(
float interpolatedTime,
Transformation t)
第一个参数interpolatedTime就是插值器的时间因子,这个因子是由动画当前完成的百分比和当前时间所对应的插值器所计算来的,取值范围为0到1.0.
跌二个参数Transformation非常简单,它是矩形的封装类,一般使用这个类来获取当前的矩形对象,代码如下
final Matrix matrix = t.getMatrix();
通过改变获得的matrix对象,可以将动画效果实现出来,而对于matrix的变换操作,基本可以实现任何效果的动画。
@Override
protected void applyTransformation(
float interpolatedTime,
Transformation t) {
final Matrix matrix = t.getMatrix();
matrix.preScale(1,
1 - interpolatedTime,
mCenterWidth,
mCenterHeight);
}
具体的矩阵处理方法:
final Matrix matrix = t.getMatrix();
matrix.preScale(1,
1 - interpolatedTime,
mCenterWidth,
mCenterHeight);
}
其中mCenterWidth、mCenterHeight即为缩放的中心点,设置为图片的中心。
模拟电视机关闭的动画:
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);
}
}
public void imgClose(View view) {
CustomTV customTV = new CustomTV();
view.startAnimation(customTV);
}
其实,你可以设置更精准的插值器,并将0到1.0的时间因子拆分成不同的过程,从而对不同的过程采用不同的动画效果,模拟更加真实的特效。
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);
}
}
public void btnAnim(View view) {
CustomAnim customAnim = new CustomAnim();
customAnim.setRotateY(30);
view.startAnimation(customAnim);
}