0

当我尝试在片段中打开 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
    }
}

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();

在此处输入图像描述

有人知道解决方案吗?

4

1 回答 1

1
        if (!childFragmentManager.isStateSaved) {}

如果未保存状态,则在调用 bottomsheet.show() 之前添加此检查,您可以执行事务或忽略。

其他方法是创建自定义 DialogFragment 并覆盖 show 方法并添加 commitallowstateloss 实现

于 2022-02-08T19:06:05.027 回答