先看下效果:
实现思路:
1:第一步绘制,利用Path 类 画出一条二阶贝塞尔曲线。
2:第二步记录曲线运动坐标点。模拟Path类 封装一个类MyPath,负责记录贝塞尔曲线各个控制点。
3:将 绘制的曲线的坐标点 和 运动的坐标点 设为一致,自定义一个二阶 贝塞尔曲线估值器,
并利用属性动画结合MyPath 工具类记录的坐标点,来控制View的坐标x,y.
使view沿着曲线运动即可。
第一步代码:绘制二阶贝塞尔曲线
package example.nzh.com.beisaier;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
public class PathView extends View {
Path path;
Paint paint;
public PathView(Context context) {
this(context, null);
}
public PathView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PathView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//定位到起始点
path.moveTo(200, 200);
//二阶贝塞尔曲线
//参数:支点,终点的坐标(相对于起始点 绝对坐标绘制)
path.quadTo(500, 700, 600, 200);
//默认接着上一个曲线的结束点
// path.quadTo(...);
//相对于最后一个点 相对坐标绘制(300,100)
// path.rQuadTo(300+200, 100+100, 300+400, 0);
canvas.drawPath(path, paint);
}
public Path getPath() {
return path;
}
}
第二步:模拟Path类 封装一个自己的Path来 记录曲线运动坐标点。
package example.nzh.com.beisaier;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
public class MyPath {
/**
* 记录贝塞尔曲线控制点
* key:点的 类型(控制点,起始点,终点)
* value:点
*/
ArrayMap<Integer, MyPoint> map;
public MyPath() {
this.map = new ArrayMap<>();
}
public void moveTo(float startX, float startY) {
map.put(MyPoint.TYPE_START, new MyPoint(startX, startY, MyPoint.TYPE_START));
}
public void quadTo(float controlX, float controlY, float endX, float endY) {
map.put(MyPoint.TYPE_QUAD_POINT, new MyPoint(controlX, controlY, MyPoint.TYPE_QUAD_POINT));
map.put(MyPoint.TYPE_END, new MyPoint(endX, endY, MyPoint.TYPE_END));
}
/**
* 返回 二阶贝塞尔曲线控制点
*
* @return
*/
public MyPoint getQuadControlPoint() {
return map.get(MyPoint.TYPE_QUAD_POINT);
}
/**
* 返回起始点 ,终点 坐标 集合
*
* @return
*/
public MyPoint[] getStartEndPoint() {
MyPoint start = map.get(MyPoint.TYPE_START);
MyPoint end = map.get(MyPoint.TYPE_END);
MyPoint[] array = {start, end};
return array;
}
}
package example.nzh.com.beisaier;
public class MyPoint {
public float x;
public float y;
public static final int TYPE_START = 1; //起始点
public static final int TYPE_END = 2; //终止点
public static final int TYPE_QUAD_POINT = 3; //二阶贝塞尔曲线控制点
public static final int TYPE_CUBIC_PIONT_1 = 4; //三阶贝塞尔曲线 控制点1
public static final int TYPE_CUBIC_PIONT_2 = 5;//三阶贝塞尔曲线 控制点2
int type;
public MyPoint(float x, float y, int type) {
this.x = x;
this.y = y;
this.type = type;
}
public MyPoint(float x, float y) {
this.x = x;
this.y = y;
}
}
第三步:实现贝塞尔运动估值器。并结合属性动画来控制view。
package example.nzh.com.beisaier;
import android.animation.TypeEvaluator;
public class BesselEvaluator implements TypeEvaluator<MyPoint> {
MyPoint controlP1;
/**
* 二阶 贝塞尔曲线 控制点 构造
*
* @param controlP
*/
public BesselEvaluator(MyPoint controlP) {
this.controlP1 = controlP;
}
/**
* @param t 动画执行的百分比
* @param p0 起始坐标
* @param p2 终止坐标
* @return
*/
@Override
public MyPoint evaluate(float t, MyPoint p0, MyPoint p2) {
//二阶贝塞尔曲线运动
//公式:baidu
float x = (1 - t) * (1 - t) * p0.x + 2 * t * (1 - t) * controlP1.x + t * t * p2.x;
float y = (1 - t) * (1 - t) * p0.y + 2 * t * (1 - t) * controlP1.y + t * t * p2.y;
MyPoint myPoint = new MyPoint(x, y);
return myPoint;
}
}
package example.nzh.com.beisaier;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Path;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import example.nzh.com.beisaier.canvas.CanvasActivity;
import example.nzh.com.beisaier.canvas.CanvasActivity2;
import example.nzh.com.beisaier.view.ViewGroupTestActivity;
public class MainActivity extends Activity implements View.OnClickListener {
TextView textView;
//利用属性动画 原理,定义一个属性 ,(贝塞尔曲线运动)
private MyPoint besselMove;
PathView pathView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pathView = (PathView) findViewById(R.id.pathview);
textView = (TextView) findViewById(R.id.textview);
textView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
MyPath myPath = new MyPath();
// 保证这里的坐标和绘制的曲线坐标一致,这样就可以沿着曲线运动。
myPath.moveTo(200, 200);
myPath.quadTo(500, 700, 600, 200);
ObjectAnimator animator = ObjectAnimator.ofObject(
this,
"besselMove",
new BesselEvaluator(myPath.getQuadControlPoint()),
myPath.getStartEndPoint());
animator.setDuration(3 * 1000);
animator.start();
}
/**
* 提供属性的set方法
*
* @param besselMove
*/
public void setBesselMove(MyPoint besselMove) {
this.besselMove = besselMove;
textView.setTranslationX(besselMove.x);
textView.setTranslationY(besselMove.y);
}
}