在用导航软件的时候,在导航过程中会有一个标志指示你往那个方向走,因为道路口多种多样,出口也不一样,如果都用图片代替,会很占应用大小,但是如果有一个自定义的转向标,我们只需给他返回一个方向和出口的方向,自动显示就好了。
这里只定义了正常的转向和环岛的转向标,自定义view用画笔画出来的,通过一系列的三角函数计算实现功能。
示例几个典型的转向标,灰色代表出口方向
右转
左上转弯
直行
环岛的出口和方向
首先是要分析,这个图标是怎么实现的,我的思路是,直行的图标分为4个部分,分别为三角形,第二段线段,圆弧,第一段线段
当然要去确定一个中心点,这样能确定出来这个图标的位置和通过这个点去计算其他部分的坐标的起始位置。我这里定义了一些变量:
private Paint mLinePaint; // 画直线的画笔
private Paint mTrianglePaint; // 画三角形的画笔
private Paint mArcPaint; // 画圆弧的画笔
private Paint mOtherRoadPaint; // 画其他路出口的画笔
private float mRootWidth;
private float mRootHeight;
/**
* 转向标基准点,假设控件宽为w,高度为h
* 右转时,基准点为 (1/3w,1/2h),如果出口方向为13-15大角度的话,基准点为(1/3w,1/3h)
* 左转时,基准点为 (2/3w,1/2h),如果出口方向为1-3大角度的话,基准点为(2/3w,1/3h)
* 直行时.基准点为 (1/2w,1/2h)
*/
private float mBaseX;
private float mBaseY;
/**
* 普通icon的一些参数
*/
private float mMainRoadIconWidth; // 高亮图标显示的宽度 ------ 基准
private float mFirstLineHeight; // 第一段线的高度/长度 ------ 基准
private float mNormalCircleArcRadius; // 正常转向标圆弧半径
private float mSweepAngle; // 圆弧扫过的角度,即圆弧夹角
private float mNormalCircleArcCenterX; // 正常转向标圆弧中心点X坐标
private float mNormalCircleArcCenterY; // 正常转向标圆弧中心点Y坐标
private float mSecondLineStartX; // 第二段线段起点的x坐标
private float mSecondLineStartY; // 第二段线段起点的y坐标
private float mSecondLineEndX; // 第二段线段终点的x坐标
private float mSecondLineEndY; // 第二段线段终点的y坐标
/**
* 箭头三角形的参数
*/
private float mTriangleLeftX; // 三角形左顶点x坐标
private float mTriangleLeftY; // 三角形左顶点y坐标
private float mTriangleRightX; // 三角形右顶点x坐标
private float mTriangleRightY; // 三角形右顶点y坐标
private float mTriangleTopX; // 三角形顶点x坐标
private float mTriangleTopY; // 三角形顶点y坐标
/**
* 环岛icon的一些参数
*/
private float mRotaryRadius; // 环岛图标的圆半径
private float mRotaryStartAngle; // 环岛圆弧开始角度
private float mRotarySweepAngle; // 环岛圆弧扫过的角度
private float mRotaryLinesHeight; // 环岛线的高度
private float mRotaryAllExitLineHeight; // 环岛其他出口显示的长度
private float mRotaryMainRoadIconWidth; // 环岛图标高亮图标显示的宽度
/**
* 环岛基准点
* 圆心位置控制图标位置
*/
private float mRotaryCenterX; // 环岛图标圆的x坐标
private float mRotaryCenterY; // 环岛图标圆的y坐标
private boolean mIsTurnRight; // 是否是右转
private boolean mIsBigAngleTurn; // 是否是大角度转弯
以上变量的名字含义都有说明,重点说一下mBaseX,mBaseY这两个,这是整个图标的基准点,通过改变这个基准点会改变图标的整体位置,上面有说明基准点的设置规则。
以右转弯为例,下面的这个图形标识了每个点的坐标计算,这样求出每个点的坐标就可以拼凑出我们想要的标志了。
下面的这段代码就是实现上面的运算各个点的坐标,因为需求不同,所以下面会有一些运算的个人规则,但是总体不变,因为三角函数本来就是有正负的,比如sin90°和sin450°都等于1,sin90° = - sin270° 等等,这些都是三角函数的基本运算规则。所以我们没有必要去分象限,分度数的正负来计算,只要坐标系转换正确穿进去的角度就可以算出正确的值。
private void calculateEverPoint() {
//当方向为左边的时候,转变为右边对应的点,求出角度,用右边对应的角度求坐标后根据对称轴对称回来
int direction = mTurnIconModel.toDirection;
float secondLineHeight = direction == 0 ? 0 : mFirstLineHeight / 2;
if (direction < 8) {
direction = 16 - direction;// 如果是左转,都以右转计算后求对称角度
}
float calculateAngle = direction * 22.5f - 270;
mSecondLineStartX = (float) (mNormalCircleArcCenterX + Math.sin(Math.toRadians(calculateAngle)) * mNormalCircleArcRadius);
mSecondLineStartY = (float) (mNormalCircleArcCenterY - Math.cos(Math.toRadians(calculateAngle)) * mNormalCircleArcRadius);
mSecondLineEndX = (float) (mSecondLineStartX + Math.cos(Math.toRadians(calculateAngle)) * secondLineHeight);
mSecondLineEndY = (float) (mSecondLineStartY + Math.sin(Math.toRadians(calculateAngle)) * secondLineHeight);
calculateTrianglePoint(calculateAngle, mMainRoadIconWidth * 3, mMainRoadIconWidth * 2);
//先计算一下右转时候同样角度的点的坐标,如果是左转的话,y坐标不变,X左边根据对称轴求对称的坐标
if (!mIsTurnRight) {
mSecondLineStartX = calculateSymmetryPoint(mSecondLineStartX);
mSecondLineEndX = calculateSymmetryPoint(mSecondLineEndX);
mTriangleLeftX = calculateSymmetryPoint(mTriangleLeftX);
mTriangleRightX = calculateSymmetryPoint(mTriangleRightX);
mTriangleTopX = calculateSymmetryPoint(mTriangleTopX);
}
// 直行 画图有误差
if (mTurnIconModel.toDirection == 8) {
mSecondLineStartY = mSecondLineStartY + 2f;
mSecondLineEndY = mSecondLineEndY - 2f;
}
}
上面示例的图标三角形是类似于箭头的,这种实现是用canvas的path绘制的
下面这段代码是计算三角形的各个点坐标,left和right坐标变化是为了拉长,这样绘制的线就是类似于箭头的
private void calculateTrianglePoint(float calculateAngle, float bottomLength, float height) {
mTriangleLeftX = (float) (mSecondLineEndX + Math.sin(Math.toRadians(calculateAngle)) * (bottomLength / 2));
mTriangleLeftY = (float) (mSecondLineEndY - Math.cos(Math.toRadians(calculateAngle)) * (bottomLength / 2));
mTriangleRightX = (float) (mSecondLineEndX - Math.sin(Math.toRadians(calculateAngle)) * (bottomLength / 2));
mTriangleRightY = (float) (mSecondLineEndY + Math.cos(Math.toRadians(calculateAngle)) * (bottomLength / 2));
mTriangleTopX = (float) (mSecondLineEndX + Math.cos(Math.toRadians(calculateAngle)) * height);
mTriangleTopY = (float) (mSecondLineEndY + Math.sin(Math.toRadians(calculateAngle)) * height);
//此计算是为了让三角形有凹角度
float offsetHeight = mTurnIconModel.type == TurnIconModel.Type.junction ? 8f : 4f;
mTriangleLeftX = (float) (mTriangleLeftX - Math.cos(Math.toRadians(calculateAngle)) * offsetHeight);
mTriangleLeftY = (float) (mTriangleLeftY - Math.sin(Math.toRadians(calculateAngle)) * offsetHeight);
mTriangleRightX = (float) (mTriangleRightX - Math.cos(Math.toRadians(calculateAngle)) * offsetHeight);
mTriangleRightY = (float) (mTriangleRightY - Math.sin(Math.toRadians(calculateAngle)) * offsetHeight);
}
而实现箭头标志连接的代码为:
private void drawTriangle(Canvas canvas) {
Path path = new Path();
path.moveTo(mTriangleTopX, mTriangleTopY);// 此点为多边形的起点
path.lineTo(mTriangleLeftX, mTriangleLeftY);
path.lineTo(mSecondLineEndX, mSecondLineEndY);
path.lineTo(mTriangleRightX, mTriangleRightY);
path.lineTo(mTriangleTopX, mTriangleTopY);
path.close(); // 使这些点构成封闭的多边形
canvas.drawPath(path, mTrianglePaint);
}
哦对了,这是指右转弯的代码,左转弯的时候,除了基础点不一样,剩下的都是根据图形大小的中间进行x轴对称处理的点,这样一套计算就可以得到左右转弯的代码了。
画环岛的时候,其实计算方法和这个直行图标一样,都是根据三角函数计算的,下面已经给出图了,计算的点也已经给出了,就不画具体的步骤了,实现的思路都一样
资源地址下载: