You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Kotlin+JavaFX:点击Move时取消运行中协程并启动新协程

解决JavaFX中飞船移动协程重复运行的问题

这个问题的核心在于你当前把moveJob定义成了局部变量——每次点击「move」时都会创建一个新的moveJob,完全无法访问到之前启动的协程,自然也就没法取消它。要解决这个问题,我们只需要把moveJob改成类的成员属性,这样每次点击时都能拿到之前的协程实例并取消它。

具体步骤

  1. moveJob声明为Ship类的成员变量
    moveJob从局部变量移到Ship类的成员位置,类型设为Job?(可空,因为初始状态下没有运行的协程)。

  2. 每次点击「move」时先取消旧协程
    在启动新的移动协程前,先检查moveJob是否存在且处于活跃状态,如果是就调用cancel()取消它,然后再启动新协程并更新moveJob的引用。

  3. move()函数中处理协程取消
    因为Kotlin协程是协作式取消的,所以你需要在move()的循环逻辑中加入取消检查(比如用isActive属性),确保协程被取消后能立刻停止更新飞船坐标。

修改后的代码示例

// 在Ship类中添加成员变量
private var moveJob: Job? = null

// 你的下拉菜单「move」点击事件处理逻辑
item1.setOnAction { 
    println("Something...") 
    root.onMouseClicked = EventHandler { event -> 
        launch { 
            println("Clicked on an object.") 
        } 
        event.consume()
        root.onMouseClicked = null 
        scene.onMouseClicked = null 
    } 

    scene.onMouseClicked = EventHandler { event -> 
        // 先取消已有的移动协程
        moveJob?.takeIf { it.isActive }?.cancel()
        // 启动新的移动协程并更新moveJob引用
        moveJob = launch { 
            move(event, root) 
        }
        root.onMouseClicked = null 
        scene.onMouseClicked = null 
    } 
}

// 示例move()函数,加入取消检查
private suspend fun move(event: MouseEvent, root: Parent) {
    val targetX = event.x
    val targetY = event.y
    // 假设这是你的移动循环逻辑
    while (isActive && !reachedTarget(targetX, targetY)) {
        // 更新飞船坐标的逻辑
        updateShipPosition(targetX, targetY)
        // 让协程挂起一下,也能触发取消检查
        delay(16) // 大约60帧每秒
    }
}

// 辅助函数:判断是否到达目标点
private fun reachedTarget(targetX: Double, targetY: Double): Boolean {
    // 实现你的距离判断逻辑,比如坐标差小于某个阈值
    return Math.abs(shipX - targetX) < 1 && Math.abs(shipY - targetY) < 1
}

关键细节解释

  • moveJob?.takeIf { it.isActive }?.cancel():这行代码会先检查moveJob是否存在,并且是否处于活跃状态(还在运行),只有满足条件才会调用cancel(),避免对已经完成的协程做无效操作。
  • 协程的协作式取消isActive是协程作用域内的属性,用来判断当前协程是否还处于活跃状态。在循环中加入这个检查,能确保协程被取消后立刻停止执行,不会继续更新飞船坐标。
  • 成员变量的作用:把moveJob变成类成员后,每次点击「move」时都能访问到上一次启动的协程实例,从而实现“取消旧任务,启动新任务”的逻辑。

这样修改后,每次用户点击「move」时,旧的移动协程会被立即取消,只有新的协程在运行,飞船就不会出现行为异常的问题了。

内容的提问来源于stack exchange,提问作者Ricardo DM

火山引擎 最新活动