dp=(160/dpi) * px dp(虚拟像素)

         
public class MapCanvasView extends View { private final String TAG = "MapCanvasView"; // 绘图工具 private Paint gridPaint, wscPaint, recordPaint, linePaint, textPaint, unloadPaint; private Path gridPath = new Path(); // 坐标系管理 private float viewCenterX, viewCenterY; // 视图中心物理坐标 private final PointF wscPoint = new PointF(); // WSC逻辑坐标 private final Matrix transformMatrix = new Matrix(); // 视图变换矩阵 private final Matrix inverseMatrix = new Matrix(); // 逆矩阵用于坐标转换 // 手势检测 private final ScaleGestureDetector scaleDetector; private float lastTouchX, lastTouchY; private static final int INVALID_POINTER_ID = -1; private int activePointerId = INVALID_POINTER_ID; // 网格配置 private final float gridSize = 50f; // 网格间距(逻辑单位) private final float gridLineWidth = 1f; // 网格线宽 private final RectF visibleRect = new RectF(); // 当前可见区域 private boolean gridDirty = true; // 网格是否需要重新生成 // 点管理 private final ArrayList<PointF> recordedPoints = new ArrayList<>(); // 记录点集合 private PointF unloadPoint = null; // 卸球点(新增) private boolean isAreaClosed = false; private int pointCounter = 1; public MapCanvasView(Context context, AttributeSet attrs) { super(context, attrs); init(); // 初始化缩放检测器 scaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } /** 初始化绘图工具 */ private void init() { // 网格画笔 gridPaint = createPaint(Color.LTGRAY, Paint.Style.STROKE, gridLineWidth); // WSC点画笔(黄色) wscPaint = createPaint(Color.parseColor("#FEC500"), Paint.Style.FILL, 0); // 记录点画笔(红色) recordPaint = createPaint(Color.RED, Paint.Style.FILL, 0); // 连线画笔(蓝色) linePaint = createPaint(Color.BLUE, Paint.Style.STROKE, 3f); // 文本画笔(白色) textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setTextSize(24); textPaint.setTextAlign(Paint.Align.CENTER); // 卸球点画笔(绿色,新增) unloadPaint = createPaint(Color.GREEN, Paint.Style.FILL, 0); } /** 创建标准化画笔 */ private Paint createPaint(int color, Paint.Style style, float strokeWidth) { Paint paint = new Paint(); paint.setColor(color); paint.setStyle(style); if (style == Paint.Style.STROKE) { paint.setStrokeWidth(strokeWidth); } return paint; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); viewCenterX = w / 2f; viewCenterY = h / 2f; wscPoint.set(0, 0); // 初始WSC在原点 resetTransformation(); calculateVisibleRect(); generateGrid(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.concat(transformMatrix); // 应用当前变换 calculateVisibleRect(); if (gridDirty) generateGrid(); // 绘制顺序:网格 → 闭合区域 → 连线 → 点 → 文本 canvas.drawPath(gridPath, gridPaint); drawClosedArea(canvas); drawConnectingLines(canvas); drawRecordedPoints(canvas); drawUnloadPoint(canvas); // 新增卸球点绘制 drawWscPoint(canvas); } // ================= 点绘制方法(修改后) ================= // /** 绘制WSC点(黄色)- 固定大小 */ private void drawWscPoint(Canvas canvas) { // 保存当前画布状态 canvas.save(); // 重置画布矩阵(移除之前的缩放平移效果) canvas.setMatrix(new Matrix()); // 将逻辑坐标转换为屏幕坐标 float[] screenPoint = {wscPoint.x, wscPoint.y}; transformMatrix.mapPoints(screenPoint); // 使用固定半径(8像素)绘制 canvas.drawCircle(screenPoint[0], screenPoint[1], 20, wscPaint); // 恢复画布状态 canvas.restore(); } /** 绘制卸球点(绿色)- 固定大小 */ private void drawUnloadPoint(Canvas canvas) { if (unloadPoint != null) { canvas.save(); canvas.setMatrix(new Matrix()); float[] screenPoint = {unloadPoint.x, unloadPoint.y}; transformMatrix.mapPoints(screenPoint); canvas.drawCircle(screenPoint[0], screenPoint[1], 20, unloadPaint); canvas.restore(); } } /** 绘制记录点(红色)及文本 - 固定大小 */ private void drawRecordedPoints(Canvas canvas) { if (recordedPoints.isEmpty()) return; canvas.save(); canvas.setMatrix(new Matrix()); for (int i = 0; i < recordedPoints.size(); i++) { PointF p = recordedPoints.get(i); float[] screenPoint = {p.x, p.y}; transformMatrix.mapPoints(screenPoint); // 固定半径8像素 canvas.drawCircle(screenPoint[0], screenPoint[1], 20, recordPaint); // 文本位置微调(固定偏移12像素) canvas.drawText(String.valueOf(i + 1), screenPoint[0], screenPoint[1] + 12, textPaint); } canvas.restore(); } /** 绘制闭合区域(半透明绿色) */ private void drawClosedArea(Canvas canvas) { if (isAreaClosed && recordedPoints.size() > 2) { Path path = createAreaPath(); Paint fillPaint = new Paint(); fillPaint.setColor(Color.argb(80, 0, 255, 0)); fillPaint.setStyle(Paint.Style.FILL); canvas.drawPath(path, fillPaint); } } /** 绘制连线(蓝色) */ private void drawConnectingLines(Canvas canvas) { if (recordedPoints.size() > 1) { canvas.drawPath(createAreaPath(), linePaint); } } /** 创建区域路径 */ private Path createAreaPath() { Path path = new Path(); PointF first = recordedPoints.get(0); path.moveTo(first.x, first.y); for (int i = 1; i < recordedPoints.size(); i++) { path.lineTo(recordedPoints.get(i).x, recordedPoints.get(i).y); } if (isAreaClosed && recordedPoints.size() > 2) { path.lineTo(first.x, first.y); } return path; } // ================= 手势处理 ================= // @Override public boolean onTouchEvent(MotionEvent event) { scaleDetector.onTouchEvent(event); final int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: handleActionDown(event); break; case MotionEvent.ACTION_MOVE: if (!scaleDetector.isInProgress()) { handleActionMove(event); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: activePointerId = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: handlePointerUp(event); break; } return true; } private void handleActionDown(MotionEvent event) { int pointerIndex = event.getActionIndex(); lastTouchX = event.getX(pointerIndex); lastTouchY = event.getY(pointerIndex); activePointerId = event.getPointerId(pointerIndex); } private void handleActionMove(MotionEvent event) { int pointerIndex = event.findPointerIndex(activePointerId); if (pointerIndex != -1) { float x = event.getX(pointerIndex); float y = event.getY(pointerIndex); float dx = x - lastTouchX; float dy = y - lastTouchY; transformMatrix.postTranslate(dx, dy); transformMatrix.invert(inverseMatrix); lastTouchX = x; lastTouchY = y; gridDirty = true; invalidate(); } } private void handlePointerUp(MotionEvent event) { int pointerIndex = event.getActionIndex(); int pointerId = event.getPointerId(pointerIndex); if (pointerId == activePointerId) { int newPointerIndex = pointerIndex == 0 ? 1 : 0; lastTouchX = event.getX(newPointerIndex); lastTouchY = event.getY(newPointerIndex); activePointerId = event.getPointerId(newPointerIndex); } } /** 缩放监听器 */ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); float currentScale = getMatrixScale(transformMatrix); float newScale = currentScale * scaleFactor; // 限制缩放范围 [0.5, 5] if (newScale < 0.5f) scaleFactor = 0.5f / currentScale; if (newScale > 5f) scaleFactor = 5f / currentScale; transformMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); transformMatrix.invert(inverseMatrix); gridDirty = true; invalidate(); return true; } } // ================= 坐标转换和网格 ================= // /** 计算当前可见区域(逻辑坐标) */ private void calculateVisibleRect() { float[] points = {0, 0, getWidth(), 0, getWidth(), getHeight(), 0, getHeight()}; inverseMatrix.mapPoints(points); float left = Float.MAX_VALUE; float top = Float.MAX_VALUE; float right = Float.MIN_VALUE; float bottom = Float.MIN_VALUE; for (int i = 0; i < points.length; i += 2) { float x = points[i], y = points[i + 1]; if (x < left) left = x; if (x > right) right = x; if (y < top) top = y; if (y > bottom) bottom = y; } visibleRect.set(left - 50, top - 50, right + 50, bottom + 50); } /** 生成网格路径 */ private void generateGrid() { gridPath.reset(); float startX = (float) Math.floor(visibleRect.left / gridSize) * gridSize; float endX = (float) Math.ceil(visibleRect.right / gridSize) * gridSize; float startY = (float) Math.floor(visibleRect.top / gridSize) * gridSize; float endY = (float) Math.ceil(visibleRect.bottom / gridSize) * gridSize; // 横向网格线 for (float y = startY; y <= endY; y += gridSize) { gridPath.moveTo(startX, y); gridPath.lineTo(endX, y); } // 纵向网格线 for (float x = startX; x <= endX; x += gridSize) { gridPath.moveTo(x, startY); gridPath.lineTo(x, endY); } gridDirty = false; } /** 重置视图变换 */ private void resetTransformation() { transformMatrix.reset(); transformMatrix.postTranslate(viewCenterX, viewCenterY); inverseMatrix.reset(); transformMatrix.invert(inverseMatrix); } /** 获取矩阵缩放因子 */ private float getMatrixScale(Matrix matrix) { float[] values = new float[9]; matrix.getValues(values); return values[Matrix.MSCALE_X]; } // ================= 公开方法 ================= // /** 设置WSC点位置 */ public void moveWscPoint(float x, float y) { wscPoint.set(x, y); invalidate(); } /** 记录当前WSC位置 */ public void recordCurrentPoint(float x, float y) { recordedPoints.add(new PointF(x, y)); isAreaClosed = false; pointCounter++; invalidate(); } /** 设置卸球点位置(新增) */ public void setUnloadPoint(float x, float y) { if (unloadPoint == null) { unloadPoint = new PointF(x, y); } else { unloadPoint.set(x, y); } invalidate(); } /** 清除卸球点(新增) */ public void clearUnloadPoint() { unloadPoint = null; invalidate(); } /** 闭合区域 */ public void closeArea() { if (recordedPoints.size() > 2) { isAreaClosed = true; invalidate(); } } /** 清除所有记录点 */ public void clearRecordedPoints() { recordedPoints.clear(); isAreaClosed = false; pointCounter = 1; invalidate(); } /** 定位到指定坐标 */ public void centerOnCoordinate(float x, float y) { float[] targetScreen = {x, y}; transformMatrix.mapPoints(targetScreen); // 转换到屏幕坐标 float dx = viewCenterX - targetScreen[0]; float dy = viewCenterY - targetScreen[1]; transformMatrix.postTranslate(dx, dy); transformMatrix.invert(inverseMatrix); gridDirty = true; invalidate(); } /** 获取记录点列表 */ public ArrayList<PointF> getPointList() { return recordedPoints; } /** 设置点列表 */ public void setPointList(List<Coord> data, boolean closed) { recordedPoints.clear(); for (Coord coord : data) { recordedPoints.add(new PointF((float) coord.x, (float) coord.y)); } isAreaClosed = closed; pointCounter = recordedPoints.size(); invalidate(); } public void setPointList(List<Coord> data) { setPointList(data, false); } } 这是我的一个安卓java,电子围栏控件,我现在发现1.大屏幕的比例不够,我应该怎么增加 2.放大和缩小屏幕时点的大小应该是不会改变的,只是距离变化。3.优化一下性能
最新发布
11-27
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值