You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:26:04