Animation框架定义了透明度、旋转、缩放和位移几种常见的动画,控制的整个View,实现原理是每次绘制视图时View所在ViewGroup中的drawChild函数获取该View的Animation的Transformation值,而后调用canvas.concat(transformToApply.getMatrix()),经过矩阵运算完成动画帧。若是没有完成就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
视图动画使用简单,效果丰富,它提供了AlphaAnimation、RotateAnimation、TranslateAnimation、ScaleAnimation四种动画方式,并提供动画集合AnimationSet,混合使用多种动画。在Android3.0以前,视图动画一家独大,但随着Android3.0以后属性动画框架的推出,它的风光就大不如从前。相比属性动画,视图动画的一个很是大的缺陷就是不具有交互性,当某个元素发生视图动画后,其响应事件的位置还依然在动画前的地方,因此视图动画只能作普通的动画效果,避免交互的发生。可是它的优势也很是明显,即效率比较高且使用方便。android
视图动画使用很是简单,不只能够经过XML文件来描述一个动画过程,一样也可使用代码来控制整个动画过程。canvas
为视图增长透明度的变换动画。bash
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
view.startAnimation(aa);复制代码
为视图增长旋转的变换动画。app
RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);
ra.setDuration(1000);
view.startAnimation(ra);复制代码
其参数分别为旋转的起始角度和旋转中心点的坐标,固然,能够经过设置参数来控制旋转动画的参考系,这里设置旋转动画的参考系为中心。框架
RotateAnimation ra1 = new RotateAnimation(0, 360, RotateAnimation.RELATIVE_TO_SELF, 0.5F, RotateAnimation.RELATIVE_TO_SELF, 0.5F);复制代码
为视图移动时增长位移动画。ide
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);复制代码
与旋转动画同样,缩放动画也能够设置罗芳的中心点,设置中心为自身中心效果布局
ScaleAnimation sa1 = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5F, Animation.RELATIVE_TO_SELF, 0.5F);
sa1.setDuration(1000);
view.startAnimation(sa1);复制代码
经过AnimationSet,能够将动画以组合的形式展示出来:post
AnimationSet as = new AnimationSet(true);
as.setDuration(1000);
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
as.addAnimation(aa);
RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);
ra.setDuration(1000);
as.addAnimation(ra);
TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
ta.setDuration(1000);
as.addAnimation(ta);
ScaleAnimation sa = new ScaleAnimation(0, 2, 0, 2);
sa.setDuration(1000);
as.addAnimation(sa);
view.startAnimation(as);复制代码
能够直接拷贝运行代码看效果!动画
对于动画事件,Android也提供了对应的监听回调,代码:
as.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复
}
});复制代码
因为Android3.0以前已有的动画框架Animation存在一些局限性——动画改变的只是显示,并不能响应事件。所以在Android3.0以后,Google就提出了属性动画这样一个新的动画框架,实现更丰富的效果。
而在Animator框架中使用最多的就是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值,而使用多个ObjectAnimator组合到AnimatorSet造成一个动画。并且ObjectAnimator可以自动驱动,能够调用setFrameDelay(long frameDelay)设置动画帧之间的间隙时间。最重要的是,属性动画经过调用属性的get、set方法来真实地控制了一个View的属性值,所以强大的属性动画框架,基本能够实现全部的动画效果。
ObjectAnimator是属性动画框架中最重要的实行类,建立一个ObjectAnimator只须要经过他的静态工厂类直接返回一个ObjectAnimator对象。参数包括一个对象和对象的属性名字,但这个属性也必须有get和set函数,内部会经过Java反射机制来调用set函数修改对象属性值。一样,你也能够调用setInterpolator设置相应的差值器。
接下来试想一下对一个Button添加一个平移动画,使用之前的动画框架平移后将不能触发点击事件,点击的有效区域仍然是原来的地方,点击移动后的地方是不会有点击事件发生的。而属性动画则不一样,它真实地改变了一个View的属性,因此事件响应的区域也一样发生了改变,这时候点击移动后的按钮,就会响应点击事件了。
属性动画平移代码以下:
ObjectAnimator animator = ObjectAnimator.ofFloat(
imageView,
"translationX",
200F);
animator.setDuration(300);
animator.start();复制代码
在使用ObjectAnimator的时候,有一点很是重要,那就是要操纵的属性必须具备get、set方法,否则ObjectAnimator就没法生效。下面是经常使用的属性:
根据以上得知视图动画所实现的动画效果,这里基本都已经包含了。
那么若是一个属性没有get、set方法,属性动画是否是就一筹莫展了呢?答案是否认的,Google在应用层提供了两种方案来解决这个问题,一个是经过自定义一个属性类或者包装类,来间接地给这个属性增长get、set方法;或者经过ValueAnimator来实现,ValueAnimator在后面的内容中讲到,这个先看看使用包装类的方法给一个属性增长get、set方法,代码以下:
private static class WrapperView {
private View mTarget;
public WrapperView(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}复制代码
经过以上代码,就跟一个属性包装了一层,并给它提供了get、set方法。使用时只须要操纵包装类就能够间接调用到get、set方法了,代码以下所示:
WrapperView wrapperView = new WrapperView(view);
ObjectAnimator.ofInt(wrapperView, "width", 500).setDuration(5000).start();复制代码
相似视图动画中的AnimationSet,在属性动画中,若是针对同一个对象的多个属性,要同时做用多种动画,可使用PropertyValuesHolder来实现。好比平移动画,若是在平移的过程当中同时改变X、Y轴的缩放,能够这样实现,代码:
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(pvh1, pvh2, pvh3).setDuration(1000).start();复制代码
在代码中,分别使用PropertyValuesHolder 对象控制translationX、scaleX、scaleY这三个属性,最屌调用ObjectAnimator.ofPropertyValuesHolder方法实现多属性动画的共同做用,整个实现方法很是相似AnimatorSet使用。
ValueAnimator在属性动画中占用很是重要的地位,虽然不ObjectAnimator那样耀眼,但它倒是属性动画的核心所在,ObjectAnimator也是继承自ValueAnimator。
public final class ObjectAnimator extends ValueAnimator复制代码
ValueAnimator自己不提供任何动画效果,它更像一个数值发生器,用来产生具备必定规律的数字,从而让调用者来控制动画的实现过程,ValueAnimator的通常使用方法:一般在ValueAnimator的AnimatorUpdateListener中监听数值的变换,完成动画的变换。
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 100);
valueAnimator.setTarget(imageView);
valueAnimator.setDuration(1000).start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
}
});复制代码
一个完整的动画具备Start、Repeat、End、Cancel四个过程,经过Android提供了接口,很方便地监听到这四个事件:
ObjectAnimator anim = ObjectAnimator.ofFloat(imageView, "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也提供了一个AnimatorListenerAdapter来让咱们选择必要的事件进行监听:
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});复制代码
对于一个属性同时做用多个属性动画效果,前面已经使用PropertyValuesHolder实现了这样的效果。而AnimatorSet不只能实现这样的效果,同时也能实现更为精确的顺序控制。一样是实现上面使用PropertyValuesHolder演示的那个动画效果,若是使用AnimatorSet来实现,那么代码以下:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 300f);
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(imageView, "scaleX", 1f, 0f, 1f);
ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(imageView, "scaleY", 1f, 0f, 1f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000);
animatorSet.playTogether(objectAnimator, objectAnimator1, objectAnimator2);
animatorSet.start();复制代码
在属性动画中,AnimatorSet正是经过playTogether()、playSquentially()、animSet.play().width()、defore()、after()这些方法来控制多个动画的协同工做方式,从而作到对动画播放顺序的精确控制。
属性动画同视图动画同样,也能够直接写在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>复制代码
前提使用XML定义属性动画XML文件必定要放在res/animator/filename.xml文件夹下面才能识别,不然不能识别。发现属性动画与视图动画在XML文件中的写法很类似。在程序中使用:
Animator anim = AnimatorInflater.loadAnimator(this,R.animator.filename);
anim.setTarget(view);
anim.start();复制代码
在Android3.0以后,Google给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() {
}
});
}
}).start();复制代码
布局动画是指做用在ViewGroup上,给ViewGroup增长View时添加一个动画过渡效果。最简单的布局动画是在ViewGroup的XML中,使用以下代码打开布局动画:
android:animateLayoutChanges="true"复制代码
经过以上设置,当ViewGroup添加到View时,子View会呈现逐渐显示的过渡效果,不过这个效果是Android默认的显示的过渡效果,没法使用自定义动画来替换这个效果。
还能够经过使用LayoutAnimatorController类自定义一个子View的过渡效果,添加一个视图动画,使得子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);
//设置布局动画
ll.setLayoutAnimation(lac);复制代码
LayoutAnimationController 的第一个参数,是须要做用的动画,而第二个参数,则是每一个子View显示的delay时间。当delay时间不为0时,能够设置子View显示的顺序。
//顺序
public static final int ORDER_NORMAL = 0;
//随机
public static final int ORDER_REVERSE = 1;
//反序
public static final int ORDER_RANDOM = 2;复制代码
插值器是动画一个很是重要的概念,经过插值器Interpolators,能够定义动画变换速率,这一点很是相似物理中的加速度,起做用主要是控制目标变量的变化值进行对应的变化。
建立自定义动画只须要实现它的applyTransformation的逻辑就能够了,不过一般状况下,还须要覆盖父类的initalize方法来实现初始化工做。applyTransformation方法以下:
protected void applyTransformation(
float interpolatedTime,
Transformation t) {
}复制代码
第一个参数interpolatedTime是上面说的插值器的时间因子,这个因子是由动画当前完成的百分比和当前时间所对应的插件所计算得来的,取值范围在0-1.0。
第二个参数Transformation 是矩阵的封装类,通常使用这个类来得到当前的局针对想,以下:
final Matrix matrix = t.getMatrix();复制代码
经过改变得到的matrix对象,能够将动画效果实现出来,而对于matrix的变换操做,基本能够实现任何效果的动画。对于matrix的介绍能够查看:深刻理解 Android 中的 Matrix
@Override
protected void applyTransformation(
float interpolatedTime,
Transformation t) {
final Matrix matrix = t.getMatrix();
//经过matrix的各类操做来实现动画
}复制代码
经过模拟电视机关闭的效果来看看简单的矩阵变化时如何实现动画效果的。电视机关闭的效果就是让一个图片纵向比例不断缩小便可,对应的矩阵动处理方法以下:
@Override
protected void applyTransformation(
float interpolatedTime,
Transformation t) {
final Matrix matrix = t.getMatrix();
matrix.preScale(1,
1 - interpolatedTime,
mCenterWidth,
mCenterHeight);
}复制代码
其中的mCenterWidth、mCenterHeight即为缩放的中心点,设置为图片中心便可。这样经过一个简单的矩阵变换,就能够模拟电视机关闭的动画。
也能够设置更精准插值器,并将0到1的时间因子拆分红不一样的过程,从而对不一样的过程采用不一样的动画效果。代码以下:
@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;
}复制代码
自定义动画的核心——如何定义动画的进行过程,代码:
@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);
}复制代码
经过以上代码使用Camera类实现动画效果就是设置三个坐标轴的旋转角度,经过最后两行代码能够改变旋转时默认旋转中心。
未完,待续。。。