参考:
一阶贝塞尔曲线(线段):
包含两个点:起点、终点,
其实就是lineTo的效果。
二阶贝塞尔曲线(抛物线):
包含三个点:起点A、一个控制点B、终点C
长度关系为:
AD / DB = BE / EC = DF / FE
二阶曲线对应的方法是quadTo
Path mPath = new Path();
mPath.moveTo(x0,y0);//起点(x0,y0)
mPath.quadTo(x1,y1,x2,y2);
//控制点B坐标(x1,y1),决定了弧度方向与大小,
//终点C坐标(x2,y2)
canvas.drawPath(mPath, mPaint);
示例:
public class MyView extends View {
private float mSupX;
private float mSupY;
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10);
Path path = new Path();
path.moveTo(200, 200);//起点
path.quadTo(mSupX, mSupY, 800, 1300);//控制点,终点
canvas.drawPath(path, paint);//画出贝塞尔曲线
paint.setColor(Color.parseColor("#ff0000"));
paint.setStrokeWidth(20);
canvas.drawPoint(mSupX, mSupY, paint);//画出控制点
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
mSupX = event.getX();
mSupY = event.getY();
invalidate();
}
return true;
}
}
效果图:
参考:Android自定义view进阶-- 神奇的贝塞尔曲线
三阶贝塞尔曲线:
包含四个点:起点A、2个控制点BC、终点D
长度关系为:
AE:AB = BF:BC = CG:CD =
EH:EF = FI:FG =
HJ:HI
三阶曲线对应的方法是cubicTo
path.cubicTo(0, 150, 300, 450, 0, 600);
//(0, 150):控制点B,
//(300, 450):控制点C,
//(0, 600):终点D
三阶曲线计算过程与二阶类似,具体可以见下图动态效果:
示例:
public class MyView extends View {
private Point controlPointOne = new Point(200, 200);
private Point controlPointTwo = new Point(500, 200);
private boolean isControlPointTwo;
private Paint paintBezier;
private Paint paintLine;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paintBezier = new Paint();
paintBezier.setStyle(Paint.Style.STROKE);
paintBezier.setColor(Color.BLACK);
paintBezier.setStrokeWidth(10);
paintLine = new Paint();
paintLine.setStyle(Paint.Style.STROKE);
paintLine.setColor(Color.RED);
paintLine.setStrokeWidth(3);
}
@Override
protected void onDraw(Canvas canvas) {
Path path = new Path();//注意path不可在构造方法中new
path.moveTo(100, 500);
if (isControlPointTwo) {
paintBezier.setColor(Color.RED);
} else {
paintBezier.setColor(Color.BLUE);
}
path.cubicTo(controlPointOne.x, controlPointOne.y, controlPointTwo.x, controlPointTwo.y, 900, 500);
//绘制路径
canvas.drawPath(path, paintBezier);
//绘制辅助点
paintBezier.setColor(Color.RED);
canvas.drawPoint(controlPointOne.x, controlPointOne.y, paintBezier);
paintBezier.setColor(Color.BLUE);
canvas.drawPoint(controlPointTwo.x, controlPointTwo.y, paintBezier);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (isControlPointTwo) {
controlPointOne.x = (int) event.getX();
controlPointOne.y = (int) event.getY();
} else {
controlPointTwo.x = (int) event.getX();
controlPointTwo.y = (int) event.getY();
}
invalidate();
break;
}
return true;
}
public boolean isControlPointTwo() {
return isControlPointTwo;
}
public void setControlPointTwo(boolean controlPointTwo) {
isControlPointTwo = controlPointTwo;
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="切换控制点" />
<com.android.imooc.MyView
android:id="@+id/my_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
MyActivity
public class MyActivity extends FragmentActivity {
private Button btn;
private MyView myView;
private boolean isControlPointTwo = false;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test3);
btn = ((Button) this.findViewById(R.id.btn));
myView = ((MyView) this.findViewById(R.id.my_view));
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isControlPointTwo) {
myView.setControlPointTwo(false);
} else {
myView.setControlPointTwo(true);
}
isControlPointTwo = !isControlPointTwo;
}
});
}
}
效果图:
demo:
public class MyView extends View {
private final Path mPath;
private final Paint mPaint2;
private final Paint mPaint1;
private final PointF mStartPoint = new PointF();
private final PointF mControlPoint1 = new PointF();
private final PointF mControlPoint2 = new PointF();
private final PointF mEndPoint = new PointF();
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPath = new Path();
mPaint2 = new Paint();
mPaint2.setColor(Color.BLUE);
mPaint2.setStyle(Paint.Style.FILL);
mPaint2.setStrokeWidth(10);
mPaint1 = new Paint();
mPaint1.setColor(Color.RED);
mPaint1.setStyle(Paint.Style.STROKE);
mPaint1.setStrokeWidth(2);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int rate = Math.min(w, h);
mStartPoint.set(0, rate);
mControlPoint1.set(0.2f * rate, 0.8f * rate);
mControlPoint2.set(0.1f * rate, 0);
mEndPoint.set(rate, 0);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制背景
canvas.drawColor(Color.GREEN);
//绘制三阶贝塞尔曲线
mPath.reset();
mPath.moveTo(mStartPoint.x, mStartPoint.y);
mPath.cubicTo(
mControlPoint1.x, mControlPoint1.y,
mControlPoint2.x, mControlPoint2.y,
mEndPoint.x, mEndPoint.y);
canvas.drawPath(mPath, mPaint1);
//绘制关键点
canvas.drawPoint(mStartPoint.x, mStartPoint.y, mPaint2);
canvas.drawPoint(mControlPoint1.x, mControlPoint1.y, mPaint2);
canvas.drawPoint(mControlPoint2.x, mControlPoint2.y, mPaint2);
canvas.drawPoint(mEndPoint.x, mEndPoint.y, mPaint2);
}
}
效果: