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

ApiDemos – BouncingBalls分析

2014-09-29 15:34 405 查看
ApiDemos – BouncingBalls分析

 

这个例子主要是介绍属性动画的使用的,关于属性动画的一些关键设定的值,我们在上一篇文章大致描述了一番。另外,在分析这个例子之前,我们需要知道属性动画的类层次关系,这样便于理解和使用。

它的层次关系如下:

 


图1 Animator类继承图

 

BouncingBalls类是一个普通的Activity,它添加了一个自定义的MyAnimationView到主界面中。我们先看这个MyAnimationView的构造函数是怎样的:

public class MyAnimationView extends View {
public MyAnimationView(Contextcontext) {
super(context);

// Animate background color
// Note that setting the background color will automatically invalidatethe
// view,so that the animated color, and the bouncing balls, get redisplayed on
// every frame of the animation.
ValueAnimator colorAnim =ObjectAnimator.ofInt(this, "backgroundColor", RED, BLUE);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
}

 

MyAnimationView继承自View类,是个自定义View,它在构造函数里启动了一个属性动画,即colorAnim.start()。

 

这里面用到了一个ObjectAnimator,这个对象动画控制器可以直接作用于某个对象。还记得我们上节讲到的属性动画的几个关键步骤是哪些吗?设置属性动画,必然离不开这几个关键步骤,否则动画是不可能成功的。在本例中这个ObjectAnimator也同样做了这几步:

1)  选定对象(Object)+属性(Property):简称 O + P;

ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", RED, BLUE);
Object = this;

P = “backgroundColor”,这是View的背景色的属性值,也同时说明View中必然存在方法setP()以及getP(),即setBackgroundColor和getBackgroundColor;

 

2)  时长(Duration):简称D;

colorAnim.setDuration(3000);
D = 3000ms=3s
 
3)  时间插值(Time interpolation):简称T;

 

这里我们并没有指定T值,如果没有指定,那么T必须是线性插值,也就是说在某个时间点time,当前的属性值的相对变化位置是 T = time/D;

 

再看,下面的代码,

colorAnim.setEvaluator(new ArgbEvaluator());

那么这个ArgbEvaluator是干什么用的呢?这个ArgbEvaluator就是计算当前属性值的,也即当前View的背景色的哦。我们可以看看ArgbEvaluator()的实现啊:
/**
* This evaluator can be used to perform typeinterpolation between integer
* values that represent ARGB colors.
*/
public class ArgbEvaluatorimplements TypeEvaluator {

/**
* This function returns the calculated in-between value fora color
* given integers that represent the startand end values in the four
*bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values arerecombined into the return value.
*
* @param fraction The fraction fromthe starting to the ending values
* @param startValue A 32-bit int valuerepresenting colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int valuerepresenting colors in the
* separate bytes of the parameter
* @return A value that is calculatedto be the linearly interpolated
* result, derived by separating the startand end values into separate
* color channels and interpolating eachone separately, recombining the
* resulting values in the same way.
*/
public Object evaluate(float fraction, ObjectstartValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;

int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;

return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
}


从上面的代码可以看出,它的作用就是计算当前的颜色值。计算方法本质上讲就是:
当前背景色 = (BLUE – RED) * fraction = (BLUE– RED)* T = (BLUE – RED)*(time/D) = (BLUE – RED) * (time / 3000);
 
你也可以自定义自己的Evaluator,但是返回值必须是属性值的类型才行。
 
4)  反复次数(Repeat Count)和行为(Behavior):简称R&B;

colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);

R = ValueAnimator.INFINITE,无限变化哦
B = ValueAnimator.REVERSE,动画结束后再反向播放哦
 
5)  动画控制器(Animator):简称A;

A = ObjectAnimator;
 
6)  帧刷新率(Frame refresh delay):简称F;

没有设置哦,那么 F = 10ms,也就是说理论上讲10ms就会刷新一帧,即调用this.setBackgroundColor一次哦。
 
上面6个步骤就是一个属性动画的完整步骤。
当前了这个动画还可以通过XML文件来实现,这个我们最后再讲吧。
 
