Issue
I want to create a simple countdown for my game, when the game starts I want this function to be called every second:
fun minusOneSecond(){
if secondsLeft > 0{
secondsLeft -= 1
seconds_thegame.text = secondsLeft.toString()
}
}
I tried this:
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)
Solution
Problem: Timer class uses a background thread with a queue to queue and execute all tasks sequentially. From your code, because you update UI (changing TextView content in minusOneSecond
function). That why the app throws the following exception and make your app crash.
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()
}
}
}
Answered By - Son Truong
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.