Android 如何每秒运行Runnable更新UI

Android 如何每秒运行Runnable更新UI,android,kotlin,Android,Kotlin,我试图在kotlin android中编写代码,以便每秒移动一幅图像,但我无法使其正常工作。现在我正在使用一个计时器每秒调度一个计时器任务,但它没有按预期工作 这是我的密码 class Actvt_Image<float> : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

我试图在kotlin android中编写代码,以便每秒移动一幅图像,但我无法使其正常工作。现在我正在使用一个
计时器
每秒调度一个
计时器任务
,但它没有按预期工作

这是我的密码

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)

        }
    }

}
class activt\u Image:appcompativity(){
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity\u actvt\u图像)
val pict_mario=findViewById(R.id.img_mario)
val bt_down=findviewbyd(R.id.bt_down)
val frame=findViewById(R.id.frame)
val txt1=findviewbyd(R.id.txt1)
变量i=100
val timer=timer()
val myTask=object:TimerTask(){
覆盖趣味跑(){
txt1.text=(i+1).toString()
旋转角度=180华氏度
img_Mario.translationX+=100
img_Mario.translationY+=20
}
}
bt_down.setOnClickListener{
i=0
时间表(myTask,10001000)
}
}
}

您试图在后台线程上更新UI,这是不可能的。UI只能在UI线程上更新。另外,使用
定时器
定时器任务
每1秒创建和销毁一个线程并不是使用线程的正确方法,因为创建线程是一个内存开销很大的操作

您需要做的是使用
处理程序
并告诉
UI线程
在每个所需的间隔后运行
Runnable
。移除
定时器
定时器任务
并使用以下命令

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)
            }
        })
上面的代码正在使用处理程序并将任务发布到UI线程消息队列。任务本身正在更新UI,并使用相同的处理程序将其自身再次发布到UI线程消息队列中,但这次使用
handler.postdayed()
method延迟1秒

编辑:如何停止可运行

如果要停止特定的
runnable
,可以使用以下方法并传入与传入
handler.post()
相同的
runnable
对象。当然,您必须始终保持对
runnable
的引用才能停止它。上面的代码没有保留引用。请参见下面的完整代码

handler.removeCallbacks(runnable) //stops a specific runnable
要停止UI线程消息队列中所有剩余的回调或
runnable
,请使用以下命令

handler.removeCallbacksAndMessages(null) //stops any pending callback in message queue
完整代码

注意:我添加了一个停止按钮click listener作为添加

class activt\u Image:appcompativity(){
私有lateinit变量处理程序:处理程序
private lateinit var runnable:runnable//对runnable对象的引用
私有变量i=0
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity\u actvt\u图像)
val pict_mario=findViewById(R.id.img_mario)
val bt_down=findviewbyd(R.id.bt_down)
val bt_stop=findviewbyd(R.id.bt_stop)
val frame=findViewById(R.id.frame)
val txt1=findviewbyd(R.id.txt1)
handler=handler(Looper.getMainLooper())
可运行的{
我++
txt1.text=i.toString()
旋转角度=180华氏度
img_Mario.translationX+=100
img_Mario.translationY+=20
handler.postDelayed(可运行,1000)
}
bt_down.setOnClickListener{
handler.post(可运行)
}
bt_stop.setOnClickListener{
//使用此选项停止所有回调
//handler.removeCallbacksAndMessages(null)
handler.removeCallbacks(可运行)
}
}
}
在此处阅读有关进程、线程和处理程序的更多信息:

我有一个代码,它按照我的预期运行

     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()
    }

定义“无法工作”尝试在您的ClickListener中调用timer.run(),它无法按预期移动它是否移动?你想如何移动?我想移动image_mario,但它不能。这也会起作用,但问题是你正在创建一个新线程,并要求该线程在UI线程上运行一段代码,而自己什么也不做。简言之,如果新线程没有做任何其他事情,那么就不需要创建新线程。相反,您可以使用UI线程本身来运行一段代码,方法是使用
handler.post()
,正如我在回答中提到的。您的代码是正确的。但我如何控制它何时可以运行。示例:当我单击一个按钮,然后它将运行时?@danghang只需在
bt\u down.setOnClickListener
之前初始化处理程序,然后将其余代码放入
bt\u down.setOnClickListener
中。我编辑了我的答案,看到了完整的代码。@DangHoang我的错,我忘了添加那个。检查我的最新答案。这次不是直接在
handler.post()方法中创建
runnable
对象。我一直将它作为单独的
runnable
对象进行引用,并使用它来启动和停止
runnable
@danghang我很高兴听到这个消息。如果此解决方案对您有效,请单击复选标记图标,将答案勾选为已接受。祝你好运
     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()
    }