在android中会经常用到自定义view来实现一些效果的显示,今天就举个例子,绘制一个像探测雷达的界面。雷达界面一般就是下面这种样子,我们下面就大概来仿照着这个图片做一下。
最终我做出来的效果:
分析一下上述图片需要绘制哪些图形。
虚线的xy坐标轴坐标轴上的距离刻度数和点4个箭头符号,东南西北文字5个虚线圆圈目标点的虚线,以及文字距离显示这里面东西很简单,直接如下
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获取宽的模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取高的模式 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //获取宽的尺寸 int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的尺寸 //如果是确切大小,直接赋值 if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } ViewHelper.ViewHightPixels = height; ViewHelper.ViewWidthPixels = width; //中心位置 centerX = width / 2; centerY = height / 2; Coordinate = new Point(centerX, centerY);//坐标系原点 //保存当前宽度高度 setMeasuredDimension(width, height); }这里面东西就有点多了,会在这里面绘制上面所述的各个图形。
完整代码如下:
view类
/** * 雷达图形 */ public class RadarView extends View { private Point Coordinate = new Point(1100, 500);//坐标系 private Paint mCirclePaint, mPointPaint, mTextPaint, mPaintLine;//画笔 private TreeSet<Float> circleDf = new TreeSet<>();//定义域 private Map<Float, Float> circleMap = new HashMap<>();//坐标点位集合 private Map<Float, Float> circleMap2 = new HashMap<>(); private Map<Float, Float> circleMap3 = new HashMap<>(); private Map<Float, Float> circleMap4 = new HashMap<>(); private Map<Float, Float> circleMap5 = new HashMap<>(); private int centerX, centerY; public static int width, height; public RadarView(Context context) { super(context); } public RadarView(Context context, @Nullable AttributeSet attrs) { super(ViewHelper.context, attrs); init();//初始化 } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); //获取宽的模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取高的模式 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //获取宽的尺寸 int heightSize = MeasureSpec.getSize(heightMeasureSpec); //获取高的尺寸 //如果是确切大小,直接赋值 if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } ViewHelper.ViewHightPixels = height; ViewHelper.ViewWidthPixels = width; centerX = width / 2; centerY = height / 2; Coordinate = new Point(centerX, centerY);//坐标系原点 //保存当前宽度高度 setMeasuredDimension(width, height); } private void init() { //初始化虚线圆圈 mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCirclePaint.setColor(Color.GREEN); mCirclePaint.setStrokeWidth(3); mCirclePaint.setStyle(Paint.Style.STROKE); mCirclePaint.setStrokeCap(Paint.Cap.ROUND); PathEffect effects1 = new DashPathEffect(new float[]{1, 2, 4, 8}, 1); mCirclePaint.setPathEffect(effects1); //初始化连接线 mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintLine.setColor(Color.GREEN); mPaintLine.setStrokeWidth(3); mPaintLine.setAntiAlias(true); mPaintLine.setStyle(Paint.Style.STROKE); mPaintLine.setStrokeCap(Paint.Cap.ROUND); PathEffect effects = new DashPathEffect(new float[]{5, 10}, 1); mPaintLine.setPathEffect(effects); //点位 mPointPaint = new Paint(); mPointPaint.setColor(Color.GREEN); mPointPaint.setStrokeWidth(5); //文字 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(Color.GREEN); mTextPaint.setTextSize(30); } double factLength = 0; int level = 100;//100个像素点为一个级别 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); try { resetCircleView(); ViewHelper.setCoordinate(Coordinate,canvas, level); canvas.save(); canvas.translate(Coordinate.x, Coordinate.y);//移动到view中心 // canvas.scale(1, -1);//轴交换 //绘制5个圆圈点 drawMap(canvas, circleMap); drawMap(canvas, circleMap2); drawMap(canvas, circleMap3); drawMap(canvas, circleMap4); drawMap(canvas, circleMap5); canvas.restore(); canvas.save(); canvas.translate(Coordinate.x, Coordinate.y); drawDataView(canvas, 2000, 160); drawDataView(canvas, 3000, 300); canvas.restore(); } catch (Exception e) { } } int lastX, lastY; @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 记录触摸点坐标 lastX = x; lastY = y; break; case MotionEvent.ACTION_MOVE: // 计算偏移量 int offsetX = x - lastX; int offsetY = y - lastY; //图形随手指滑动 if (Math.abs(offsetX) > 30 || Math.abs(offsetY) > 30) { Coordinate = new Point(centerX + offsetX, centerY + offsetY);//坐标系 invalidate(); } break; case MotionEvent.ACTION_UP: centerY = centerY + y - lastY; centerX = centerX + x - lastX; break; } return true; } //绘制线以及标注 private void drawDataView(Canvas canvas, double length, double angle) { try { Point point = ViewHelper.getPoinByLineAndAngle(length/10, 90 + angle); canvas.drawLine(0, 0, point.x, point.y, mPaintLine); canvas.drawCircle(point.x, point.y, 6, mPointPaint);// 小圆 DecimalFormat df = new DecimalFormat("#.000"); factLength = Double.parseDouble(df.format(length / 1000)); canvas.drawText(factLength + "km", point.x / 3 + 50, point.y / 3, mTextPaint);// 显示距离 } catch (Exception e) { } } /** * 绘制圆圈点位 */ private void drawMap(Canvas canvas, Map<Float, Float> resetCircleView) { for (Float key : resetCircleView.keySet()) { Float value = resetCircleView.get(key); canvas.drawPoint((float) (value * Math.cos(key)), (float) (value * Math.sin(key)), mCirclePaint); } } /** * 往集合添加坐标点 分为5个 100个像素点一个 */ int number1 = 100, number2 = 200, number3 = 300, number4 = 400, number5 = 500; private void resetCircleView() { try { //初始化圆圈数据源 for (float i = 1; i <= 360 * 2; i++) { circleDf.add(i); } circleMap.clear(); circleMap2.clear(); circleMap3.clear(); circleMap4.clear(); circleMap5.clear(); for (Float x : circleDf) { float thta = (float) (Math.PI / 180 * x); circleMap.put(thta, (float) number1); circleMap2.put(thta, (float) number2); circleMap3.put(thta, (float) number3); circleMap4.put(thta, (float) number4); circleMap5.put(thta, (float) number5); } } catch (Exception e) { } } }帮助类:
package test.hk.com.helper; /** * 画布工具类 */ public class ViewHelper { public static Context context; public static int ViewWidthPixels, ViewHightPixels; private static int maxLevel; /** * 得到view大小 */ public static Point getWinSize() { Point point = new Point(); point.x = ViewWidthPixels; point.y = ViewHightPixels; return point; } /** * 绘制坐标系 * * @param coo 坐标系原点 * @param canvas 画布 * @param level 级别 */ public static void setCoordinate(Point coo, Canvas canvas, int level) { Point winSize = getWinSize(); //初始化坐标系文字和箭头画笔 Paint paint = new Paint(); paint.setStrokeWidth(4); paint.setColor(Color.GREEN); paint.setTextSize(30); paint.setStyle(Paint.Style.FILL_AND_STROKE); //xy轴线 Paint paint_xy = new Paint(); paint_xy.setStrokeWidth(4); paint_xy.setColor(Color.GREEN); paint_xy.setStyle(Paint.Style.STROKE); paint_xy.setPathEffect(new DashPathEffect(new float[]{1, 5}, 1));//虚线 //绘制线 canvas.drawPath(getLinePath(coo), paint_xy); // 右箭头 canvas.drawLine(coo.x + coo.y, coo.y, coo.x + coo.y - 40, coo.y - 20, paint); canvas.drawLine(coo.x + coo.y, coo.y, coo.x + coo.y - 40, coo.y + 20, paint); //左箭头 canvas.drawLine(coo.x - coo.y, coo.y, coo.x - coo.y + 40, coo.y - 20, paint); canvas.drawLine(coo.x - coo.y, coo.y, coo.x - coo.y + 40, coo.y + 20, paint); //下箭头 canvas.drawLine(coo.x, winSize.y, coo.x - 20, winSize.y - 40, paint); canvas.drawLine(coo.x, winSize.y, coo.x + 20, winSize.y - 40, paint); // 上箭头 canvas.drawLine(coo.x, 0, coo.x - 20, 40, paint); canvas.drawLine(coo.x, 0, coo.x + 20, 40, paint); //为坐标系绘制文字 drawCoordinateText(canvas, coo, winSize, paint, level); } /** * 坐标系绘制刻度和文字 * @param canvas 画布 * @param coo 坐标系原点 * @param winSize 屏幕尺寸 * @param paint 画笔 * @param number 放大缩小级别 */ private static void drawCoordinateText(Canvas canvas, Point coo, Point winSize, Paint paint, int number) { //y正轴文字 maxLevel = coo.y / 100 + 1;//绘制几个刻度 for (int i = 1; i < maxLevel; i++) { paint.setStrokeWidth(2); canvas.drawText(number * i / 100 + "km", coo.x + 20, coo.y + 10 - number * i, paint);//文字 paint.setStrokeWidth(5); canvas.drawLine(coo.x, coo.y - number * i, coo.x + 10, coo.y - number * i, paint);//刻度点 } //x正轴文字 for (int i = 1; i < maxLevel; i++) { paint.setStrokeWidth(2); canvas.drawText(number * i / 100 + "km", coo.x - 20 + number * i, coo.y + 40, paint); paint.setStrokeWidth(5); canvas.drawLine(coo.x + number * i, coo.y, coo.x + number * i, coo.y - 10, paint); } paint.setTextSize(50); canvas.drawText("E", coo.x + coo.y + 20, coo.y + 20, paint); canvas.drawText("W", coo.x - coo.y - 60, coo.y + 20, paint); canvas.drawText("N", coo.x - 60, 40, paint); canvas.drawText("S", coo.x - 60, winSize.y, paint); } /** * 坐标系xy轴路径 * @param coo 坐标原点 */ public static Path getLinePath(Point coo) { Path path = new Path(); //x正半轴线 path.moveTo(coo.x, coo.y); path.lineTo(coo.x + coo.y, coo.y); //x负半轴线 path.moveTo(coo.x, coo.y); path.lineTo(coo.x - coo.y, coo.y); //y正半轴线 path.moveTo(coo.x, coo.y); path.lineTo(coo.x, -RadarView.width); //y负半轴线 path.moveTo(coo.x, coo.y); path.lineTo(coo.x, RadarView.width); return path; } //已知角度和斜边,求点 public static Point getPoinByLineAndAngle(double length, double angle) { //获得弧度 double radian = 2 * Math.PI / 360 * angle; int line1 = (int) (Math.sin(radian) * length);//y int line2 = (int) (Math.cos(radian) * length);//x Point point1 = new Point(line1, line2); return point1; } }使用
<test.com.view.RadarView android:id="@+id/radarView" android:layout_width="match_parent" android:layout_height="match_parent" android:layerType="software" />
上面代码都是用的固定像素,所以在不同屏幕上显示效果可能不能很好适配,要适配的话可以在代码中添加修改。到这里主要的功能就结束了,为了方便理解写的比较乱,见谅见谅~,可以继续在上面修改添加新的功能,比如放大缩小等等,主要就是要了解安卓屏幕坐标以及绘制的基本方法,那么做起来就很顺手了。
