android 绘制坐标系(雷达探测界面)

简介

在android中会经常用到自定义view来实现一些效果的显示,今天就举个例子,绘制一个像探测雷达的界面。雷达界面一般就是下面这种样子,我们下面就大概来仿照着这个图片做一下。

最终我做出来的效果:

分析一下上述图片需要绘制哪些图形。

  • 虚线的xy坐标轴
  • 坐标轴上的距离刻度数和点
  • 4个箭头符号,东南西北文字
  • 5个虚线圆圈
  • 目标点的虚线,以及文字距离显示

主要分为两步,1重写onMeasure()来确定雷达图像的显示大小,2重写onDraw()绘制上面所说的一些图形。

onMeasure()确定大小

这里面东西很简单,直接如下

  @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);
    }

onDraw()绘制图形

这里面东西就有点多了,会在这里面绘制上面所述的各个图形。

虚线的xy坐标轴

          //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));//虚线
        //绘制线   coo  坐标系原点
        recording.drawPath(getLinePath(coo), paint_xy);


    /**
     * 坐标系xy轴路径
     *
     * @param coo     坐标原点
     */
    static int centerx;
    public static Path getLinePath(Point coo) {
        Path path = new Path();
        centerx = coo.y;
        //x正半轴线
        path.moveTo(coo.x, coo.y);
        path.lineTo(coo.x + centerx, coo.y);
        //x负半轴线
        path.moveTo(coo.x, coo.y);
        path.lineTo(coo.x - centerx, 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;
    }

坐标轴上的距离刻度数和点

        //初始化坐标系文字
        Paint paint = new Paint();
        paint.setStrokeWidth(4);
        paint.setColor(Color.GREEN);
        paint.setTextSize(30);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);  

  /**
     * 坐标系绘制刻度和文字
     * @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);
    }

4个箭头符号,东南西北文字

//初始化坐标系文字和箭头画笔
        Paint paint = new Paint();
        paint.setStrokeWidth(4);
        paint.setColor(Color.GREEN);
        paint.setTextSize(30);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);

  // 右箭头
        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);

        //为坐标系绘制文字
       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);

 

虚线圆圈

  /**
     * 往集合添加坐标点 分为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) {

        }
    }

   /**
     * 绘制圆圈点位
     */
    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);
        }
    }


            resetCircleView();
               //画5个圈
            drawMap(canvas, circleMap);
            drawMap(canvas, circleMap2);
            drawMap(canvas, circleMap3);
            drawMap(canvas, circleMap4);
            drawMap(canvas, circleMap5);

目标点的虚线,以及文字距离显示

    //绘制线以及标注
    private void drawDataView(Canvas canvas, double length, double angle) {
        try {
             //文字
              mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
               mTextPaint.setColor(Color.GREEN);
              mTextPaint.setTextSize(30);
            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) {
        }
    }

    //已知角度和斜边,求点
    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;
    }

完整代码如下:

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;
    }
}

使用

扫描二维码关注公众号,回复: 8640155 查看本文章
    <test.com.view.RadarView
        android:id="@+id/radarView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layerType="software"
        />

结语

上面代码都是用的固定像素,所以在不同屏幕上显示效果可能不能很好适配,要适配的话可以在代码中添加修改。到这里主要的功能就结束了,为了方便理解写的比较乱,见谅见谅~,可以继续在上面修改添加新的功能,比如放大缩小等等,主要就是要了解安卓屏幕坐标以及绘制的基本方法,那么做起来就很顺手了。

发布了40 篇原创文章 · 获赞 70 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_35959554/article/details/102870138