【发布时间】:2022-02-09 03:10:02
【问题描述】:
当我试图在 Fragment 中打开 BottomSheetDialogFragment 时遇到问题,使用来自另一个片段的回调结果,该片段嵌套在另一个活动中。
所有进一步的演示都是对项目中真实案例的抽象,具有无法更改的已建立的应用程序架构。 让我来解释你。
我有一个名为“MainActivity”的主要主机活动,其中包含 BaseFragment
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.flContainer.setOnClickListener { view ->
Snackbar.make(view, \"Replace with your own action\", Snackbar.LENGTH_LONG)
.setAction(\"Action\", null).show()
}
}
override fun onStart() {
super.onStart()
supportFragmentManager.beginTransaction()
.add(R.id.flContainer, BaseFragment())
.addToBackStack(BaseFragment.TAG)
.commitAllowingStateLoss()
}
override fun onResume() {
super.onResume()
Log.e(\"VadymTag\", \"MainActivity onResume\")
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.e(\"VadymTag\", \"MainActivity onSaveInstanceState\")
}
override fun onPause() {
super.onPause()
Log.e(\"VadymTag\", \"MainActivity onPause\")
}
}
这个 BaseFragment 使用包含 LoginFragment 的 LoginActivity 打开登录屏幕,因为它需要授权用户。
class BaseFragment : Fragment() {
private var _binding: FragmentBaseBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentBaseBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding?.bOpenLogin?.setOnClickListener {
startActivity(Intent(requireContext(), LoginActivity::class.java))
}
MainNavigator.openBottomSheet = ::openBottomSheet
}
override fun onResume() {
super.onResume()
Log.e(\"VadymTag\", \"BaseFragment onResume\")
}
fun openBottomSheet() {
val bottomSheetFragment = MyBottomSheetDialog()
bottomSheetFragment.show(childFragmentManager, MyBottomSheetDialog.TAG)
}
override fun onPause() {
super.onPause()
Log.e(\"VadymTag\", \"BaseFragment onPause\")
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.e(\"VadymTag\", \"BaseFragment onSaveInstanceState\")
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
val TAG = BaseFragment::class.java.simpleName
}
}
LoginActivity 还使用supportFragmentManager.setFragmentResultListener (... 处理登录成功/失败的结果。对于此示例, FragmentResultListener 成功处理任何更改。
LoginActivity 要求 MainNavigator 从称为登录的 BaseFragment 打开 BottomSheetDialogFragment,用于授权用户并完成。
class LoginActivity : AppCompatActivity() {
private lateinit var binding: LoginMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LoginMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun onStart() {
super.onStart()
supportFragmentManager.beginTransaction()
.add(R.id.flLoginContainer, LoginFragment())
.addToBackStack(LoginFragment.TAG)
.commit()
initLoginListener()
}
fun initLoginListener() {
supportFragmentManager
.setFragmentResultListener(LOGIN_KEY, this) { _, bundle ->
MainNavigator.openBottomSheet()
finish()
}
}
companion object {
const val LOGIN_KEY = \"login_key\"
const val LOGIN_FIELD = \"login_key\"
}
}
登录片段
class LoginFragment : Fragment() {
private var _binding: LoginFragmentBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = LoginFragmentBinding.inflate(inflater, container, false)
return _binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding?.bLoginSuccess?.setOnClickListener {
parentFragmentManager.setFragmentResult(LOGIN_KEY, bundleOf(LOGIN_FIELD to true))
}
}
companion object {
val TAG = LoginFragment::class.java.simpleName
}
}
Main Navigator 是用于跨整个应用程序导航的抽象。
object MainNavigator {
var openBottomSheet: () -> Unit = {}
}
MainNavigator 调用 BaseFragment 打开 BottomSheetDialogFragment。
class MyBottomSheetDialog : BottomSheetDialogFragment() {
private var _binding: FragmentMyBottomSheetBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentMyBottomSheetBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
val TAG = MyBottomSheetDialog::class.java.simpleName
}
}
当 LoginActivity 调用 MainNavigator 打开 BottomSheetDialogFragment。 - 发生 2022-02-08 19:49:58.285 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: BaseFragment onPause 2022-02-08 19:49:58.286 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: MainActivity onPause 2022-02-08 19:49:58.790 0-0/? E/init: updatable process \'console\' exited 4 times in 4 minutes 2022-02-08 19:49:59.027 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: BaseFragment onSaveInstanceState 2022-02-08 19:49:59.031 20135-20135/com.vadim.stackoverflowquestion E/VadymTag: MainActivity onSaveInstanceState 2022-02-08 19:49:59.799 20135-20135/com.vadim.stackoverflowquestion E/AndroidRuntime: FATAL EXCEPTION: main Process: com.galazjukvadim.stackoverflowquestion, PID: 20135 java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1844) at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1884) at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329) at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294) at androidx.fragment.app.DialogFragment.show(DialogFragment.java:260) at com.galazjukvadim.stackoverflowquestion.BaseFragment.openBottomSheet(BaseFragment.kt:52) at com.galazjukvadim.stackoverflowquestion.BaseFragment$onViewCreated$2.invoke(BaseFragment.kt:39) at com.galazjukvadim.stackoverflowquestion.BaseFragment$onViewCreated$2.invoke(BaseFragment.kt:39) at com.galazjukvadim.stackoverflowquestion.LoginActivity.initLoginListener$lambda-0(LoginActivity.kt:33) at com.galazjukvadim.stackoverflowquestion.LoginActivity.$r8$lambda$3dwuINVTP3WL69H0HgmUiCWJ7Dw(Unknown Source:0) at com.galazjukvadim.stackoverflowquestion.LoginActivity$$ExternalSyntheticLambda0.onFragmentResult(Unknown Source:2) at androidx.fragment.app.FragmentManager$LifecycleAwareResultListener.onFragmentResult(FragmentManager.java:256) at androidx.fragment.app.FragmentManager.setFragmentResult(FragmentManager.java:865) at com.galazjukvadim.stackoverflowquestion.LoginFragment.onViewCreated$lambda-0(LoginFragment.kt:30) at com.galazjukvadim.stackoverflowquestion.LoginFragment.$r8$lambda$PNHKtYyi4mi0uK7kLsV6wOurKW4(Unknown Source:0)
它引起了以下称为 Activity state loss 的问题。
阅读以下文章:1,2 我看到了预期的行为。
我的 BaseFragment 和 MainActivity 调用了 onPause 并在 onSaveInstanceState 之后触发了 throw IllegalStateException: Can not perform this action after onSaveInstanceState 。
在这种情况下,使用.commitAllowingStateLoss() 显示BottomSheetDialogFragment 是可以负担的。但是在bottomSheetFragment.show(childFragmentManager, MyBottomSheetDialog.TAG) 的引擎盖下通常使用ft.commit();
有人知道解决方案吗?
标签: android-fragments fragment android-dialogfragment fragmenttransaction activity-state