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.优化一下性能
最新发布