android 在带有导航体系结构组件的片段中显示备份确认

lnlaulya  于 2023-02-20  发布在  Android
关注(0)|答案(6)|浏览(149)

我正在使用Android的导航架构组件。
对于我的一个片段,我希望拦截“后退”和“向上”导航,这样我就可以在放弃用户任何未保存的更改之前显示一个确认对话框。(当您在编辑事件详细信息后按下后退/向上时,与默认日历应用程序的行为相同)
我目前的方法(未经测试)如下:
对于“向上”导航,我覆盖了片段上的onOptionsItemSelected

override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    if(item?.itemId == android.R.id.home) {
        if(unsavedChangesExist()) {
            // TODO: show confirmation dialog
            return true
        }
    }
    return super.onOptionsItemSelected(item)
}

对于“返回”导航,我在片段和它的Activity之间创建了一个自定义接口和回调系统:

interface BackHandler {
    fun onBackPressed(): Boolean
}

class MainActivity : AppCompatActivity() {
    ...

    val backHandlers: MutableSet<BackHandler> = mutableSetOf()

    override fun onBackPressed() {
        for(handler in backHandlers) {
            if(handler.onBackPressed()) {
                return
            }
        }
        super.onBackPressed()
    }

    ...
}

class MyFragment: Fragment(), BackHandler {
    ...

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is MainActivity) {
            context.backHandlers.add(this)
        }
    }

    override fun onDetach() {
        (activity as? MainActivity)?.backHandlers?.remove(this)
        super.onDetach()
    }

    override fun onBackPressed(): Boolean {
        if(unsavedChangedExist()) {
            // TODO: show confirmation dialog
            return true
        }
    }

    ...
}

对于这么简单的事情来说,这一切都是相当恶心和样板的。有没有更好的方法?

cngwdvgl

cngwdvgl1#

androidx.appcompat:appcompat:1.1.0-beta01开始,为了拦截带有导航组件的后退按钮,您需要向OnBackPressedDispatcher添加回调。此回调必须扩展OnBackPressedCallback并覆盖handleOnBackPressedOnBackPressedDispatcher遵循责任链模式来处理回调。换句话说,如果您将回调设置为enabled,则仅执行您的回调。否则,OnBackPressedDispatcher将忽略它并继续执行下一个回调,以此类推,直到找到一个已启用的回调(例如,当您有多个回调时,这可能会很有用)。
因此,为了显示对话框,你必须在Fragment中执行类似的操作:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Show your dialog and handle navigation
    }

    // you can enable/disable the callback here by setting
    // callback.isEnabled = true/false. Or just enable it in the lambda.
}

addCallback方法接收一个LifecycleOwner,并且它将确保在LifecycleOwner到达STARTED阶段时添加回调,不仅如此,这还使得当其关联的LifecycleOwner被销毁时删除回调。
至于up按钮,似乎(至少现在)没有太多的可能性。到目前为止,我能找到的使用导航组件的唯一选项是为导航本身添加一个侦听器,它将同时处理两个按钮:

navController.addOnDestinationChangedListener { navController, destination ->
  if (destination.id == R.id.destination) {
    // do your thing
  }
}

无论如何,这都有一个警告,即允许您添加侦听器的活动或片段知道它可能不应该知道的目的地。

inb24sb2

inb24sb22#

使用导航体系结构组件,您可以执行以下操作:
1.告诉你的活动把所有点击主页按钮(后退箭头)的点击发送给所有正在收听它的人。

override fun onOptionsItemSelected(item: MenuItem): Boolean {
     if (item.itemId == android.R.id.home) {
         onBackPressedDispatcher.onBackPressed()
         return true
     }
     return super.onOptionsItemSelected(item)
}

1.然后在你的片段中,像这样消耗事件

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requireActivity().onBackPressedDispatcher.addCallback(this) {
           if (*condition for showing dialog here*) {
               // Show dialog
           } else {
               // pop fragment by calling function below. Analogous to when the user presses the system UP button when the associated navigation host has focus.
               findNavController().navigateUp()
           }
        }
    }
iovurdzv

iovurdzv3#

对于向上导航,只需覆盖onOptionsItemSelected()

override fun onOptionsItemSelected(item: MenuItem): Boolean =
    when (item.itemId) {
        android.R.id.home -> {
            showDialog() // show your dialog here
            true
        }
        else -> super.onOptionsItemSelected(item)
}
stszievb

stszievb4#

您可以在片段中的onAttach中使用以下函数,以便在导航组件的帮助下覆盖onBackPressed()

requireActivity().onBackPressedDispatcher.addCallback(
    this,
    object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            if (YOUR_CONDITION) {
                // Do something here
            } else {
                if (!findNavController().navigateUp()) {
                    if (isEnabled) {
                        isEnabled = false
                        requireActivity().onBackPressedDispatcher.onBackPressed()
                    }
                }
            }
        }
    }
)
6xfqseft

6xfqseft5#

如果您将其与AppBarConfiguration一起使用,则最新版本中现在提供了AppBarConfiguration.OnNavigateUpListener。有关详细信息,请参阅以下链接
https://developer.android.com/reference/androidx/navigation/ui/AppBarConfiguration.OnNavigateUpListener

gcuhipw9

gcuhipw96#

如果您在活动中覆盖onBackPressed(),则必须确保它应该调用super.onBackOnBackPressed(),否则这些调度程序不会触发

相关问题