以下面一个实例来说说这个Animation自定义动画怎么使用。
图中,我们可以看出,小球是带有轨迹的向下滚动,而且,轨道与轨道之间的拼接是用二阶贝塞尔曲线实现的,小球模拟重力效果,加速度的向下滚。那么,这里有疑问的地方是,如果模拟这个场景去实现呢?
首先我们想到了用android的动画Animation。
我们对Animation进行了深究,发现有一个ObjectAnimator的类,其中创建的时候,就有个相关的参数TypeEvaluator和Object,只要实现这两个类,就能做成我们想要的轨迹效果。
不啰嗦,贴上代码
public class PathEvaluator implements TypeEvaluator {
/**
* @param t :执行的百分比
* @param startValue : 起点
* @param endValue : 终点
* @return
*/
@Override
public PathPoint evaluate(float t, PathPoint startValue, PathPoint endValue) {
float x, y;
float oneMiunsT = 1 - t;
//三阶贝塞尔曲线
if (endValue.mOperation == PathPoint.THIRD_CURVE) {
x = startValue.mX*oneMiunsT*oneMiunsT*oneMiunsT+3*endValue.mContorl0X*t*oneMiunsT*oneMiunsT+3*endValue.mContorl1X*t*t*oneMiunsT+endValue.mX*t*t*t;
y = startValue.mY*oneMiunsT*oneMiunsT*oneMiunsT+3*endValue.mContorl0Y*t*oneMiunsT*oneMiunsT+3*endValue.mContorl1Y*t*t*oneMiunsT+endValue.mY*t*t*t;
//二阶贝塞尔曲线
}else if(endValue.mOperation == PathPoint.SECOND_CURVE){
x = oneMiunsT*oneMiunsT*startValue.mX+2*t*oneMiunsT*endValue.mContorl0X+t*t*endValue.mX;
y = oneMiunsT*oneMiunsT*startValue.mY+2*t*oneMiunsT*endValue.mContorl0Y+t*t*endValue.mY;
//直线
}else if (endValue.mOperation == PathPoint.LINE) {
//x起始点+t*起始点和终点的距离
x = startValue.mX + t * (endValue.mX - startValue.mX);
y = startValue.mY + t * (endValue.mY - startValue.mY);
} else {
x = endValue.mX;
y = endValue.mY;
}
return PathPoint.moveTo(x,y);
}
}
public class AnimatorPath {
//一系列的轨迹记录动作
private List mPoints = new ArrayList<>();
/**
* 移动位置到:
* @param x
* @param y
*/
public void moveTo(float x,float y){
mPoints.add(PathPoint.moveTo(x,y));
}
/**
* 直线移动
* @param x
* @param y
*/
public void lineTo(float x,float y){
mPoints.add(PathPoint.lineTo(x,y));
}
/**
* 二阶贝塞尔曲线移动
* @param c0X
* @param c0Y
* @param x
* @param y
*/
public void secondBesselCurveTo(float c0X, float c0Y,float x,float y){
mPoints.add(PathPoint.secondBesselCurveTo(c0X,c0Y,x,y));
}
/**
* 三阶贝塞尔曲线移动
* @param c0X
* @param c0Y
* @param c1X
* @param c1Y
* @param x
* @param y
*/
public void thirdBesselCurveTo(float c0X, float c0Y, float c1X, float c1Y, float x, float y){
mPoints.add(PathPoint.thirdBesselCurveTo(c0X,c0Y,c1X,c1Y,x,y));
}
/**
*
* @return 返回移动动作集合
*/
public Collection getPoints(){
return mPoints;
}
}
public AnimatorPath getRandomPath() {
ArrayList array = new ArrayList<>();
animatorPath = new AnimatorPath();
Point point = new Point();
point.set(startPoint.x, startPoint.y);
animatorPath.moveTo(point.x, point.y);
for (int i = 1; i <= maxTurn; i++) {
if (i % 2 == 1) {
boolean isLeft = isLeft();
array.add(isLeft);
point = isLeft ? makeLeftPath(point) : makeRightPath(point);
} else if (i % 2 == 0) {
boolean isLeft = isLeft();
array.add(isLeft);
if (i != maxTurn) {
point = isLeft ? makeLeftPath(point) : makeRightPath(point);
if (array.size() > 1 && array.get(array.size() - 2) == isLeft) {
point = isLeft ? makeRightPath(point) : makeLeftPath(point);
i += 1;
}
} else {
point = isLeft ? makeLeftEndPath(point) : makeRightEndPath(point);
}
}
}
return animatorPath;
}
private void startAnimatorPath(View view, String propertyName, AnimatorPath path) {
if (anim != null && anim.isStarted()) {
isStart = false;
anim.cancel();
}
isStart = true;
anim = ObjectAnimator.ofObject(this, propertyName, new PathEvaluator(), path.getPoints().toArray());
anim.setInterpolator(new AccelerateInterpolator(0.85f));
anim.setDuration(8000);
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (vibrator != null && isStart) {
vibrator.vibrate(pattern, -1); //重复两次上面的pattern 如果只想震动一次,index设为-1
}
if (mInterstitialAd.isLoaded()) {
mInterstitialAd.show();
} else {
Log.e("xx", "none");
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}