当我尝试在Fragment中打开BottomSheetDialogFragment时,我遇到了问题,使用来自另一个片段的回调结果,该片段嵌套在另一个Activity中。
所有进一步的演示都是对项目中真实的案例的抽象,已经建立了不可更改的应用程序架构。让我来解释一下。
我有一个名为“MainActivity”的主主机Activity,其中包含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使用LoginActivity打开登录屏幕,LoginActivity包含LoginFragment,因为它是授权用户所必需的。
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,其中BaseFragment被称为login,用于授权用户并完成。
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"
}
}
LoginFragment
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
}
}
MainNavigator是用于跨整个应用程序导航的抽象。
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();
有人知道解决办法吗?
2条答案
按热度按时间vlju58qv1#
在调用www.example.com()之前添加此检查bottomsheet.show,如果状态未保存,则可以执行事务,否则忽略。
另一种方法是创建自定义的DialogFragment和overide show方法并添加commitallowstateloss实现
vmdwslir2#
我修复了我的问题,使用: