android OnBackPressedDispatcher的正确使用

9q78igpj  于 2023-02-17  发布在  Android
关注(0)|答案(1)|浏览(418)

有时候,我想自己处理用户按下的后退按钮,我的代码(示例)如下:

class TestActivity : AppCompatActivity() {

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

    override fun onBackPressed() {

        if (checkSomeCondition()) {
            // do nothing
        } else {
            super.onBackPressed()
        }
    }

    private fun checkSomeCondition() = false
}

当按下back时,我得到通知,然后我决定是否要处理它,或者通过调用super.onBackPressed()让系统处理它。
由于onBackPressed()现在已被弃用,因此我使用OnBackPressedDispatcher替换它:

class TestActivity : AppCompatActivity() {

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

        onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (checkSomeCondition()) {
                    // do nothing
                } else {
                    onBackPressedDispatcher.onBackPressed()
                }
            }
        })
    }

    private fun checkSomeCondition() = false
}

此代码的问题:通过调用onBackPressedDispatcher.onBackPressed(),我调用了我自己的回调函数,创建了一个无限循环,而不是像使用super.onBackPressed()那样将其交给系统。
这可以通过临时禁用回调来避免,例如:

onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if (checkSomeCondition()) {
                // do nothing
            } else {
                this.isEnabled = false
                onBackPressedDispatcher.onBackPressed()
                this.isEnabled = true
            }
        }
    })

但这似乎相当尴尬,而且似乎不像是故意的。
这里OnBackPressedDispatcher的正确用法是什么?它可以让我自己处理后退按钮将其交给系统?
PS:我已经看到了this关于onBackPressed的弃用的问题,但它没有回答我更具体的问题。

bq3bfh9z

bq3bfh9z1#

按照Basics of System Back videoPredictive Back gesture的全部意义在于提前知道要处理什么。
这意味着,您应该永远不要checkSomeCondition这样的即时逻辑作为对handleOnBackPressed的调用的一部分。
相反,正如Custom back navigation documentation中所解释的,只要用于签入checkSomeCondition的条件发生变化,就应该提前更新OnBackPressedCallbackisEnabled状态,这样可以确保回调仅在条件为真时调用,在条件为假时禁用,从而允许默认行为发生。

class TestActivity : AppCompatActivity() {

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

        // Instead of always setting enabled to true at the beginning,
        // you need to check the state ahead of time to know what the
        // initial enabled state should be
        val isConditionAlreadySet = checkSomeCondition()
        val callback = object : OnBackPressedCallback(
            isConditionAlreadySet
        ) {
            override fun handleOnBackPressed() {
                // Since you've handled isEnabled correctly, you know
                // your condition is set correctly here, so you can
                // unconditionally do what you need to do to handle back
            }
        }
        // This is the key part - now you know ahead of time
        // when the condition changes, which lets you control
        // when you want to handle back or not
        setOnSomeConditionChangedListener { isConditionMet ->
          callback.isEnabled = isConditionMet
        }
        onBackPressedDispatcher.addCallback(this, callback)
    }

    private fun checkSomeCondition() = false
    private fun setOnSomeConditionChangedListener(
        (isConditionMet: Boolean) -> Unit
    ) {
        // The implementation here will depend on what
        // conditions checkSomeCondition() actually depends on
    }

}

相关问题