參考連結
https://www.jianshu.com/p/c4601bab860a
理論知識:
坐标計算
令
A0和B3連線的斜率 k = (B3Y - A0Y) / (B3X - A0X)
常數 b = A3Y - k * A3X
則
A2的X坐标 A2X = A3X - (A3X - A0X) * rate
A2的Y坐标 A2Y = k * A2X + b
B1的X坐标 B1X = A3X + (B3X - A3X) * rate
B1的Y坐标 B1Y = k * B1X + b
rate是一個(0, 0.5)區間内的值,數值越大,數值點之間的曲線弧度越小。
除此以外,如果數值點是第一個點或者最後一個點,可以把斜率k視為0,然後隻計算左控制點或者有控制點。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPn5EeVpWT0kERPpHOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5YDNyATNygTM0EDNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
package com.aiyuba.uiview;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Created by maoyujiao on 2020/4/14.
* 參考 https://www.jianshu.com/p/c4601bab860a
* 折線的平滑處理
* 繪制曲線,最常用的參數曲線函數就是貝塞爾曲線。
* 二次貝塞爾曲線 quadTo
* 三次貝塞爾曲線 cubicTo ,每個中間點的的切線上都會擴充2個點,然後以3個點畫曲線
*
* 令 A0表示第一個點 B3表示第三個點 A2表示第二個點
A0和B3連線的斜率 k = (B3Y - A0Y) / (B3X - A0X)
常數 b = A3Y - k * A3X
則
A2的X坐标 A2X = A3X - (A3X - A0X) * rate
A2的Y坐标 A2Y = k * A2X + b
B1的X坐标 B1X = A3X + (B3X - A3X) * rate
B1的Y坐标 B1Y = k * B1X + b
rate是一個(0, 0.5)區間内的值,數值越大,數值點之間的曲線弧度越小。
*/
public class CurveLine extends View {
private static final String TAG = "CurveLine";
private final ArrayList<PointF> cubicToPoints = new ArrayList<>();
List<PointF> points = new ArrayList<>();
Path path = new Path();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
float rate = 0.4f;
public CurveLine(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4);
initPoint();
initControlPoint();
}
/**
* //構造3次貝塞兒曲線
*/
private void initControlPoint() {
for (int i = 0; i < points.size() ; i++) {
if(i == 0){
cubicToPoints.add(points.get(i));
//後控點
PointF pointF_2 = new PointF();
pointF_2.x = points.get(i).x + (points.get(i + 1).x - points.get(i).x)*rate;
pointF_2.y = points.get(i).y;
cubicToPoints.add(pointF_2);
}else if(i == points.size() - 1){
//前控點
PointF pointF_1 = new PointF();
pointF_1.x = points.get(i).x - (points.get(i).x - points.get(i - 1).x)*rate;
pointF_1.y = points.get(i).y;
cubicToPoints.add(pointF_1);
cubicToPoints.add(points.get(i));
}else {
float k = (points.get(i + 1).y - points.get(i - 1).y) / (points.get(i + 1).x - points.get(i - 1).x);
float b = points.get(i).y - k * points.get(i).x;
Log.d(TAG, "CurveLine: k" + k + ",b" + b);
PointF point_1 = new PointF();
point_1.x = points.get(i).x - (points.get(i).x - points.get(i - 1).x) * rate;
point_1.y = k * point_1.x + b;
cubicToPoints.add(point_1);//前控制點
cubicToPoints.add(points.get(i));//目前點
PointF point_2 = new PointF();
point_2.x = points.get(i).x + (points.get(i + 1).x - points.get(i).x) * rate;
point_2.y = k * point_2.x + b;
cubicToPoints.add(point_2);//後控制點
}
}
}
private void initPoint() {
PointF point1 = new PointF();
point1.set(100, 400);
points.add(point1);
PointF point2 = new PointF();
point2.set(200, 300);
points.add(point2);
PointF point3 = new PointF();
point3.set(300, 400);
points.add(point3);
PointF point4 = new PointF();
point4.set(400, 300);
points.add(point4);
PointF point5 = new PointF();
point5.set(500, 400);
points.add(point5);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path.moveTo(points.get(0).x,points.get(0).y);
for (int i = 0; i < points.size(); i++) {
canvas.drawCircle(points.get(i).x,points.get(i).y,4,paint);
}
//每3個點畫一條貝塞爾曲線
//從1開始,将第二個點在第一條貝塞爾曲線上。
for (int i = 1; i < cubicToPoints.size() - 2; i+=3) {
path.cubicTo(cubicToPoints.get(i).x,cubicToPoints.get(i).y,
cubicToPoints.get(i + 1).x,cubicToPoints.get(i + 1).y,
cubicToPoints.get(i+ 2).x,cubicToPoints.get(i + 2).y);
}
canvas.drawPath(path,paint);
// 二次貝塞爾沒有經過這些點
// for (int i = 1; i < points.size(); i++) {
// path.quadTo(points.get(i-1).x,
// points.get(i-1).y,
// (points.get(i-1).x + points.get(i).x) / 2,
// (points.get(i-1 ).y + points.get(i ).y) / 2
// );
// }
}
}
效果圖