让导航中的 DialogFragment 在弹出返回堆栈时不会消失
Let DialogFragment in navigation not disappear on poping back stack
我有 FragmentA、FragmentB 和 DialogFragment(BottomDialogFragment)。我将它们缩写为 A、B 和 D
D会在点击A中的按钮后显示。这意味着 A -> D
B会在点击D中的按钮后显示。这意味着 D -> B
我在navigation.xml
中配置它们
<fragment
android:id="@+id/A"
android:name="com.example.A">
<action
android:id="@+id/A_D"
app:destination="@id/D" />
</fragment>
<dialog
android:id="@+id/D"
android:name="com.example.D">
<action
android:id="@+id/D_B"
app:destination="@id/B" />
</dialog>
<fragment
android:id="@+id/B"
android:name="com.example.B">
</fragment>
现在当我点击A中的按钮时,片段会跳转到D。
然后我点击D中的按钮,片段会跳转到B。
但是当我在 B 中弹出导航堆栈时,它会返回到 A,并且 D 没有显示。
我该怎么办?我希望 D 仍然存在于 A.
表面
无需使用 controller.popBack() 弹出堆栈,因为堆栈由导航库管理。请注意,堆栈在后进先出的基础上运行,因此这就是片段消失的原因。
您需要添加更多导航图操作:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation"
app:startDestination="@id/A">
<fragment
android:id="@+id/A"
android:name="com.example.A"
android:label="A" >
<action
android:id="@+id/action_A_to_D"
app:destination="@id/D" />
</fragment>
<dialog
android:id="@+id/D"
android:name="com.example.D"
android:label="D" >
<action
android:id="@+id/action_D_to_B"
app:destination="@id/B" />
<action
android:id="@+id/action_D_to_A"
app:destination="@id/A" />
</dialog>
<fragment
android:id="@+id/B"
android:name="com.example.B"
android:label="B" >
<action
android:id="@+id/action_B_to_D"
app:destination="@id/D" />
</fragment>
</navigation>
然后在你的对话框 Fragment 中添加以下内容:
对于OK/Yes:--
private fun doNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentB)
}
对于CANCEL/No:--
private fun doBackNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentA)
}
最后在 B 片段中,覆盖后退按钮并执行此操作:
Navigation.findNavController(requireView()).navigate(R.id.action_fragmentB_to_fragmentD)
What should I do? I want the D still exists on the surface of A.
到目前为止,这是不可能的,因为对话框是在 window 而不是 activities/fragments 中处理的;因此它们的返回堆栈处理方式不同 这是因为 Dialog
实现了 FloatingWindow interface.
检查 以获得更多说明。
但要回答您的问题,有两种方法:
- 方法 1: 将片段
B
更改为 DailogFragment
,在这种情况下 B
和 D
都是对话框,因此当您弹出堆栈从 B
返回到 D
时,您仍然会看到 D
显示。
- 方法 2: 如果您 return 从
B
到 D
,设置一个标志,如果是,你重新显示 D
.
实际上,方法 2 并不是那么好,因为当您从 D
到 B
时,它不会将 D
保留在后栈中;这只是一种解决方法;用户也会在 return 从 B
到 D
时看到对话框 transitioning/fade 动画;所以这根本不自然。所以,这里只讨论方法一。
详细方法 1:
优点:
- 这很自然,并且会按照您的意愿保持后退堆栈不变。
缺点:
DialogFragment B
比正常的 fragment/activity. 有限制 window
B
不再是普通片段,而是 DialogFragment
,因此您可能会遇到一些其他限制。
要解决B
的有限window,可以使用以下主题:
<style name="DialogTheme" parent="Theme.MyApp">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowIsFloating">false</item>
</style>
其中 Theme.MyApp
是您应用的主题。
并使用 getTheme()
将其应用于 B
:
class FragmentB : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return layoutInflater.inflate(R.layout.fragment_b, container, false)
}
override fun getTheme(): Int = R.style.DialogTheme
}
您还需要将导航图中的 B
更改为对话框:
<dialog
android:id="@+id/B"
android:name="com.example.B">
</dialog>
预览:
请参阅此 link 以获得完整的工作示例。
您需要利用 NavigationUI 全局操作(如 所述)以便能够导航“back”到目的地。将此代码放入 main_graph xml:
<action android:id="@+id/action_global_fragmentD" app:destination="@id/fragmentD"/>
接下来,在您的 activity 中添加这些以赶回按:
class MainActivity: AppCompatActivity {
...
var backPressedListener: OnBackPressedListener? = null
override fun onBackPressed() {
super.onBackPressed()
backPressedListener?.backHaveBeenPressed()
}
}
interface OnBackPressedListener {
fun backHaveBeenPressed()
}
这就像 swift 中的 代表
接下来在 FragmentB 中,添加这些:
class FragmentB: Fragment(), OnBackPressedListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
(activity as MainActivity).backPressedListener = this
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun backHaveBeenPressed() {
// show Dialog
findNavController().navigate(R.id.action_global_fragmentD)
}
}
然后您可以根据需要导航回 DialogFragment。此方法不使用 popBackStack,因为您的 用例 是 NavigationUI 框架未处理的自定义行为(您需要实现它)。
我有 FragmentA、FragmentB 和 DialogFragment(BottomDialogFragment)。我将它们缩写为 A、B 和 D
D会在点击A中的按钮后显示。这意味着 A -> D
B会在点击D中的按钮后显示。这意味着 D -> B
我在navigation.xml
中配置它们<fragment
android:id="@+id/A"
android:name="com.example.A">
<action
android:id="@+id/A_D"
app:destination="@id/D" />
</fragment>
<dialog
android:id="@+id/D"
android:name="com.example.D">
<action
android:id="@+id/D_B"
app:destination="@id/B" />
</dialog>
<fragment
android:id="@+id/B"
android:name="com.example.B">
</fragment>
现在当我点击A中的按钮时,片段会跳转到D。
然后我点击D中的按钮,片段会跳转到B。
但是当我在 B 中弹出导航堆栈时,它会返回到 A,并且 D 没有显示。
我该怎么办?我希望 D 仍然存在于 A.
表面无需使用 controller.popBack() 弹出堆栈,因为堆栈由导航库管理。请注意,堆栈在后进先出的基础上运行,因此这就是片段消失的原因。
您需要添加更多导航图操作:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation"
app:startDestination="@id/A">
<fragment
android:id="@+id/A"
android:name="com.example.A"
android:label="A" >
<action
android:id="@+id/action_A_to_D"
app:destination="@id/D" />
</fragment>
<dialog
android:id="@+id/D"
android:name="com.example.D"
android:label="D" >
<action
android:id="@+id/action_D_to_B"
app:destination="@id/B" />
<action
android:id="@+id/action_D_to_A"
app:destination="@id/A" />
</dialog>
<fragment
android:id="@+id/B"
android:name="com.example.B"
android:label="B" >
<action
android:id="@+id/action_B_to_D"
app:destination="@id/D" />
</fragment>
</navigation>
然后在你的对话框 Fragment 中添加以下内容:
对于OK/Yes:--
private fun doNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentB)
}
对于CANCEL/No:--
private fun doBackNav() {
NavHostFragment.findNavController(this).navigate(R.id.action_fragmentD_to_fragmentA)
}
最后在 B 片段中,覆盖后退按钮并执行此操作:
Navigation.findNavController(requireView()).navigate(R.id.action_fragmentB_to_fragmentD)
What should I do? I want the D still exists on the surface of A.
到目前为止,这是不可能的,因为对话框是在 window 而不是 activities/fragments 中处理的;因此它们的返回堆栈处理方式不同 这是因为 Dialog
实现了 FloatingWindow interface.
检查
但要回答您的问题,有两种方法:
- 方法 1: 将片段
B
更改为DailogFragment
,在这种情况下B
和D
都是对话框,因此当您弹出堆栈从B
返回到D
时,您仍然会看到D
显示。 - 方法 2: 如果您 return 从
B
到D
,设置一个标志,如果是,你重新显示D
.
实际上,方法 2 并不是那么好,因为当您从 D
到 B
时,它不会将 D
保留在后栈中;这只是一种解决方法;用户也会在 return 从 B
到 D
时看到对话框 transitioning/fade 动画;所以这根本不自然。所以,这里只讨论方法一。
详细方法 1:
优点:
- 这很自然,并且会按照您的意愿保持后退堆栈不变。
缺点:
DialogFragment B
比正常的 fragment/activity. 有限制 window
B
不再是普通片段,而是DialogFragment
,因此您可能会遇到一些其他限制。
要解决B
的有限window,可以使用以下主题:
<style name="DialogTheme" parent="Theme.MyApp">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowIsFloating">false</item>
</style>
其中 Theme.MyApp
是您应用的主题。
并使用 getTheme()
将其应用于 B
:
class FragmentB : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return layoutInflater.inflate(R.layout.fragment_b, container, false)
}
override fun getTheme(): Int = R.style.DialogTheme
}
您还需要将导航图中的 B
更改为对话框:
<dialog
android:id="@+id/B"
android:name="com.example.B">
</dialog>
预览:
请参阅此 link 以获得完整的工作示例。
您需要利用 NavigationUI 全局操作(如
<action android:id="@+id/action_global_fragmentD" app:destination="@id/fragmentD"/>
接下来,在您的 activity 中添加这些以赶回按:
class MainActivity: AppCompatActivity {
...
var backPressedListener: OnBackPressedListener? = null
override fun onBackPressed() {
super.onBackPressed()
backPressedListener?.backHaveBeenPressed()
}
}
interface OnBackPressedListener {
fun backHaveBeenPressed()
}
这就像 swift 中的 代表
接下来在 FragmentB 中,添加这些:
class FragmentB: Fragment(), OnBackPressedListener {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
(activity as MainActivity).backPressedListener = this
return inflater.inflate(R.layout.fragment_b, container, false)
}
override fun backHaveBeenPressed() {
// show Dialog
findNavController().navigate(R.id.action_global_fragmentD)
}
}
然后您可以根据需要导航回 DialogFragment。此方法不使用 popBackStack,因为您的 用例 是 NavigationUI 框架未处理的自定义行为(您需要实现它)。