使用导航架构组件在片段中显示对备份/备份的确认

Jam*_*mes 4 android android-architecture-navigation

我正在使用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)
}
Run Code Online (Sandbox Code Playgroud)

对于“后退”导航,我在片段及其活动之间创建了一个自定义接口和回调系统:

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
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

对于这么简单的事情,这一切都是非常粗略的和简单的。有没有更好的办法?

Ric*_*ira 10

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

因此,为了显示对话框,您必须执行以下操作:

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

至于向上按钮,似乎(至少现在)没有很多可能性。到目前为止,我唯一找到的使用导航组件的选项是为导航本身添加一个侦听器,该侦听器可以同时处理两个按钮:

navController.addOnDestinationChangedListener { navController, destination ->
  if (destination.id == R.id.destination) {
    // do your thing
  }
}
Run Code Online (Sandbox Code Playgroud)

无论如何,这样做有一个警告,就是允许您在其中添加侦听器的活动或片段了解其可能不应该到达的目的地。

  • 如果按下 HOME 按钮,然后返回到使用 requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) 的地方,则不会调用handleOnBackPressed() (3认同)

11m*_*1m0 7

使用导航架构组件,您可以执行以下操作:

  1. 告诉您的活动将主页按钮(后退箭头)上的所有向上点击发送给收听它的任何人。这在你的活动中。
override fun onOptionsItemSelected(item: MenuItem): Boolean {
     if (item.itemId == android.R.id.home) {
         onBackPressedDispatcher.onBackPressed()
         return true
     }
     return super.onOptionsItemSelected(item)
}
Run Code Online (Sandbox Code Playgroud)
  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()
           }
        }
    }
Run Code Online (Sandbox Code Playgroud)