在带有导航架构组件的片段中显示 back/up 的确认
Show confirmation on back/up in Fragment with Navigation Architecture Component
我正在为 Android 使用导航架构组件。
对于我的一个片段,我希望拦截 "back" 和 "up" 导航,以便我可以在丢弃用户未保存的任何更改之前显示确认对话框。 (当您在编辑事件详细信息后按 back/up 时,与默认日历应用程序的行为相同)
我目前的做法(未经测试)如下:
对于 "up" 导航,我覆盖了片段上的 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)
}
对于 "back" 导航,我在片段及其 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
}
}
...
}
对于这么简单的事情,这一切都非常粗俗和样板。有没有更好的方法?
如果您将它与 AppBarConfiguration 一起使用,最新版本现在有一个 AppBarConfiguration.OnNavigateUpListener。参考下面link获取更多信息
从 androidx.appcompat:appcompat:1.1.0-beta01
开始,为了拦截带导航组件的后退按钮,您需要向 OnBackPressedDispatcher
添加回调。此回调必须扩展 OnBackPressedCallback
并覆盖 handleOnBackPressed
。 OnBackPressedDispatcher
遵循责任链模式来处理回调。换句话说,如果您将回调设置为 enabled,则只会执行您的回调。否则,OnBackPressedDispatcher
将忽略它并继续进行下一个回调,依此类推,直到找到一个启用的回调(例如,当您有多个回调时,这可能很有用)。有关此 here.
的更多信息
因此,为了显示您的对话框,您必须执行与此类似的操作:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val callback = object : OnBackPressedCallback(true /** true means that the callback is enabled */) {
override fun handleOnBackPressed() {
// Show your dialog and handle navigation
}
}
// note that you could enable/disable the callback here as well by setting callback.isEnabled = true/false
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}
至于向上按钮,似乎(至少目前)没有太多可能性。到目前为止,我能找到的唯一使用导航组件的选项是为导航本身添加一个侦听器,它将同时处理两个按钮:
navController.addOnDestinationChangedListener { navController, destination ->
if (destination.id == R.id.destination) {
// do your thing
}
}
无论如何,这有一个警告,即允许 activity 或您添加侦听器的片段了解它可能不应该知道的目的地。
对于向上导航,只需覆盖 onOptionsItemSelected()
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
android.R.id.home -> {
showDialog() // show your dialog here
true
}
else -> super.onOptionsItemSelected(item)
}
使用导航架构组件,您可以执行以下操作:
- 告诉您的 activity 将主页按钮(后退箭头)上的所有向上点击发送给任何正在收听它的人。这进入你的 activity.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressedDispatcher.onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
- 然后在你的片段中,像这样消费事件
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()
}
}
}
您可以在片段的 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()
}
}
}
}
}
)
如果您在 activity 中覆盖 onBackPressed() 必须确保它应该调用 super.onBackOnBackPressed() 否则这些调度程序不会触发
我正在为 Android 使用导航架构组件。
对于我的一个片段,我希望拦截 "back" 和 "up" 导航,以便我可以在丢弃用户未保存的任何更改之前显示确认对话框。 (当您在编辑事件详细信息后按 back/up 时,与默认日历应用程序的行为相同)
我目前的做法(未经测试)如下:
对于 "up" 导航,我覆盖了片段上的 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)
}
对于 "back" 导航,我在片段及其 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
}
}
...
}
对于这么简单的事情,这一切都非常粗俗和样板。有没有更好的方法?
如果您将它与 AppBarConfiguration 一起使用,最新版本现在有一个 AppBarConfiguration.OnNavigateUpListener。参考下面link获取更多信息
从 androidx.appcompat:appcompat:1.1.0-beta01
开始,为了拦截带导航组件的后退按钮,您需要向 OnBackPressedDispatcher
添加回调。此回调必须扩展 OnBackPressedCallback
并覆盖 handleOnBackPressed
。 OnBackPressedDispatcher
遵循责任链模式来处理回调。换句话说,如果您将回调设置为 enabled,则只会执行您的回调。否则,OnBackPressedDispatcher
将忽略它并继续进行下一个回调,依此类推,直到找到一个启用的回调(例如,当您有多个回调时,这可能很有用)。有关此 here.
因此,为了显示您的对话框,您必须执行与此类似的操作:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val callback = object : OnBackPressedCallback(true /** true means that the callback is enabled */) {
override fun handleOnBackPressed() {
// Show your dialog and handle navigation
}
}
// note that you could enable/disable the callback here as well by setting callback.isEnabled = true/false
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}
至于向上按钮,似乎(至少目前)没有太多可能性。到目前为止,我能找到的唯一使用导航组件的选项是为导航本身添加一个侦听器,它将同时处理两个按钮:
navController.addOnDestinationChangedListener { navController, destination ->
if (destination.id == R.id.destination) {
// do your thing
}
}
无论如何,这有一个警告,即允许 activity 或您添加侦听器的片段了解它可能不应该知道的目的地。
对于向上导航,只需覆盖 onOptionsItemSelected()
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
android.R.id.home -> {
showDialog() // show your dialog here
true
}
else -> super.onOptionsItemSelected(item)
}
使用导航架构组件,您可以执行以下操作:
- 告诉您的 activity 将主页按钮(后退箭头)上的所有向上点击发送给任何正在收听它的人。这进入你的 activity.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressedDispatcher.onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
- 然后在你的片段中,像这样消费事件
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()
}
}
}
您可以在片段的 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()
}
}
}
}
}
)
如果您在 activity 中覆盖 onBackPressed() 必须确保它应该调用 super.onBackOnBackPressed() 否则这些调度程序不会触发