Android跨不同分辨率屏幕Canvas同位置绘制及TCP坐标同步问题
这个问题我做跨屏绘图项目的时候踩过坑!核心原因就是绝对像素坐标没法在不同分辨率/比例的屏幕上通用,得把坐标转换成相对比例来传输,再在接收端根据自身屏幕参数还原。下面给你详细拆解解决方案:
解决方案:用相对比例替代绝对像素坐标
1. 发送端:将触摸/绘制坐标转为相对比例
首先,在发送数据之前,不要直接传触摸事件的event.x和event.y(这是当前屏幕的绝对像素),而是把它们转换成相对于当前屏幕可用绘制区域的比例值(范围0~1)。
比如在自定义绘图View里实现:
// 先获取当前View的可用绘制区域尺寸(避免状态栏、导航栏干扰) private var availableWidth = 0f private var availableHeight = 0f override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) availableWidth = w.toFloat() availableHeight = h.toFloat() } // 处理触摸事件时转换坐标 override fun onTouchEvent(event: MotionEvent): Boolean { when (event.action) { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> { // 计算相对比例 val relativeX = event.x / availableWidth val relativeY = event.y / availableHeight // 将比例值通过TCP发送(保留4位小数减少精度损失) val sendData = String.format("%.4f,%.4f", relativeX, relativeY) // 这里写你的TCP发送逻辑... return true } } return super.onTouchEvent(event) }
2. 接收端:根据自身屏幕参数还原绝对坐标
接收端拿到相对比例后,用自己的屏幕可用区域尺寸乘以比例值,就能得到适配当前屏幕的绝对坐标了。
同样在接收端的自定义绘图View里处理:
private var myAvailableWidth = 0f private var myAvailableHeight = 0f private var lastAdjustedX = 0f private var lastAdjustedY = 0f override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) myAvailableWidth = w.toFloat() myAvailableHeight = h.toFloat() } // 处理收到的TCP数据 private fun handleReceivedData(data: String) { // 解析相对坐标 val parts = data.split(",") if (parts.size != 2) return val relativeX = parts[0].toFloat() val relativeY = parts[1].toFloat() // 还原为当前屏幕的绝对坐标 val adjustedX = relativeX * myAvailableWidth val adjustedY = relativeY * myAvailableHeight // 用调整后的坐标绘制线条 canvas.drawLine(lastAdjustedX, lastAdjustedY, adjustedX, adjustedY, paint) // 更新上一个坐标,用于下一次绘制 lastAdjustedX = adjustedX lastAdjustedY = adjustedY invalidate() }
3. 进阶:处理横竖屏/不同宽高比的情况
如果发送端和接收端的屏幕宽高比差异很大(比如一个竖屏一个横屏),基础方案可能会导致拉伸变形。这时候可以额外发送发送端的屏幕宽高比,接收端做适配:
发送端额外传输宽高比
val aspectRatio = availableWidth / availableHeight val sendData = String.format("%.4f,%.4f,%.4f", relativeX, relativeY, aspectRatio)
接收端根据宽高比适配
private fun handleReceivedData(data: String) { val parts = data.split(",") if (parts.size != 3) return val relativeX = parts[0].toFloat() val relativeY = parts[1].toFloat() val senderAspectRatio = parts[2].toFloat() val receiverAspectRatio = myAvailableWidth / myAvailableHeight var adjustedX: Float var adjustedY: Float if (senderAspectRatio > receiverAspectRatio) { // 发送端屏幕更宽,接收端以高度为基准适配,避免纵向拉伸 val scale = myAvailableHeight adjustedX = relativeX * scale * senderAspectRatio adjustedY = relativeY * scale } else { // 接收端屏幕更宽,以宽度为基准适配,避免横向拉伸 val scale = myAvailableWidth adjustedX = relativeX * scale adjustedY = relativeY * scale / senderAspectRatio } // 后续绘制逻辑... }
关键注意点
- 排除系统UI干扰:一定要用View的实际可用绘制区域尺寸(
onSizeChanged里的w/h),而不是整个屏幕的分辨率,否则状态栏、导航栏会导致坐标偏移。 - 浮点精度控制:传输时保留3~4位小数足够,既减少数据量,又能保证绘制精度。
- 同步绘制状态:如果是连续绘制线条,记得同时传输“开始绘制”“结束绘制”的状态,或者连续传输坐标点,避免线条断裂。
内容的提问来源于stack exchange,提问作者Philip Yoon
相关产品推荐
相关产品推荐

