簡體   English   中英

按下按鈕時如何執行任何代碼(kotlin,xml)

[英]How to execute any code while the button is pressed (kotlin, xml)

我有一個按鈕和 imageView,我需要在按下按鈕時執行一些更改圖像屬性的代碼。 但我現在不知道如何實現它。 我嘗試通過執行代碼 while(event?.action.= MotionEvent,ACTION_UP) 來使用 onTouchListener。 但它會導致應用程序掛起。

您不能只在主線程中執行無限循環,它會掛起您的應用程序。 但是可以執行不會阻塞主線程的異步代碼。 Kotlin 協程可以幫助做到這一點。

如果您在項目中使用 kotlin 協程,則可以在ACTION_DOWN事件上啟動具有無限循環的新協程。 並在ACTION_UP事件上取消相應的作業:

var job: Job? = null
button.setOnTouchListener { v, event ->
    when (event.action) {
        ACTION_DOWN -> {
            job = launch {
                while (true) {
                    // increasing counter as an example:
                    textView.text = "${counter++}"
                    delay(100)
                }
            }
        }
        ACTION_UP -> {
            job?.cancel()
        }
    }
    false
}

您希望在收到ACTION_DOWN事件(即用戶按下您的View )時開始您的任務(無論它是什么),並在收到ACTION_UP事件(用戶抬起手指或其他任何東西)或ACTION_CANCEL (例如用戶將手指拖到View之外)。

這會給你當按鈕被按住時的行為。 但是該任務需要異步運行 - 協程、線程、延遲的Runnable發布到主循環器(您可以通過調用其中一個post方法通過View來執行此操作)。

你不能只是循環,系統不能做任何其他事情(包括顯示 UI 更改和響應觸摸),直到你的代碼完成運行。 如果你在阻塞線程的同時等待一個ACTION_UP ,你就不會得到一個。 (無論如何,新的MotionEvent將通過稍后的 function 調用來實現。)

下面是一個使用 looper 的簡單示例:

class MainFragment : Fragment(R.layout.fragment_main) {

    lateinit var binding: FragmentMainBinding

    // This is a reusable Runnable that changes a background, then reposts itself
    // to the task queue to run again in the future.
    private val colourCycleTask = object : Runnable {
        private fun rnd() = (0..255).random()
        
        override fun run() {
            binding.someView.setBackgroundColor(Color.rgb(rnd(), rnd(), rnd()))
            binding.someView.postDelayed(this, 250L)
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentMainBinding.bind(view)
        binding.button.addHoldListener()
    }

    private fun View.addHoldListener() {
        setOnTouchListener { view, event ->
            var handled = true
            when(event.action) {
                MotionEvent.ACTION_DOWN -> view.post(colourCycleTask) // run the task
                MotionEvent.ACTION_UP -> {
                    view.removeCallbacks(colourCycleTask) // remove the task from the queue
                    view.performClick()
                }
                MotionEvent.ACTION_CANCEL -> view.removeCallbacks(colourCycleTask)
                else -> handled = false
            }
            handled
        }
    }
    
}

Runnable發布到主Looper基本上是將您的一些代碼添加到任務隊列中 - 因此您不會阻塞線程並防止其他任何事情發生,您是在對系統說“嘿,此時執行此操作請”,它會盡力趕上那個時間。 而且因為Runnable在最后重新發布了自己,所以你會在允許其他代碼運行的同時獲得這種循環行為,因為你沒有控制執行。 您只是推遲了一些代碼稍后運行,然后允許繼續執行。

我認為協程是一種更簡潔的方法,但我喜歡以Looper為例,因為它自古以來就是 Android 的一部分,當你有 main 時,它可以是一種獲得這種行為的簡單方法- 需要延遲或運行大量時間的線程工作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM