控制点Control(X1 + a(X2 - X1), Y1 + a(Y2 - Y1))
(X1,Y1)、(X2,Y2)分别代表贝塞尔曲线的起始点和终点,a在[0,1]之间随意取值
package cn.study.yusiludemo.custom.besier.control;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
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.Paint.Style;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
/**
* 通过坐标点集合
* 如何确定贝塞尔曲线的控制点,并把折线上的点绘制在曲线上
*/
public class BezierView2 extends View{
/**画笔**/
private Paint paint;
/**路径path**/
private Path path;
/**贝塞尔曲线控制点1**/
Point point1 = new Point();
/**贝塞尔曲线控制点2**/
Point point2 = new Point();
/**贝塞尔曲线终点**/
Point point3 = new Point();
/**设置第一个控制点33%的距离,可随意取值在[0,1]**/
float mFirstMultiplier = 0.3f;
/**设置第二个控制点为66%的距离**/
float mSecondMultiplier = 1 - mFirstMultiplier;
/**原始坐标点**/
private List<Point> pointList = new ArrayList<>();
/**原始坐标点调整后对应到贝赛尔曲线上的坐标点**/
private List<Point> bezierPointList = new ArrayList<>();
public BezierView2(Context context) {
super(context);
}
public BezierView2(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BezierView2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void init(){
path = new Path();
paint = new Paint();
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
// 添加第一个点,
pointList.add(new Point(218,1394));
// 添加第二个点
pointList.add(new Point(406,1494));
// 添加第三个点
pointList.add(new Point(644,1218));
// 添加第四个点
pointList.add(new Point(832,1606));
// 添加第五个点
pointList.add(new Point(1020,1218));
//初始化时和原始点一样
Collections.copy(pointList,bezierPointList);
try {
bezierPointList = deepCopy(pointList);
}catch (IOException | ClassNotFoundException ioException){
}
}
private float mProgress;
public void setPercenter(float percenter){
if(percenter < 0.0f || percenter > 1.0f){
throw new IllegalArgumentException("setPercenter must between 0.0f and 1.0f");
}
mProgress = percenter;
invalidate();
}
public void startAnimal(){
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this,"percenter",0.0f,1.0f);
objectAnimator.setDuration(3000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.reset();
// 通过画折线和贝塞尔曲线可以知道,点得位置是不一样的。
// 画折线
for(int k = 0 ;k<pointList.size();k++){
paint.setColor(Color.GREEN);
if(k < pointList.size() - 1){
canvas.drawLine(pointList.get(k).getX(), pointList.get(k).getY(), pointList.get(k + 1 ).getX(), pointList.get(k + 1).getY(), paint);
}
paint.setColor(Color.BLUE);
canvas.drawCircle(pointList.get(k).getX(), pointList.get(k).getY(), 5, paint);
}
// 设置第一个点开始
path.moveTo(pointList.get(0).getX(), pointList.get(0).getY());
for (int b = 0; b < pointList.size(); b++) {
int nextIndex = b + 1 < pointList.size() ? b + 1 : b;
int nextNextIndex = b + 2 < pointList.size() ? b + 2 : nextIndex;
Log.e("控制点坐标===>>>",nextIndex+"==="+nextNextIndex);
// 设置第一个控制点
control(pointList, point1, b, nextIndex, mSecondMultiplier);
paint.setColor(Color.parseColor("#FF681C"));
canvas.drawCircle(point1.getX(), point1.getY(), 5, paint);
// Log.e("控制点坐标==1=>>>",p1.getX() + "===" + p1.getY());
// 设置第二个控制点
point2.setX(pointList.get(nextIndex).getX());
point2.setY(pointList.get(nextIndex).getY());
canvas.drawCircle(point2.getX(), point2.getY(), 5, paint);
// Log.e("控制点坐标==2=>>>",p2.getX() + "===" + p2.getY());
// 设置第二个控制点
control(pointList, point3, nextIndex, nextNextIndex, mFirstMultiplier);
canvas.drawCircle(point3.getX(), point3.getY(), 5, paint);
// Log.e("控制点坐标==3=>>>",p3.getX() + "===" + p3.getY());
// 最后一个点就是赛贝尔曲线上的点
path.cubicTo(point1.getX(), point1.getY(), point2.getX(), point2.getY(), point3.getX(), point3.getY());
// 画点
}
paint.setColor(Color.RED);
PathMeasure mPathMeasure = new PathMeasure(path, false);
reSetPointWithPath(mPathMeasure, bezierPointList);
for (int k = 0; k < bezierPointList.size(); k ++) {
canvas.drawCircle(bezierPointList.get(k).getX(), bezierPointList.get(k).getY(), 5, paint);
}
canvas.drawPath(path, paint);
}
/**
* 计算控制点
* @param points
* @param result
* @param index1
* @param index2
* @param multiplier
*/
private void control(List<Point> points, Point result, int index1, int index2, final float multiplier) {
float p1x = points.get(index1).getX();
float p1y = points.get(index1).getY();
float p2x = points.get(index2).getX();
float p2y = points.get(index2).getY();
float diffX = p2x - p1x;
float diffY = p2y - p1y;
result.setX(p1x + (diffX * multiplier));
result.setY(p1y + (diffY * multiplier));
}
/**
* 重新设置数据点的位置,为曲线上的位置
* @param mPathMeasure
* @param pointsList
*/
public void reSetPointWithPath(PathMeasure mPathMeasure, List<Point> pointsList){
//Path中的点的个数
int length = (int) mPathMeasure.getLength();
int pointsLength = pointsList.size();
float[] coords = new float[2];
for (int b = 0; b < length; b++) {
//返回b点的坐标,并把x,y坐标值赋值到coords;
mPathMeasure.getPosTan(b, coords, null);
double prevDiff = Double.MAX_VALUE;
boolean ok = true;
for (int j = 0; j < pointsLength && ok; j ++) {
double diff = Math.abs(pointsList.get(j).getX() - coords[0]);
if (diff < 1) {
pointsList.get(j).setY(coords[1]);
prevDiff = diff;
}
ok = prevDiff > diff;
}
}
}
/**
* 深度复制list对象,先序列化对象,再反序列化对象
*
* @param src 需要复制的对象列表
* @return 返回新的对象列表
* @throws IOException 读取Object流信息失败
* @throws ClassNotFoundException 泛型类不存在
*/
public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException{
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
return (List<T>)in.readObject();
}
}
package cn.study.yusiludemo.custom.besier.control;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* 通过坐标集合
* 如何确定贝塞尔曲线的控制点,并把折线上的点绘制在曲线上
*/
public class BezierView extends View{
public BezierView(Context context) {
super(context);
}
public BezierView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
List<Float> points = new ArrayList<Float>();
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setAntiAlias(true);
paint.setStyle(Style.STROKE);
// 添加第一个点,
points.add((float) 218.0);// X轴
points.add((float) 1394.0);// Y轴
// 添加第二个点
points.add((float) 406.0);
points.add((float) 1494.0);
// 添加第三个点
points.add((float) 644.0);
points.add((float) 1218.0);
// 添加第四个点
points.add((float) 832.0);
points.add((float) 1606.0);
points.add((float) 1020.0);
points.add((float) 1218.0);
// 通过画折线和贝塞尔曲线可以知道,点得位置是不一样的。
// 画折线
for (int i = 0; i < points.size() - 2; i = i + 2) {
paint.setColor(Color.GREEN);
canvas.drawLine(points.get(i), points.get(i + 1), points.get(i + 2), points.get(i + 3), paint);
paint.setColor(Color.BLUE);
canvas.drawCircle(points.get(i), points.get(i + 1), 5, paint);
}
canvas.drawCircle(points.get(points.size() - 2), points.get(points.size() - 1), 5, paint);
// 贝塞尔曲线
Path p = new Path();
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
float xp = points.get(0);
float yp = points.get(1);
// // 设置第一个点开始
p.moveTo(xp, yp);
int length = points.size();
// 设置第一个控制点33%的距离
float mFirstMultiplier = 0.3f;
// 设置第二个控制点为66%的距离
float mSecondMultiplier = 1 - mFirstMultiplier;
for (int b = 0; b < length; b += 2) {
int nextIndex = b + 2 < length ? b + 2 : b;
int nextNextIndex = b + 4 < length ? b + 4 : nextIndex;
Log.e("控制点坐标===>>>",nextIndex+"==="+nextNextIndex);
// 设置第一个控制点
control(points, p1, b, nextIndex, mSecondMultiplier);
paint.setColor(Color.parseColor("#FF681C"));
canvas.drawCircle(p1.getX(), p1.getY(), 5, paint);
// Log.e("控制点坐标==1=>>>",p1.getX() + "===" + p1.getY());
// 设置第二个控制点
p2.setX(points.get(nextIndex));
p2.setY(points.get(nextIndex + 1));
canvas.drawCircle(p2.getX(), p2.getY(), 5, paint);
// Log.e("控制点坐标==2=>>>",p2.getX() + "===" + p2.getY());
// 设置第二个控制点
control(points, p3, nextIndex, nextNextIndex, mFirstMultiplier);
canvas.drawCircle(p3.getX(), p3.getY(), 5, paint);
// Log.e("控制点坐标==3=>>>",p3.getX() + "===" + p3.getY());
// 最后一个点就是赛贝尔曲线上的点
p.cubicTo(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY());
// 画点
}
paint.setColor(Color.RED);
PathMeasure mPathMeasure;
mPathMeasure = new PathMeasure(p, false);
reSetPointWithPath(mPathMeasure, points);
for (int k = 0; k < points.size()-1; k +=2) {
canvas.drawCircle(points.get(k), points.get(k+1), 5, paint);
}
canvas.drawPath(p, paint);
}
/**
* 计算控制点
* @param points
* @param result
* @param index1
* @param index2
* @param multiplier
*/
private void control(List<Float> points, Point result, int index1, int index2, final float multiplier) {
float p1x = points.get(index1);
float p1y = points.get(index1 + 1);
float p2x = points.get(index2);
float p2y = points.get(index2 + 1);
float diffX = p2x - p1x;
float diffY = p2y - p1y;
result.setX(p1x + (diffX * multiplier));
result.setY(p1y + (diffY * multiplier));
}
/**
* 重新设置数据点的位置,为曲线上的位置
* @param mPathMeasure
* @param pointsList
*/
public void reSetPointWithPath(PathMeasure mPathMeasure, List<Float> pointsList){
int length = (int) mPathMeasure.getLength();
int pointsLength = pointsList.size();
float[] coords = new float[2];
for (int b = 0; b < length; b++) {
//返回b点的坐标,并把x,y坐标值赋值到coords;
mPathMeasure.getPosTan(b, coords, null);
double prevDiff = Double.MAX_VALUE;
boolean ok = true;
for (int j = 0; j < pointsLength && ok; j += 2) {
double diff = Math.abs(pointsList.get(j) - coords[0]);
if (diff < 1) {
pointsList.set(j + 1, coords[1]);
prevDiff = diff;
}
ok = prevDiff > diff;
}
}
}
}