我想为我的游戏创建一个简单的倒计时,当游戏开始时,我想每秒调用这个函数:

fun minusOneSecond(){
  if secondsLeft > 0{
     secondsLeft -= 1
     seconds_thegame.text = secondsLeft.toString()
  }
}

我试过这个:

var secondsLeft = 15

timer.scheduleAtFixedRate(
   object : TimerTask() {

      override fun run() {
         minusOneSecond()
      }

    },0, 1000
)   // 1000 Millisecond  = 1 second

But the app unfortunately stops, the 2nd time the run function is called

I just started with android development and Kotlin 3 weeks ago and so far I understand the most out of it.

With swift in Xcode I use this line and I thought something similar would work with Kotlin

setTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(minusOneSecond), userInfo: nil, repeats: true)

推荐答案

Problem: Timer类使用一个带有队列的后台线程来按顺序排队和执行所有任务.从您的代码中,因为您更新了UI(在minusOneSecond函数中更改TextView内容).这就是为什么应用程序会抛出以下异常并导致应用程序崩溃.

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Solution: There are many ways to achieve your task, but I prefer using post() and postDelayed() method from Handler class. Because it's simple and easy to understand.

val mainHandler = Handler(Looper.getMainLooper())

mainHandler.post(object : Runnable {
    override fun run() {
        minusOneSecond()
        mainHandler.postDelayed(this, 1000)
    }
})

Update: From author's comment about how to pause/resume the task from Handler. Here is an example.

class MainActivityKt : AppCompatActivity() {

    lateinit var mainHandler: Handler

    private val updateTextTask = object : Runnable {
        override fun run() {
            minusOneSecond()
            mainHandler.postDelayed(this, 1000)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Your logic code
        ...
        mainHandler = Handler(Looper.getMainLooper())
    }

    override fun onPause() {
        super.onPause()
        mainHandler.removeCallbacks(updateTextTask)
    }

    override fun onResume() {
        super.onResume()
        mainHandler.post(updateTextTask)
    }

    fun minusOneSecond() {
        if secondsLeft > 0 {
            secondsLeft -= 1
            seconds_thegame.text = secondsLeft.toString()
        }
    }
}

Kotlin相关问答推荐

查看流数据和改进的HTTP请求的模型

Gradle Jooq配置自定义生成器

Kotlin中用于调用常量名称的枚举类方法之间的区别

使用 Compose for Desktop Bundle 文件

我们应该在 Effect 和 Either 之间 Select 哪个作为我们业务服务的返回类型?

Kotlin 有垃圾收集器吗?如果是这样,它基于哪种算法?

如何有效地填充 Gradle Kotlin DSL 中的额外属性?

判断 AAR 元数据值时发现的一个或多个问题:

runBlocking 中的 deferred.await() 抛出的异常即使在被捕获后也被视为未处理

在 kotlin 中写入 parcer 可空值

IntentService (kotlin) 的默认构造函数

变量后的Android问号

Kotlin 创建snackbar

如何处理 Kotlin 中的异常?

取消信号上的kotlin流量采集

Kotlin suspend fun

Kotlin协程处理错误和实现

如何将 Kotlin 日期中的字符串或时间戳格式化为指定的首选格式?

Android插件2.2.0-alpha1无法使用Kotlin编译

kotlin中的全局可拓函数