您的位置:首页 > 编程语言 > Lua

属性动画高级用法之TypeEvaluator和Interpolator

2016-07-14 18:22 639 查看
1、Interpolator 插值器,控制动画的变化速率,就是通常描述的加速度,可以简单理解为变化的快慢,它是一个接口,实现类如下



从上面关系图可以看到,差值器最终实现的接口是TimeInterpolator,它的源码如下

public interface TimeInterpolator {
float getInterpolation(float input);
}参数input的值介于0和1,显示当前在动画中所经过的动画时间的动画值,0个代表开始和1个代表结。束
 
1.1、下面是DecelerateInterpolator的源码,看下getInterpolation方法的实现,如果mFactor
等于1.0f就会执行

result = (float)(1.0f - (1.0f - input) * (1.0f - input));上面这行翻译过来就是抛物线函数 1-(1-x)*(1-x) ,它对应的二维坐标图如下。可以看到x的值在[0,1]之间直线与抛物线的切点交于横坐标的角度在变小,而这里的角度可以看做是加速度,因此DecelerateInterpolator做的是减速运动。其他的插值器原理类似,这里就不在描述。


 

public class DecelerateInterpolator implements Interpolator {
public DecelerateInterpolator() {
}

/**
* Constructor
*
* @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
* an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
* ease-out effect (i.e., it starts even faster and ends evens slower)
*/
public DecelerateInterpolator(float factor) {
mFactor = factor;
}

public DecelerateInterpolator(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.DecelerateInterpolator);
mFactor = a.getFloat(com.android.internal.R.styleable.DecelerateInterpolator_factor, 1.0f);
a.recycle();
}

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

private float mFactor = 1.0f;
}
1.2、自定义插值器
自定义插值器需要实现接口 Interpolator,代码很简单如下
,先减速后加速,是一个抛物口朝上抛物线。

<span style="font-size:14px;">public class ParabolaInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return (2 * input - 1) * (2 * input - 1);
}
}</span>


2、TypeEvaluator估值器

系统自带的估值器有下面几种



下面是小球按照正玄曲线从屏幕左边移动到右边,效果图如下



在这里使用的正玄曲线曲线公式是(float) Math.sin((30 * fraction) - Math.PI/2)+1,翻译成数学公式就是sin(10πx-π/2)+1,取值范围是[0,2]。如果将公式放到自定义的TypeEvaluator里面,配合动画就可以实时返回y和x的坐标值。

正玄曲线图如下:



现在开始进行自定义view的绘制,实现小球移动

2.1 移动

这个比较简单,通过paint和canvas对象结合可以绘制一个小球,通过坐标改变然后刷新界面重新绘制就可以移动。

2.2 设置动画,获取坐标

采用属性动画,因为ValueAnimator对象可以传入一个TypeEvaluator<T>对象,通过上面知道TypeEvaluator可以计算出坐标值。

2.3 移动

可以设置动画监听器addUpdateListener,可以获取到当前坐标,在调用界面刷新。

废话不多说,下面是源码

<span style="font-size:14px;">public class MoveCircle extends View {
//小球半径
private int radius=30;
//画笔
private Paint mPaint;

//保存小球移动过程中的坐标
private PointF currentPointF;
//高度和宽度
private int halfHeight;
private int width;

public MoveCircle(Context context) {
super(context);
init();
}

public MoveCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

private void init(){
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.BLUE);
halfHeight = ToolUtils.getDisplayMetrics(getContext()).heightPixels/2;
width = ToolUtils.getDisplayMetrics(getContext()).widthPixels;
currentPointF = new PointF(radius,halfHeight);
startAnimator();
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(currentPointF.x, currentPointF.y, radius, mPaint);
super.onDraw(canvas);
}

/**
* 启动动画
*/
private void startAnimator() {
//使用估值器ValueAnimator,传入一个自定义PositionEvaluator,设置起点坐标,设置终点坐标
ValueAnimator animator = ValueAnimator.ofObject(
new PositionEvaluator(),new PointF(radius, halfHeight),new PointF(width-radius, halfHeight));
//监听变化过程
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPointF = (PointF) animation.getAnimatedValue();
Log.i("tag"," onAnimationUpdate x: "+currentPointF.x+"   y: "+currentPointF.y);
invalidate();
}
});
// 设置加速插值器AccelerateInterpolator()
animator.setInterpolator(new AccelerateInterpolator());
animator.setStartDelay(200);//推迟启动200毫秒
animator.setDuration(10 * 1000).start();//启动
}

}</span>


<span style="font-size:14px;">public class PositionEvaluator implements TypeEvaluator<PointF>{

private PointF pointF;

public PositionEvaluator() {
pointF = new PointF();
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//以正玄曲线从左边运行到右边
//x轴:直线运动
float x = 0;
if (fraction == 0) {
x = startValue.x;
} else {
x = fraction * endValue.x;
}
// sin(30x-π/2)+1
//y轴:正玄曲线,公式  (float) Math.sin((30 * fraction) - Math.PI/2)+1,其范围是[0,2]
float range = (float) Math.sin((30 * fraction) - Math.PI/2)+1;

//小球纵坐标+波动范围
float y = endValue.y + range*30;
pointF.x = x;
pointF.y = y;
return pointF;
}

}</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息