需求:雷达分为单点避障雷达和多点避障雷达,单点避障雷达又分为前后避障,多点避障雷达也分为前后避障,前避障和后避障都涉及到8个方向的避障,例如前避障的4个角度分别为-30~-15,-15~0,0~15,15~30。4个区间的雷达探测距离分别<10m(显示红红红),10-15m(显示黄黄红),>15m(显示绿绿绿)三个区间,等于25.5米则为无效点。效果如图所示,左图为单点避障雷达效果,右图为多点雷达效果。
1.设置自定义控件相关的属性,在attr文件中:
<declare-styleable name="RadarView"> <attr name="firstColor" format="color" />//第一层的颜色 <attr name="secondColor" format="color" />//第二层的颜色 <attr name="thirdColor" format="color" />//第三层的颜色 <attr name="stroke_width" format="dimension" />//圆弧的宽度 <attr name="divider_width" format="dimension" />//圆弧之间的间隔 <attr name="text_color_1" format="color" />//实时变化的距离的颜色 <attr name="text_color_2" format="color" />//10M,15M的文字颜色 <attr name="text_size_1" format="dimension" />//上面对应的文字的大小 <attr name="text_size_2" format="dimension" />//上面对应的文字的大小 </declare-styleable>
2.开始自定义控件
package com.example.myapplication3; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.SparseIntArray; import android.view.View; import java.util.Locale; /** * @author Kit Zuo * 2428382937@qq.com * @create 2019/12/05 * @Desc 自定义控件实现避障雷达图 */ public class RadarView extends View { private float text_size_1; private float text_size_2; private float stroke_width; private float divide_width; private int textColor_1; private int textColor_2; private int thirdColor; private int secondColor; private int firstColor; private Paint mFirstPaint; private Paint mSecondPaint; private Paint mThirdPaint; private Paint mTextPaint; private Paint mLinePaint; // 半径 private float inner_radius = 100f; // 中心点 private float centerX = 100f; private float centerY = 100f; // 开始角度 private RectF rect_first; private RectF rect_second; private RectF rect_third; private float start_angle = 270; private static final float origin_angel = -60f; private static final float angel_divider = 30f; private float radius_third; private float radius_second; private Path path;//多点前避障的path private Path path_back;多点后避障的path private Path path_single;//单点前避障的path private Path path_single_back;//单点后避障的path private boolean isShowBac = true;//是否连接了后避障雷达 private boolean isRadarType_multi = true;//是否是多点避障雷达 private static final int multi_count = 4;//显示的角度区间个数 private float angle; public RadarView(Context context) { this(context, null); } public RadarView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RadarView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RadarView); this.firstColor = typedArray.getColor(R.styleable.RadarView_firstColor, Color.parseColor("#ff0000")); this.secondColor = typedArray.getColor(R.styleable.RadarView_secondColor, Color.parseColor("#ffcc00")); this.thirdColor = typedArray.getColor(R.styleable.RadarView_thirdColor, Color.parseColor("#00ff00")); this.textColor_1 = typedArray.getColor(R.styleable.RadarView_text_color_1, Color.parseColor("#ff6600")); this.textColor_2 = typedArray.getColor(R.styleable.RadarView_text_color_2, Color.parseColor("#ffffff")); this.stroke_width = typedArray.getDimension(R.styleable.RadarView_stroke_width, 30); this.divide_width = typedArray.getDimension(R.styleable.RadarView_divider_width, 60); this.text_size_1 = typedArray.getDimension(R.styleable.RadarView_text_size_1, 20); this.text_size_2 = typedArray.getDimension(R.styleable.RadarView_text_size_2, 30); typedArray.recycle(); init(); } private void init() { mTextPaint = new Paint(); mTextPaint.setStyle(Paint.Style.STROKE); mTextPaint.setStrokeWidth(2); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(text_size_1); mTextPaint.setColor(Color.BLACK); mTextPaint.setTextAlign(Paint.Align.CENTER); int windowHeight = DensityUtil.getWindowHeight(getContext()); int windowWidth = DensityUtil.getWindowWidth(getContext()); mThirdPaint = new Paint(); mThirdPaint.setStyle(Paint.Style.STROKE); mThirdPaint.setStrokeWidth(stroke_width); mThirdPaint.setAntiAlias(true); mSecondPaint = new Paint(); mSecondPaint.setStyle(Paint.Style.STROKE); mSecondPaint.setStrokeWidth(stroke_width); mSecondPaint.setAntiAlias(true); mFirstPaint = new Paint(); mFirstPaint.setStyle(Paint.Style.STROKE); mFirstPaint.setAntiAlias(true); mFirstPaint.setStrokeWidth(stroke_width); mLinePaint = new Paint(); mLinePaint.setStyle(Paint.Style.STROKE); mLinePaint.setStrokeWidth(2); mLinePaint.setAntiAlias(true); mLinePaint.setColor(ContextCompat.getColor(getContext(), R.color.white)); centerX = windowWidth >> 1; centerY = windowHeight >> 1; radius_third = inner_radius + divide_width * 2; radius_second = inner_radius + divide_width; rect_third = new RectF(centerX - radius_third, centerY - radius_third, centerX + radius_third, centerY + radius_third); rect_second = new RectF(centerX - radius_second, centerY - radius_second, centerX + radius_second, centerY + radius_second); rect_first = new RectF(centerX - inner_radius, centerY - inner_radius, centerX + inner_radius, centerY + inner_radius); path = new Path(); path_back = new Path(); path_single = new Path(); path_single_back = new Path(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //-30,-15,-15-0,0-15,15-30 if (isRadarType_multi) { // canvas.save(); // canvas.rotate(angle,centerX,centerY); int size = dataRadar.size(); if (size >= 0) { canvas.save(); for (int n = 0; n < multi_count; n++) { doColor(n); float start_angle = origin_angel + this.start_angle; canvas.drawArc(rect_second, start_angle, angel_divider, false, mSecondPaint); canvas.drawArc(rect_first, start_angle, angel_divider, false, mFirstPaint); path.addArc(rect_third, start_angle, angel_divider); canvas.drawPath(path, mThirdPaint); double v = Math.PI * (90 - angel_divider) / 180; if (n < 3) { float x_1 = (float) (centerX - (radius_third + stroke_width / 2) * Math.cos(v)); float y_1 = (float) (centerY - (radius_third + stroke_width / 2) * Math.sin(v)); float x_2 = (float) (centerX - (inner_radius - stroke_width / 2) * Math.cos(v)); float y_2 = (float) (centerY - (inner_radius - stroke_width / 2) * Math.sin(v)); canvas.drawLine(x_1, y_1, x_2, y_2, mLinePaint); } mTextPaint.setTextSize(text_size_2); mTextPaint.setColor(textColor_2); canvas.drawTextOnPath(String.format(Locale.US, "%.1f", avoid_dis[n]), path, 0, 10, mTextPaint); canvas.rotate(angel_divider, centerX, centerY); } canvas.restore(); float y_4 = centerY - (inner_radius - stroke_width / 2); float y_7 = centerY - (radius_second - stroke_width / 2); mTextPaint.setTextSize(text_size_1); mTextPaint.setColor(textColor_1); canvas.drawText("15M", centerX, y_7 - divide_width / 2 - 10, mTextPaint); canvas.drawText("10M", centerX, y_4 - divide_width / 2 - 10, mTextPaint); if (isShowBac) { canvas.save(); for (int m = 4; m < multi_count * 2; m++) { doColor(m); float start_angle = 90 - origin_angel; canvas.drawArc(rect_second, start_angle, -angel_divider, false, mSecondPaint); canvas.drawArc(rect_first, start_angle, -angel_divider, false, mFirstPaint); path_back.addArc(rect_third, start_angle, -angel_divider); canvas.drawPath(path_back, mThirdPaint); double v = Math.PI * (90 - angel_divider) / 180; if (m < 7) { float x_1 = (float) (centerX - (radius_third + stroke_width / 2) * Math.cos(v)); float y_1 = (float) (centerY + (radius_third + stroke_width / 2) * Math.sin(v)); float x_2 = (float) (centerX - (inner_radius - stroke_width / 2) * Math.cos(v)); float y_2 = (float) (centerY + (inner_radius - stroke_width / 2) * Math.sin(v)); canvas.drawLine(x_1, y_1, x_2, y_2, mLinePaint); } mTextPaint.setTextSize(text_size_2); mTextPaint.setColor(textColor_2); canvas.drawTextOnPath(String.format(Locale.US, "%.1f", avoid_dis_back[m - 4]), path_back, 0, 10, mTextPaint); canvas.rotate(-angel_divider, centerX, centerY); } canvas.restore(); float y_5 = centerY + (inner_radius + stroke_width / 2); float y_8 = centerY + (radius_second + stroke_width / 2); mTextPaint.setTextSize(text_size_1); mTextPaint.setColor(textColor_1); canvas.drawText("15M", centerX, y_8 + divide_width / 2 - 10, mTextPaint); canvas.drawText("10M", centerX, y_5 + divide_width / 2 - 10, mTextPaint); } } // canvas.restore(); } else { doColor(8); float start_angle = origin_angel + this.start_angle + angel_divider; canvas.drawArc(rect_second, start_angle, angel_divider * 2, false, mSecondPaint); canvas.drawArc(rect_first, start_angle, angel_divider * 2, false, mFirstPaint); path_single.addArc(rect_third, start_angle, angel_divider * 2); canvas.drawPath(path_single, mThirdPaint); mTextPaint.setTextSize(text_size_2); mTextPaint.setColor(textColor_2); canvas.drawTextOnPath(String.format(Locale.US, "%.1f", single_avoid), path_single, 0, 10, mTextPaint); float y_4 = centerY - (inner_radius - stroke_width / 2); float y_7 = centerY - (radius_second - stroke_width / 2); mTextPaint.setTextSize(text_size_1); mTextPaint.setColor(textColor_1); canvas.drawText("15M", centerX, y_7 - divide_width / 2 - 10, mTextPaint); canvas.drawText("10M", centerX, y_4 - divide_width / 2 - 10, mTextPaint); if (isShowBac) { doColor(9); float start_angle_single = 90 - origin_angel - angel_divider; canvas.drawArc(rect_second, start_angle_single, -angel_divider * 2, false, mSecondPaint); canvas.drawArc(rect_first, start_angle_single, -angel_divider * 2, false, mFirstPaint); path_single_back.addArc(rect_third, start_angle_single, -angel_divider * 2); canvas.drawPath(path_single_back, mThirdPaint); mTextPaint.setTextSize(text_size_2); mTextPaint.setColor(textColor_2); canvas.drawTextOnPath(String.format(Locale.US, "%.1f", single_avoid_back), path_single_back, 0, 10, mTextPaint); float y_5 = centerY + (inner_radius + stroke_width / 2); float y_8 = centerY + (radius_second + stroke_width / 2); mTextPaint.setTextSize(text_size_1); mTextPaint.setColor(textColor_1); canvas.drawText("15M", centerX, y_8 + divide_width / 2 - 10, mTextPaint); canvas.drawText("10M", centerX, y_5 + divide_width / 2 - 10, mTextPaint); } } } private void doColor(int n) { int warn_index = dataRadar.get(n + 1); if (warn_index >= 1) { if (warn_index == 1) { mThirdPaint.setColor(thirdColor); mSecondPaint.setColor(thirdColor); mFirstPaint.setColor(thirdColor); } else if (warn_index == 2) { mThirdPaint.setColor(secondColor); mSecondPaint.setColor(secondColor); mFirstPaint.setColor(thirdColor); } else if (warn_index == 3) { mThirdPaint.setColor(firstColor); mSecondPaint.setColor(firstColor); mFirstPaint.setColor(firstColor); } } else { mThirdPaint.setColor(thirdColor); mSecondPaint.setColor(thirdColor); mFirstPaint.setColor(thirdColor); } } public void setCenter(float centerX, float centerY, float inner_radius, float start_angle) { this.start_angle = start_angle + 270; this.inner_radius = inner_radius; this.centerX = centerX; this.centerY = centerY; rect_third.set(new RectF(centerX - (inner_radius + divide_width * 2), centerY - (inner_radius + divide_width * 2), centerX + (inner_radius + divide_width * 2), centerY + (inner_radius + divide_width * 2))); rect_second.set(new RectF(centerX - (inner_radius + divide_width), centerY - (inner_radius + divide_width), centerX + (inner_radius + divide_width), centerY + (inner_radius + divide_width))); rect_first.set(new RectF(centerX - inner_radius, centerY - inner_radius, centerX + inner_radius, centerY + inner_radius)); invalidate(); } private float[] avoid_dis = {25.5f, 25.5f, 25.5f, 25.5f};//多点前避障4个区间的距离 private float[] avoid_dis_back = {25.5f, 25.5f, 25.5f, 25.5f};//多点后避障4个区间的距离 private float single_avoid;//单点前避障的探测距离 private float single_avoid_back;//单点后避障的探测距离 public void setDir(float[] avoid_dis) { this.avoid_dis = avoid_dis; } public void setDirBack(float[] avoid_dis) { this.avoid_dis_back = avoid_dis; } private SparseIntArray dataRadar = new SparseIntArray(); public void setDataRadar(SparseIntArray dataRadar) { this.dataRadar = dataRadar; invalidate(); } public boolean isShowBac() { return isShowBac; } public void setShowBac(boolean showBac) { isShowBac = showBac; } public boolean isRadarType_multi() { return isRadarType_multi; } public void setRadarType_multi(boolean radarType_multi) { isRadarType_multi = radarType_multi; } public float getSingle_avoid() { return single_avoid; } public void setSingle_avoid(float single_avoid) { this.single_avoid = single_avoid; } public float getSingle_avoid_back() { return single_avoid_back; } public void setSingle_avoid_back(float single_avoid_back) { this.single_avoid_back = single_avoid_back; } public void setAngle(float angle) { this.angle = angle; } }
3.总结
这个自定义控件虽然非常的简单,用到的都是比较简单的数学知识,但是实际应用比较广泛,也比较有趣,所以记录一下。