OnBackPressedDispatcher 的正确使用

asc*_*sco 11 android

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

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
}
Run Code Online (Sandbox Code Playgroud)

当按下返回时我会收到通知,然后我决定是否要处理它,或者让系统通过调用 来处理它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
}
Run Code Online (Sandbox Code Playgroud)

这段代码的问题是:通过调用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
            }
        }
    })
Run Code Online (Sandbox Code Playgroud)

但这看起来相当尴尬,而且似乎并不是有意为之。

OnBackPressedDispatcher让我自己处理后退按钮或将其交给系统的正确用法是什么?

PS:我已经看到了关于弃用 onBackPressed 的问题,但它没有回答我更具体的问题。

ian*_*ake 5

根据“系统后退基础知识”视频,预测后退手势的全部要点是提前了解要处理的后退内容。

这意味着您永远不应该拥有即时逻辑,例如checkSomeCondition作为调用的一部分handleOnBackPressed。

相反,正如自定义后退导航文档中所述,只要您用于签入的条件发生变化,您就应该提前更新isEnabled状态。这可确保仅当条件为真时才调用回调,而当条件为假时则禁用回调,从而允许发生默认行为。OnBackPressedCallback checkSomeCondition

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
    }

}
Run Code Online (Sandbox Code Playgroud)