ortech
ortech

Reputation: 1

Beginner problem with Kotlin and Timer / TimerTask with variable timePeriod - mainActivity

I am Alois from Austria. I started with Kotlin to program apps on smartphones 3 days ago, so nearly no glue. I just know vb.net as the latest programming language I started 15 years ago. So hope my english is good enough to explain my problem in detail.

As my first app I wanted to realize an idea from my son. It should become a small game. On the screen you can see 3 buttons (SPEED, UPGRADE and INCOME) on the bottom, in the middle top there is a textView (mytxt) and in the middle a imageView.

Every specific timeperiod (starting with 1s) you earn money, so every second you get 1 piece. This I wanted to program withe a timer/timertask. When I press on the SPEED button, the timeperiod shall get less (in steps of 100ms). But this is my first difficulty, the program crashes without error output by pressing this button.

Please could somebody theck my code an help me with this?

Thanks a lot in advance. Alois

package com.example.uyc_android
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import java.util.Timer
import java.util.TimerTask


var money=0
var timerperiod: Long= 1000
var computerType=1
var income =1


class MainActivity : AppCompatActivity() {

    private lateinit var timer: Timer
    private lateinit var timerTask: TimerTask
    private var counter = 0

    @SuppressLint("SetTextI18n")
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val mytxt = findViewById<TextView>(R.id.txtMoney)
        val btnSpeed = findViewById<Button>(R.id.btnSpeed)
        val btnUpgrade = findViewById<Button>(R.id.btnUpgrade)
        val btnIncome = findViewById<Button>(R.id.btnIncome)
        val bild = findViewById<ImageView>(R.id.imagePC)

        timer = Timer()

        var timerTask = object : TimerTask() {
            override fun run() {
                counter++
                // Aktualisiere den Wert auf der Benutzeroberfläche im UI-Thread
                runOnUiThread {
                    mytxt.text = counter.toString()
                }
            }
        }
        timer.schedule(timerTask, 0, timerperiod)

        btnSpeed.setOnClickListener {
            timerperiod -= 100L
            if (timerperiod <= 100L) {
                timerperiod = 100L
            }

            timerTask.cancel()
            timer.schedule(timerTask, 0, timerperiod)

            runOnUiThread { btnSpeed.text = "SPEED ${timerperiod}" }
        }
            btnUpgrade.setOnClickListener {
                computerType++
                if (computerType >= 3) {
                    computerType = 3
                }
                if (computerType == 1) {
                    runOnUiThread { bild.setTag(R.drawable.pc1) }
                }
                if (computerType == 2) {
                    runOnUiThread { bild.setTag(R.drawable.pc2) }
                }
                if (computerType == 3) {
                    runOnUiThread { bild.setTag(R.drawable.pc3) }
                }

                runOnUiThread { btnUpgrade.text = "UPGRADE ${computerType}" }
            }


            btnIncome.setOnClickListener {
                income++
                runOnUiThread { btnIncome.text = "INCOME ${income}" }
            }




    }


}

I searched a lot in www. I watched many tutorials but no result. Maybe somebody can change my code so it works.

Upvotes: -1

Views: 880

Answers (1)

Tenfour04
Tenfour04

Reputation: 93852

You cannot reuse TimerTask instances, so you must create a new instance each time. Since it is only used inside onCreate(), you can define a creator function inside onCreate().

By the way, all your uses of runOnUiThread { } are unnecessary because timer and button listener callbacks are already always called on the UI thread.

fun createTimerTask() = object : TimerTask() {
    override fun run() {
        counter++
        mytxt.text = counter.toString()
    }
}
var timerTask = createTimerTask()
timer.schedule(timerTask, 0, timerperiod)

btnSpeed.setOnClickListener {
    timerperiod -= 100L
    if (timerperiod <= 100L) {
        timerperiod = 100L
    }

    timerTask.cancel()
    timerTask = createTimerTask()  // <---------------
    timer.schedule(timerTask, 0, timerperiod)

    btnSpeed.text = "SPEED ${timerperiod}"
}

By the way, this:

timerperiod -= 100L
if (timerperiod <= 100L) {
    timerperiod = 100L
}

can be simplified to:

timerPeriod = max(timerPeriod - 100L, 100L)

And your upgrade button listener code can be simplified to:

btnUpgrade.setOnClickListener {
    computerType = min(computerType + 1, 3)
    btnUpgrade.text = "UPGRADE ${computerType}"
    val tag = when (computerType) {
        1 -> R.drawable.pc1
        2 -> R.drawable.pc2
        3 -> R.drawable.pc3
        else -> return
    }
    bild.setTag(tag)
}

Upvotes: 0

Related Questions