Как запускать Runnable каждую секунду для обновления пользовательского интерфейса - PullRequest
2 голосов
/ 06 июля 2019

Я пытаюсь закодировать в android kotlin, чтобы перемещать изображение каждую секунду, но я не могу заставить его работать. Прямо сейчас я использую Timer для планирования Timer Task каждую секунду, но он не работает, как ожидалось.

Вот мой код

class Actvt_Image<float> : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_actvt__image)

        val pict_mario = findViewById<ImageView>(R.id.img_Mario)
        val bt_down = findViewById<Button>(R.id.bt_down)
        val frame = findViewById<LinearLayout>(R.id.frame)
        val txt1=findViewById<TextView>(R.id.txt1)

        var i =100
        val timer = Timer()
        val myTask = object : TimerTask() {
            override fun run() {

                txt1.text = (i+1).toString()
                img_Mario.rotation=180f
                img_Mario.translationX +=100
                img_Mario.translationY +=20
            }
        }

        bt_down.setOnClickListener {

            i=0
            timer.schedule(myTask, 1000, 1000)

        }
    }

}

Ответы [ 2 ]

0 голосов
/ 06 июля 2019

У меня есть один код, и он работает, как я ожидал

     val t = object : Thread() {
        override fun run() {
            while (!isInterrupted) {
                try {
                    Thread.sleep(1000)  //1000ms = 1 sec
                    runOnUiThread {
                        i++

                        txt1.text = i.toString()
                        img_Mario.rotation=180f
                        img_Mario.translationX +=20

                    }

                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }

            }
        }
    }

    bt_down.setOnClickListener {
        i=0
        t.start()
    }
0 голосов
/ 06 июля 2019

Вы пытаетесь обновить пользовательский интерфейс в фоновом потоке, что невозможно. Пользовательский интерфейс может быть обновлен только в потоке пользовательского интерфейса. Кроме того, использование Timer и TimerTask для создания и уничтожения потока каждую 1 секунду не является правильным способом использования потоков, поскольку создание потока является дорогостоящей операцией с памятью.

Вам нужно использовать Handler и сказать UI Thread, что нужно запускать Runnable после каждого требуемого интервала. Удалите Timer и TimerTask и используйте следующее

val handler = Handler(Looper.getMainLooper())
handler.post(object : Runnable {
            override fun run() {
                txt1.text = (i+1).toString()
                img_Mario.rotation=180f
                img_Mario.translationX +=100
                img_Mario.translationY +=20

                handler.postDelayed(this, 1000)
            }
        })

Приведенный выше код использует обработчик и отправляет задачу в очередь сообщений потока пользовательского интерфейса. Сама задача обновляет пользовательский интерфейс и снова отправляет себя в очередь сообщений потока пользовательского интерфейса, используя тот же обработчик, но на этот раз с задержкой в ​​1 секунду, используя handler.postDelayed() methond

РЕДАКТИРОВАТЬ: Как остановить работоспособный

Если вы хотите остановить определенный runnable, вы можете использовать следующий метод и передать тот же runnable объект, который вы передали в handler.post(). Конечно, вы должны всегда сохранять ссылку на runnable, чтобы остановить его. Приведенный выше код не сохраняет ссылку. См. Полный код ниже.

handler.removeCallbacks(runnable) //stops a specific runnable

Чтобы остановить все оставшиеся обратные вызовы или runnable из очереди сообщений потока пользовательского интерфейса, используйте эту

handler.removeCallbacksAndMessages(null) //stops any pending callback in message queue

Полный код

ПРИМЕЧАНИЕ: я добавил слушатель щелчка кнопки остановки как дополнение

class Actvt_Image<float> : AppCompatActivity() {

        private lateinit var handler : Handler
        private lateinit var runnable : Runnable // reference to the runnable object
        private var i = 0

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_actvt__image)

            val pict_mario = findViewById<ImageView>(R.id.img_Mario)
            val bt_down = findViewById<Button>(R.id.bt_down)
            val bt_stop = findViewById<Button>(R.id.bt_stop)
            val frame = findViewById<LinearLayout>(R.id.frame)
            val txt1=findViewById<TextView>(R.id.txt1)

            handler = Handler(Looper.getMainLooper())
            runnable = Runnable {
                i++
                txt1.text = i.toString()
                img_Mario.rotation=180f
                img_Mario.translationX +=100
                img_Mario.translationY +=20
                handler.postDelayed(runnable, 1000)
            }

            bt_down.setOnClickListener {

                handler.post(runnable)

            }

            bt_stop.setOnClickListener {
                //Use this to stop all callbacks
                //handler.removeCallbacksAndMessages(null)

                handler.removeCallbacks(runnable) 

            }
        }

    }

Подробнее о процессах, потоках и обработчике читайте здесь: https://developer.android.com/guide/components/processes-and-threads https://developer.android.com/reference/android/os/Handler

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