Kotlin: вызывать функцию каждую секунду - PullRequest
4 голосов
/ 08 апреля 2019

Я хочу создать простой отсчет времени для своей игры, чтобы при запуске игры я хотел, чтобы эта функция вызывалась каждую секунду:

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

Но приложение, к сожалению, останавливается, во второй раз вызывается функция запуска

Я только начал разработку Android и Kotlin 3 недели назад, и до сих пор я понимаю, что из этого лучше всего.

С помощью SWIFT в XCode я использую эту строку, и я думал, что нечто подобное будет работать с Kotlin

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

Ответы [ 3 ]

6 голосов
/ 08 апреля 2019

Проблема: Таймер Класс использует фоновый поток с очередью для постановки в очередь и последовательного выполнения всех задач. Из вашего кода, потому что вы обновляете пользовательский интерфейс (изменяя содержимое TextView в функции minusOneSecond). Поэтому приложение выдает следующее исключение и приводит к сбою приложения.

android.view.ViewRootImpl $ CalledFromWrongThreadException: только оригинальный поток, создавший иерархию представлений, может касаться его представлений.

Решение: Есть много способов выполнить вашу задачу, но я предпочитаю использовать post () и postDelayed () метод из Handler класс. Потому что это просто и легко понять.

val mainHandler = Handler(Looper.getMainLooper())

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

Обновление: Из комментария автора о том, как приостановить / возобновить задачу из обработчика. Вот пример.

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()
        }
    }
}
2 голосов
/ 08 апреля 2019

Я использую этот код для обновления часов каждую минуту

 fixedRateTimer("timer", false, 0L, 60 * 1000) {
     this@FullscreenActivity.runOnUiThread {
         tvTime.text = SimpleDateFormat("dd MMM - HH:mm", Locale.US).format(Date())
     }
 }

, поэтому вам нужно запустить его с параметром 1000 вместо 60*1000

0 голосов
/ 08 апреля 2019

пожалуйста, используйте

inline fun Timer.schedule(
    time: Date, 
    period: Long, 
    crossinline action: TimerTask.() -> Unit
): TimerTask

ссылка: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.-timer/schedule.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...