在这个BouncingBalls界面,手指滑动会产生N多个运动的小球哦。我们来看看MyAnimationView的onTouchEvent事件:
@Override
public boolean onTouchEvent(MotionEventevent) {
if (event.getAction() != MotionEvent.ACTION_DOWN &&
event.getAction() !=MotionEvent.ACTION_MOVE) {
return false;
}
ShapeHolder newBall =addBall(event.getX(), event.getY());

// Bouncing animation with squash and stretch
float startY = newBall.getY();
float endY = getHeight() - 50f;
float h = (float)getHeight();
float eventY = event.getY();
int duration = (int)(500 * ((h - eventY)/h));
ValueAnimator bounceAnim =ObjectAnimator.ofFloat(newBall, "y", startY, endY);
bounceAnim.setDuration(duration);
bounceAnim.setInterpolator(new AccelerateInterpolator());

 
从上面的代码得知,在MOVE事件的时候,创建一个ShapeHolder类型的对象,然后在这个对象上创建它的”y”属性的动画,当然了这个ShapeHolder是自定义的对象,具体是什么大家自己看吧,但是可以肯定的是这个对象一定有一个getY和setY方法。比较新鲜的是设定了时间插值对象:
bounceAnim.setInterpolator(new AccelerateInterpolator());

这个AccelerateInterpolator就是我们上面提到的T值。它的实现如下:
/**
* An interpolator where the rate of changestarts out slowly and
* and then accelerates.
*
*/
public class AccelerateInterpolator implements Interpolator {
private final float mFactor;
private final double mDoubleFactor;

public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}

/**
* Constructor
*
* @param factor Degree to which theanimation should be eased. Seting
* factor to 1.0f produces a y=x^2 parabola. Increasing factor above
* 1.0f exaggerates the ease-in effect (i.e.,it starts even
* slower and ends evens faster)
*/
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}

public AccelerateInterpolator(Contextcontext, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs,com.android.internal.R.styleable.AccelerateInterpolator);

mFactor =a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor,1.0f);
mDoubleFactor = 2 * mFactor;

a.recycle();
}

public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return
b7a9
input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
}


上面最主要的方法是getInterpolation()这个方法,这个值返回当前属性值的T值,其中参数input其实就是线性变化百分比,如果直接把input(0.0<= input <=1.0)返回的话,那么属性值的变化是线性的,但是本类顾名思义是一个加速插值类型,所以返回的值类型是input*input,这样看起来就会有加速的功能哦。
 
onTouchEvent()中有一段代码是这样的:
// Sequence the twoanimations to play one after the other
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);

// Start the animation
animatorSet.start();

 
AnimatorSet是干什么用的,原来这个直译就是动画控制器组合,说白了,可以让N个Animator同时动画,或者一前一后等。把所有的动画集中起来控制哦。
 
好了,上面就是这个例子的主要知识点了哦。
 
下面,我们看看如何在XML中设置属性动画的呢。属性动画的XML文件位置应当放在目录/res/animator/下面,我们以更改背景色为例,创建一个myanimationview_background_change.xml:
 
<?xml version="1.0"encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="backgroundColor"
android:duration="3000"
android:valueFrom="#FFFF8080"
android:valueTo="#FF8080FF"
android:repeatCount="-1"
android:repeatMode="reverse"
></objectAnimator>

</set>

 
修改MyAnimationView的构造函数:
public MyAnimationView(Context context) {
super(context);

// Animate background color
// Note that setting the background color will automatically invalidatethe
// view, so that the animated color, and the bouncing balls, getredisplayed on
// every frame of the animation.
/*ValueAnimator colorAnim = ObjectAnimator.ofInt(this,"backgroundColor", RED, BLUE);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(newArgbEvaluator());
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();*/
AnimatorSet colorAnimSet =(AnimatorSet)AnimatorInflater.loadAnimator(context, R.animator.myanimationview_background_change);
ObjectAnimator colorAnim =(ObjectAnimator)colorAnimSet.getChildAnimations().get(0);
colorAnim.setTarget(this);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnimSet.start();
}

 
请下载APIDemos源码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息