需求
需要一個(gè)箭頭,連接1個(gè)View,指向(引導(dǎo))另一個(gè)View
實(shí)現(xiàn)方案
拿到這個(gè)需求我就在想,應(yīng)該如何實(shí)現(xiàn)會(huì)比較好。
考慮到Android平臺(tái)分辨率碎片化嚴(yán)重,單純的XML代碼估計(jì)很難實(shí)現(xiàn)。
于是想到用Canvas來畫。
實(shí)現(xiàn)思路比較簡單:
- 計(jì)算兩個(gè)View的起點(diǎn)和終點(diǎn)
- 通過貝塞爾曲線描繪一條彎曲的曲線
- 繪制一個(gè)倒三角形
計(jì)算兩個(gè)View的起點(diǎn)和終點(diǎn)
int[] location = new int[2];
startView.getLocationInWindow(location);
x1 = location[0];
y1 = location[1] - PixTool.getStatusBarHeight(context) + startView.getHeight() / 2;
endView.getLocationInWindow(location);
x2 = location[0] + endView.getWidth() / 2;
y2 = location[1] - PixTool.getStatusBarHeight(context) - 53;
Note: 這里的 getLocationInWindow 獲取到的坐標(biāo)是以屏幕左上角為原點(diǎn)計(jì)算的,所以真實(shí)的坐標(biāo)需要減去狀態(tài)欄以及ActionBar的高度(因?yàn)轫?xiàng)目沒有用到ActionBar,所以沒有減去這個(gè)高度)
通過貝塞爾曲線描繪一條彎曲的曲線
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 創(chuàng)建畫筆
Paint p = new Paint();
p.setColor(context.getResources().getColor(R.color.gray)); // 設(shè)置顏色
p.setStrokeWidth(PixTool.dip2px(context, 1)); // 設(shè)置寬度
p.setAntiAlias(true); // 抗鋸齒
// 設(shè)置虛線
PathEffect effects = new DashPathEffect(new float[]{PixTool.dip2px(context, 6), PixTool.dip2px(context, 3)}, 1);
p.setPathEffect(effects);
//畫貝塞爾曲線
p.setStyle(Paint.Style.STROKE);
Path path2 = new Path();
path2.moveTo(x1, y1);
float quaX = x1 / 4;
float quaY = (y1 + y2) / 2;
if (y2 - y1 < 0) {
quaX = (x1 + x2) / 2;
quaY = y2 - 100;
}else if (y2 - y1 < 50){
quaX = (x1 + x2) / 2;
quaY = y1 - 50;
}
path2.quadTo(quaX, quaY, x2, y2);
canvas.drawPath(path2, p);
}
Note: 這里的貝塞爾曲線的點(diǎn)需要一個(gè)優(yōu)化,當(dāng)終點(diǎn)y值小于起點(diǎn)y值得時(shí)候,需要再做一個(gè)處理。
繪制一個(gè)倒三角形
float length = 32; // 三角形的邊長
float x = x2 - length / 2;
p.setPathEffect(null); // 取消虛線效果
p.setStyle(Paint.Style.FILL); //設(shè)置填滿
// 畫三角形
Path path = new Path();
path.moveTo(x, y2);
path.lineTo(x + length, y2);
path.lineTo((x + x + length) / 2, y2 + 23);
path.close();
canvas.drawPath(path, p);
實(shí)現(xiàn)效果

附上完整代碼 (代碼略渣,歡迎交流學(xué)習(xí))
public class DashArrow extends View {
Context context;
float x1 = 0;
float y1 = 0;
float x2 = 0;
float y2 = 0;
public DashArrow(Context context, float x1, float y1, float x2, float y2) {
super(context);
this.context = context;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public DashArrow(Context context, View startView, View endView) {
super(context);
this.context = context;
int[] location = new int[2];
startView.getLocationInWindow(location);
x1 = location[0];
y1 = location[1] - PixTool.getStatusBarHeight(context) + startView.getHeight() / 2;
endView.getLocationInWindow(location);
x2 = location[0] + endView.getWidth() / 2;
y2 = location[1] - PixTool.getStatusBarHeight(context) - 53;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 創(chuàng)建畫筆
Paint p = new Paint();
p.setColor(context.getResources().getColor(R.color.gray)); // 設(shè)置顏色
p.setStrokeWidth(PixTool.dip2px(context, 1)); // 設(shè)置寬度
p.setAntiAlias(true); // 抗鋸齒
// 設(shè)置虛線
PathEffect effects = new DashPathEffect(new float[]{PixTool.dip2px(context, 6), PixTool.dip2px(context, 3)}, 1);
p.setPathEffect(effects);
//畫貝塞爾曲線
p.setStyle(Paint.Style.STROKE);
Path path2 = new Path();
path2.moveTo(x1, y1);
float quaX = x1 / 4;
float quaY = (y1 + y2) / 2;
if (y2 - y1 < 0) {
quaX = (x1 + x2) / 2;
quaY = y2 - 100;
}else if (y2 - y1 < 50){
quaX = (x1 + x2) / 2;
quaY = y1 - 50;
}
path2.quadTo(quaX, quaY, x2, y2);
canvas.drawPath(path2, p);
float length = 32; // 三角形的邊長
float x = x2 - length / 2;
p.setPathEffect(null); // 取消虛線效果
p.setStyle(Paint.Style.FILL); //設(shè)置填滿
// 畫三角形
Path path = new Path();
path.moveTo(x, y2);
path.lineTo(x + length, y2);
path.lineTo((x + x + length) / 2, y2 + 23);
path.close();
canvas.drawPath(path, p);
}
}