如图汽车仪表盘可以分解为:绘制大半圆弧,绘制刻度,绘制指针
2.1 :整个外仪表盘 DashBoardView,这个是布局文件自定义的View,我们只需要定义大弧的缺口和半径即可 2.2 :绘制刻度:刻度实际就是一个个很小的小矩形,每个刻度就是一个矩形,每一个刻度都有自己单独的坐标,具体实现就是先绘制小矩形,并加入到 path路径中
然后根据目标刻度的数量和圆弧长度,得到每个刻度之间距离
圆弧长度 PathMeasure .
2.3 :绘制指针:重点是在绘制指针直线时,需要知道起点和终点坐标,起点坐标好说,就是仪表盘圆弧的中心点,终点坐标,需要根据刻度求解出角度,然后通过正弦或者余弦函数计算出坐标。 3:代码示例 3.1 :自定义仪表盘 DashBoardViewDashBoardView.java
public DashBoardView extends View {
........
// 1: 定义大弧的缺口角度和半径
private static final int DASH_BOARD_ANGLT = 120 ;
// 2: 定义仪表板的半径
private static final int DASH_BOARD_DADIUS = Utils.dp2px(150);
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 3: 画弧线
// 3.1 先确定在页面的位置 -----比如在页面中心,可以通过下面的矩形确定位置
// Reacf reacf = new Reacf (left,top,right,bottom);
Reacf arcReacf = new Reacf (getWidth()/2 - RADIUS , getHeight()/2-RADIUS,
getWidth()/2 + RADIUS , getHeight()/2 + RADIUS);
canvas.drawArc (reacf, 90+ ANGLE/2 , 360-ANGLE,false,paint);
}
}
// 布局文件
3.2 :在仪表盘DashBoardView上画刻度
每一个小刻度实际上就是一个小矩形,我们可以通过PathDashEffect来画刻度
Path dash = new Path();//刻度
// 绘制一个小刻度矩形,每个分割点就是一个矩形,最后一个参数是顺时针
dash.addRect(0, 0, Utils.dp2px(2), Utils.dp2px(10), Path.Direction.CW);
//计算总弧形长度 算出平均间隔
PathMeasure pathMeasure = new PathMeasure(arc, false);//是否闭合
//参数1 刻度本体。 参数2 间隔距离(弧形长度 - 刻度的厚度 然后平均分20分)。参数3 起始第一个距离开头。 参数4 旋转类型
effect = new PathDashPathEffect(dash, (pathMeasure.getLength() - Utils.dp2px(2)) / 20, 0, PathDashPathEffect.Style.ROTATE);
DashBoardView.java
public DashBoardView extends View {
........
// 1: 定义大弧的缺口角度和半径
private static final int DASH_BOARD_ANGLT = 120 ;
// 2: 定义仪表板的半径
private static final int DASH_BOARD_DADIUS = Utils.dp2px(150);
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 3: 画弧线
// 3.1 先确定在页面的位置 -----比如在页面中心,可以通过下面的矩形确定位置
// Reacf reacf = new Reacf (left,top,right,bottom);
Reacf arcReacf = new Reacf (getWidth()/2 - RADIUS , getHeight()/2-RADIUS,
getWidth()/2 + RADIUS , getHeight()/2 + RADIUS);
canvas.drawArc (reacf, 90+ ANGLE/2 , 360-ANGLE,false,paint);
// 4: 画刻度
paint.setPathEffect(effect);
paint.setPathEffect(null);//置空
}
}
3.3 :在仪表盘DashBoardView上画指针
画指针需要知道两个点: 起始点(startX , startY), 2: 终点(stopX, stopY)
终点可以根据刻度计算角度,然后通过 正弦或者余弦函数求解
终点要各自加上了仪表盘中心的位置,也就屏幕的宽高一半。否则线就画出屏幕了。因为终点是依据弧形原点画的。
startX : getWidth()/2
startY : getHeigth()/2
stopX : (float) Math.cos(Math.toRadians(getAngleFromMark(5))) * LENGTH + getWidth() / 2
stopY : (float) Math.sin(Math.toRadians(getAngleFromMark(5))) * LENGTH + getHeight() / 2
// 根据刻度求角度
private int getAngleFromMark(int mark) {
return (int) (90 + (float) ANGLE / 2 + (360 - (float) ANGLE) / 20 * mark);
}
canvas.drawLine (startX, startY, stopX,stopY);
3.4 完整代码
public class DashBoard extends View {
private static final int ANGLE = 120;//仪表盘 缺口 弧度
private static final float LENGTH = Utils.dp2px(100);//指针长度
private static final float RADIUS = Utils.dp2px(150);//半径
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿标志
Path dash = new Path();//刻度
PathDashPathEffect effect;//刻度间隔
public DashBoard(Context context) {
super(context);
}
public DashBoard(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DashBoard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{//直接写大括号,会在所有构造方法super方法执行之后 执行
paint.setStyle(Paint.Style.STROKE);//行程
paint.setStrokeWidth(Utils.dp2px(2));//弧线宽度
//----画弧线----
Path arc = new Path();
//参数,先确定这个弧线的矩形。 起始点角度。 扫过的角度
arc.addArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
90 + ANGLE/2, 360 - ANGLE);
//----画刻度----
dash.addRect(0, 0, Utils.dp2px(2), Utils.dp2px(10), Path.Direction.CW);//每个分割点就是一个矩形,最后一个参数是顺时针
//计算总弧形长度 算出平均间隔
PathMeasure pathMeasure = new PathMeasure(arc, false);//是否闭合
//参数1 刻度本体。 参数2 间隔距离(弧形长度 - 刻度的厚度 然后平均分20分)。参数3 起始第一个距离开头。 参数4 旋转类型
effect = new PathDashPathEffect(dash, (pathMeasure.getLength() - Utils.dp2px(2)) / 20, 0, PathDashPathEffect.Style.ROTATE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画弧 useCenter 是否往圆心里画
canvas.drawArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
90 + ANGLE/2, 360 - ANGLE, false, paint);
//画刻度
paint.setPathEffect(effect);
canvas.drawArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
90 + ANGLE/2, 360 - ANGLE, false, paint);
paint.setPathEffect(null);//置空
//画指针
canvas.drawLine(getWidth() / 2, getHeight() / 2,
(float) Math.cos(Math.toRadians(getAngleFromMark(5))) * LENGTH + getWidth() / 2,
(float) Math.sin(Math.toRadians(getAngleFromMark(5))) * LENGTH + getHeight() / 2,
paint);
}
//根据刻度求角度
private int getAngleFromMark(int mark) {
return (int) (90 + (float) ANGLE / 2 + (360 - (float) ANGLE) / 20 * mark);
}
3.5 :绘制刻度上的数字
根据 drawText ( String textValue, float X, float Y, paint) 函数我们知道,绘制Text关键在于 x轴和Y轴坐标
3.5.1 :总的刻度数 -----得到等分刻度份数(20等分) 3.5.2 :有个等分份数结合总的弧长 ------得到每个等分的弧长,然后通过函数将弧长转化成角度 3.5.3 :有了角度通过sin或者cos函数----就可以计算出X轴和Y轴坐标 private void drawTextCircle(Canvas canvas) {
float α;
float[] p;
float angle = mSweepAngle * 1f / mSection;
for (int i = 0; i <= mSection; i++) {
α = mStartAngle + angle * i;
p = getCoordinatePoint(mRadius - mLength2, α);
if (α % 360 > 135 && α % 360 < 225) {
mTextPaint.setTextAlign(Paint.Align.LEFT);
} else if ((α % 360 >= 0 && α % 360 < 45) || (α % 360 > 315 && α % 360 <= 360)) {
mTextPaint.setTextAlign(Paint.Align.RIGHT);
} else {
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
mTextPaint.getTextBounds(mHeaderText, 0, mTexts[i].length(), mRectText);
int txtH = mRectText.height();
if (i <= 1 || i >= mSection - 1) {
canvas.drawText(mTexts[i], p[0], p[1] + txtH / 2, mTextPaint);
} else if (i == 3) {
canvas.drawText(mTexts[i], p[0] + txtH / 2, p[1] + txtH, mTextPaint);
} else if (i == mSection - 3) {
canvas.drawText(mTexts[i], p[0] - txtH / 2, p[1] + txtH, mTextPaint);
} else {
canvas.drawText(mTexts[i], p[0], p[1] + txtH, mTextPaint);
}
}
}
/**
* 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
*/
public float[] getCoordinatePoint(int radius, float angle) {
float[] point = new float[2];
double arcAngle = Math.toRadians(angle); //将角度转换为弧度
if (angle < 90) {
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 90) {
point[0] = mCenterX;
point[1] = mCenterY + radius;
} else if (angle > 90 && angle < 180) {
arcAngle = Math.PI * (180 - angle) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
} else if (angle == 180) {
point[0] = mCenterX - radius;
point[1] = mCenterY;
} else if (angle > 180 && angle < 270) {
arcAngle = Math.PI * (angle - 180) / 180.0;
point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
} else if (angle == 270) {
point[0] = mCenterX;
point[1] = mCenterY - radius;
} else {
arcAngle = Math.PI * (360 - angle) / 180.0;
point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
}
return point;
}
4:指针根据条件移动 5:总结
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)