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

Android Kotlin:从MainActivity调用invalidate()未触发onDraw()问题

解决Android Kotlin中Activity触摸调用View方法后onDraw不触发的问题

我来帮你排查这个问题!你遇到的情况其实挺常见的——把触摸逻辑从View移到Activity后,明明调用了更新坐标的方法和invalidate(),但onDraw()就是不执行,大概率是这几个核心原因,咱们一步步来解决:


1. 最常见的坑:Activity中引用的View实例不对

这是最容易犯的错误:你在Activity里获取的CustomView实例,和布局中实际显示的不是同一个!比如你可能自己new了一个CustomView但没加到布局里,或者findViewById找错了id,导致调用invalidate()的是个“无效”的实例。

解决方式:
确保在Activity的onCreate里正确绑定布局中的View:

class MainActivity : AppCompatActivity() {
    private lateinit var customView: YourCustomView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 这里的id必须和布局文件中你的CustomView的id一致
        customView = findViewById(R.id.your_custom_view_id)
    }

    // 你的onTouchEvent逻辑
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                // 先确认这里调用的是上面初始化的customView实例
                customView.updateCirclePos(event.x, event.y)
                return true
            }
        }
        return super.onTouchEvent(event)
    }
}

2. 检查CustomView的方法是否正确更新成员变量并调用invalidate

要确保你的CustomView中,更新坐标的方法确实修改了类的成员变量(不是局部变量),并且调用的是View自身的invalidate():

class YourCustomView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
    // 用成员变量保存圆形坐标,onDraw时会用到
    private var circleX = 0f
    private var circleY = 0f
    private val circlePaint = Paint().apply {
        color = Color.RED
        style = Paint.Style.FILL
    }

    // 对外提供的更新方法
    fun updateCirclePos(x: Float, y: Float) {
        // 一定要修改成员变量!
        this.circleX = x
        this.circleY = y
        // 调用View的invalidate(),触发重绘
        invalidate()
        // 加个日志确认方法被调用,同时查看View的状态
        Log.d("CustomView", "updateCirclePos called, pos: ($circleX, $circleY), isVisible: $isVisible")
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 加日志确认onDraw是否执行
        Log.d("CustomView", "onDraw triggered")
        canvas.drawCircle(circleX, circleY, 50f, circlePaint)
    }
}

3. 注意坐标转换的坑(虽然不影响onDraw触发,但会导致画错位置)

Activity的onTouchEvent中获取的event.x/event.y是相对于Activity的ContentView的,而View的onDraw用的是自身的局部坐标。如果你的View在布局中有margin、padding或者嵌套在其他容器里,直接传这个坐标会导致圆形位置不对,但如果只是onDraw不触发,这个不是核心原因,但也要注意:

// 如果需要转换坐标,在Activity的onTouchEvent里这样处理
val location = IntArray(2)
customView.getLocationOnScreen(location)
// 计算相对于CustomView的局部坐标
val viewLocalX = event.rawX - location[0]
val viewLocalY = event.rawY - location[1]
customView.updateCirclePos(viewLocalX, viewLocalY)

4. 检查是否在非UI线程调用了updateCirclePos

invalidate()必须在UI线程调用,如果你的updateCirclePos是在非UI线程被调用的,那invalidate()不会生效。这种情况下可以换成postInvalidate(),它可以在非UI线程触发重绘:

fun updateCirclePos(x: Float, y: Float) {
    this.circleX = x
    this.circleY = y
    // 非UI线程用这个代替invalidate()
    postInvalidate()
}

5. 排查View的可见性和触摸拦截

  • 如果你的CustomView的visibility被设置为GONE或INVISIBLE,invalidate()也不会触发onDraw(),可以检查布局或代码中有没有误设置。
  • 如果父布局的onTouchEvent返回了true,会拦截触摸事件,导致Activity的onTouchEvent不被触发,但你说方法已经被调用了,这个可能性较低,但也可以排查下。

按照上面的步骤排查,应该就能解决invalidate()不触发onDraw()的问题啦!

内容的提问来源于stack exchange,提问作者Lord Imperator

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:03:57