需求
需要一个箭头,连接1个View,指向(引导)另一个View
实现方案
拿到这个需求我就在想,应该如何实现会比较好。
考虑到Android平台分辨率碎片化严重,单纯的XML代码估计很难实现。
于是想到用Canvas来画。
实现思路比较简单:
- 计算两个View的起点和终点
- 通过贝塞尔曲线描绘一条弯曲的曲线
- 绘制一个倒三角形
计算两个View的起点和终点
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
获取到的坐标是以屏幕左上角为原点计算的,所以真实的坐标需要减去状态栏以及ActionBar的高度(因为项目没有用到ActionBar,所以没有减去这个高度)
通过贝塞尔曲线描绘一条弯曲的曲线
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 创建画笔
Paint p = new Paint();
p.setColor(context.getResources().getColor(R.color.gray)); // 设置颜色
p.setStrokeWidth(PixTool.dip2px(context, 1)); // 设置宽度
p.setAntiAlias(true); // 抗锯齿
// 设置虚线
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: 这里的贝塞尔曲线的点需要一个优化,当终点y值小于起点y值得时候,需要再做一个处理。
绘制一个倒三角形
float length = 32; // 三角形的边长
float x = x2 - length / 2;
p.setPathEffect(null); // 取消虚线效果
p.setStyle(Paint.Style.FILL); //设置填满
// 画三角形
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);
实现效果
附上完整代码 (代码略渣,欢迎交流学习)
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);
// 创建画笔
Paint p = new Paint();
p.setColor(context.getResources().getColor(R.color.gray)); // 设置颜色
p.setStrokeWidth(PixTool.dip2px(context, 1)); // 设置宽度
p.setAntiAlias(true); // 抗锯齿
// 设置虚线
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); //设置填满
// 画三角形
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);
}
}