您的位置:首页 > 移动开发 > Android开发

Android动画三:属性动画 (property animation)

2016-04-04 00:50 459 查看
参考了郭神的文章,感谢郭神。

因为郭神代码质量高,有部分代码直接借用了郭神相关文章中的,并无盗窃之意,只是学习记录。

郭神博客链接 : http://blog.csdn.net/guolin_blog/

1. API 11(3.0系统)才加入的新特性

注意:属性动画是3.0(API 11)中新加入的特性,在3.0以下系统无法使用。

可以采用开源动画库nineoldandroids来兼容以前的版本,但在3.0以下系统运行时其内部是通过代理View动画来实现的,因此在低版本上本质还是View动画。

2. ValueAnimator

ValueAnimator是整个属性动画机制当中最核心的一个类,初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它本身不作用于任何对象,就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,也就相当于对我们的对象做了动画。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且设定好动画所需的运行时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,

使用示例:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f, 3f, 5f);
anim.setDuration(1000);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("Test", "cuurent value is " + currentValue);
}
});
anim.start();


如果希望将一个整数值从0平滑地过渡到100,那么也很简单,只需要调用ValueAnimator的ofInt()方法就可以了,如下所示:

ValueAnimator anim = ValueAnimator.ofInt(0, 100);


调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放。

ValueAnimator运用类型估值器TypeEvaluator

TypeEvaluator告诉动画系统如何从初始值平滑地过渡到结束值。

前面我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别对浮点型和整型的数据进行动画操作,其实他们都有内置的FloatEvaluator和IntEvaluator来实现这一过渡。我们来看一下FloatEvaluator的代码实现:

public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}


实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过渡。

示例:先定义一个Point 类,

public class Point {

private float x;

private float y;

public Point(float x, float y) {
this.x = x;
this.y = y;
}

public float getX() {
return x;
}

public float getY() {
return y;
}

}


Point类非常简单,只有x和y两个变量用于记录坐标的位置,并提供了构造方法来设置坐标,以及get方法来获取坐标。接下来定义PointEvaluator,如下所示:

public class PointEvaluator implements TypeEvaluator{

@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
Point point = new Point(x, y);
return point;
}

}


接下来我们就可以非常轻松地对Point对象进行动画操作了,比如说我们有两个Point对象,现在需要将Point1通过动画平滑过度到Point2,就可以这样写:

Point point1 = new Point(0, 0);
Point point2 = new Point(300, 300);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获取到当前进度占整个动画的比例
float fraction = animation.getAnimatedFraction();
//获取到当前动画的进度值
currentPoint = (Point) animation.getAnimatedValue();
//利用获取到的currentPoint 生成动画
}
});
anim.setDuration(5000);
anim.start();


3. ObjectAnimator

相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,但它其实是继承自ValueAnimator的。

示例:

将textview移除屏幕,再重新移入屏幕。

float curTranslationX = textview.getTranslationX();
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);
animator.setDuration(5000);
animator.start();


在垂直方向上进行缩放

ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 2f, 1f);
animator.setDuration(5000);
animator.start();


ObjectAnimator可以对任意对象的任意属性做动画,其原理是:要求动画作用对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次调用set方法,随着时间的推移,所传递的值越来越接近最终值。如果我们对object的属性a做动画,动画生效需同时满足以下两个条件:

(1)object必须提供setA方法,如果动画没有传递初始值,还必须提供getA方法,因为系统要去获取属性a的初始值(如果获取不到初始值,程序直接crash)

(2)object的setA方法对属性a所做的改变必须通过某种方式反映出来,例如UI的改变(如果这条不满足,就看不到动画效果)。

View中就因为存在setAlpha()、getAlpha()、setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,ObjectAnimator 才能对View实现4种View动画效果。

针对上述条件2,如果未能满足,有以下3种解决方法:

(1)给对象加上get和set方法,如果有权限的话;

这种方式最简单,但往往都是对原生控件做动画,我们没有权限,都是Android SDK内部实现的方法,无法更改。除非是自定义View。

(2)用一个类来包装原始对象,间接为其提供get和set方法;

示例:

private static class ViewWrapper{
private View mTarget;

public ViewWrapper(View target){
mTarget = target;
}

public int getWidth(){
return mTarget.getLayoutParams().width;
}

public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}


通过以上代码就给属性width包装了一层,并给它提供了get、set方法,使用时只需操纵包装类就可以间接调用到get、set方法了,代码示例如下:

ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500),setDuration(5000).start();


以上通过包装mButton,我们在5s内让mButton的宽度增加到了500px。

(3)采用ValueAnimator,监听动画过程,自己实现属性的改变。

ObjectAnimator运用类型估值器TypeEvaluator

示例:(ColorEvaluator的继承写法略)

ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(),
"#0000FF", "#FF0000");
anim.setDuration(5000);
anim.start();


4. 时间插值器TimeInterpolator

接口TimeInterpolator的源码定义如下:

/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {

/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
*        in the animation where 0 represents the start and 1.0 represents
*        the end
* @return The interpolation value. This value can be more than 1.0 for
*         interpolators which overshoot their targets, or less than 0 for
*         interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}


TimeInterpolator的getInterpolation方法中,参数input由系统计算而得,返回的float数值就是估值器中用到的fraction。可以通过自定义插值器改变返回的fraction,改变动画的加减速效果。然后通过以下代码使自定义的插值器产生作用。

anim.setInterpolator(new myTimeInterpolator());


5. 组合动画

实现组合动画功能主要需要借助AnimatorSet这个类,通过playTogether(), playSequentially(), set.play().with().before().after()这些方法来控制动画的播放顺序。

6. 利用XML文件使用属性动画(路径 //res/animator/anim_demo.xml)

6.1 在xml文件中定义

三种标签:

标签1“ animator”对应代码中的ValueAnimator

标签2“ objectAnimator” 对应代码中的ObjectAnimator

标签3“ set” 对应代码中的AnimatorSet

动画集合中可以再嵌套集合,set标签控制动画顺序的属性为:

anroid:ordering=["together" | "sequentially"]


6.2 在代码中使用定义好的xml文件

示例:

Animator anim = AnimatorInflater.loadAnimator(this, R.animator.anim_demo);
anim.setTarget(mButton);
anim.start();


7. 属性动画的监听回调

Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了

示例:

anim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//动画开始时
}

@Override
public void onAnimationRepeat(Animator animation) {
//动画重复播放时
}

@Override
public void onAnimationEnd(Animator animation) {
//动画结束时
}

@Override
public void onAnimationCancel(Animator animation) {
//动画取消时
}
});


为方便开发,系统还提供了一个适配器类,叫作AnimatorListenerAdapter,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画开始这个事件,就只需要单独重写这一个方法就可以了,如下所示:

anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
}
});


还有一个监听器AnimatorUpdateListener, 定义如下:

public static interface AnimatorUpdateListener{
void onAnimationUpdate(ValueAnimator animation);
}


这个监听器会监听整个动画过程,我们知道动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被调用一次,利用这个可以做一些特殊的处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: