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

Android Kotlin游戏开发:如何让Dot视图匹配圆点实际尺寸?

解决Dot View全屏导致点击错误的问题

核心问题在于:你之前让Dot作为全屏View,在Canvas上绘制小圆点,导致任何屏幕点击都会触发最新添加的Dot的触摸事件;而修改onMeasure后,又错误地使用屏幕绝对坐标绘制圆点,导致圆点被限制在小View内部,无法正常显示位置。

正确的解决思路

  1. 让Dot的View尺寸严格匹配圆点大小(直径=60,对应半径30)
  2. 通过布局参数(LayoutParams)控制Dot在父布局中的位置,而非在Canvas中使用绝对坐标
  3. 圆点绘制在DotView的中心,这样View的位置就是圆点的实际位置,触摸范围精准对应圆点

修改后的代码实现

1. 重构Dot.kt

class Dot(context: Context, attrs: AttributeSet?, private var dotColor: Int) : View(context, attrs) {
    private var isMatching: Boolean = false
    private var dotIsPressed: Boolean = false
    private var isDestroyed: Boolean = false
    private lateinit var mHandler: Handler
    private lateinit var runnable: Runnable
    // 用布局参数控制位置
    private val layoutParams = ConstraintLayout.LayoutParams(
        ConstraintLayout.LayoutParams.WRAP_CONTENT,
        ConstraintLayout.LayoutParams.WRAP_CONTENT
    )

    init {
        this.isPressed = false
        this.isDestroyed = false
        mHandler = Handler()
        runnable = object : Runnable {
            override fun run() {
                moveDown()
                invalidate()
                if (!isDestroyed) {
                    mHandler.postDelayed(this, 20)
                }
            }
        }
        // 初始化位置:随机水平位置,顶部开始
        val random = Random()
        val screenWidth = context.resources.displayMetrics.widthPixels
        // 限制水平位置,避免圆点超出屏幕
        layoutParams.leftMargin = random.nextInt(screenWidth - 60)
        layoutParams.topMargin = 0
        this.layoutParams = layoutParams
        startFalling()
    }

    fun getDotColor() = dotColor

    fun isMatching() = isMatching

    fun setMatching(matching: Boolean) {
        this.isMatching = matching
    }

    fun dotIsPressed() = dotIsPressed

    override fun setPressed(pressed: Boolean) {
        this.dotIsPressed = pressed
    }

    fun isDestroyed() = isDestroyed

    fun setDestroyed(destroyed: Boolean) {
        this.isDestroyed = destroyed
        if (destroyed) {
            mHandler.removeCallbacks(runnable)
        }
    }

    fun moveDown() {
        // 更新topMargin实现下落
        layoutParams.topMargin += 10
        this.layoutParams = layoutParams
    }

    fun checkCollision(line: Line) {
        // 这里可以根据layoutParams.topMargin和line的位置判断碰撞
    }

    fun startFalling() {
        mHandler.post(runnable)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 设置View尺寸为圆点直径(60 = 30*2)
        setMeasuredDimension(60, 60)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if (!isDestroyed) {
            val paint = Paint().apply {
                color = dotColor
            }
            // 在View中心绘制圆点
            canvas?.drawCircle(width/2f, height/2f, 30f, paint)
        }
    }
}

2. 修改MainActivity.kt中的createAndAddDot方法

fun createAndAddDot(color: Int) {
    Log.d("Dot", "createAndAddDot called")
    val dot = Dot(this, null, color)
    val layout = findViewById<ConstraintLayout>(R.id.layout)
    layout.addView(dot)
    dots.add(dot)

    dot.setOnTouchListener { view, event ->
        if (event.action == MotionEvent.ACTION_DOWN) {
            val dotToRemove = dots.find { it == view }
            dotToRemove?.let {
                it.setDestroyed(true)
                layout.removeView(it)
                dots.remove(it)
                updateScore(1)
                view.performClick()
            }
        }
        true
    }
}

同时修改创建Dot的调用处:

// 原来的代码
createAndAddDot(0, 0, dotColor)
// 修改为
createAndAddDot(dotColor)

关键修改点说明

  • View尺寸控制:通过onMeasure固定View为60x60,完全匹配圆点大小,触摸事件只会在这个范围内触发
  • 位置控制:用ConstraintLayout.LayoutParams的leftMargin和topMargin控制Dot在屏幕中的位置,替代原来的绝对坐标xPos、yPos
  • 绘制逻辑:在View中心绘制圆点,确保圆点始终显示在View范围内,View的位置就是圆点的实际位置
  • 内存优化:销毁Dot时移除Handler的回调,避免内存泄漏

内容的提问来源于stack exchange,提问作者Spencer S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 15:50:32