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