Android返回栈白屏问题:TripActivity无法正常返回MainActivity
问题描述
我的应用包含MainActivity和TripActivity,TripActivity内设有底部导航栏,默认显示TripDetailFragment(记为A),可通过底部点击切换至EditTripFragment(记为B)等Fragment。
异常场景:从MainActivity跳转至TripActivity后,完成A→B→A的Fragment导航,点击返回键时TripActivity显示白屏,无法回到MainActivity。
期望路径:MainActivity→TripActivity(详情→编辑→详情)→MainActivity
相关代码
TripActivity.kt
class TripActivity : AppCompatActivity() { private var currentFragment: Fragment? = null private var _binding: ActivityTripBinding? = null private val binding get() = _binding!! lateinit var editTripFragment: EditTripFragment lateinit var expenseFragment: ExpenseFragment lateinit var tripDetailFragment: TripDetailFragment lateinit var currentTrip: TripTable override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() _binding = ActivityTripBinding.inflate(layoutInflater) setContentView(binding.root) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } editTripFragment = EditTripFragment() expenseFragment = ExpenseFragment() tripDetailFragment = TripDetailFragment() val receivedBundle = intent.extras currentTrip = receivedBundle?.getParcelable<TripTable>(Constants.ARG_SELECTED_TRIP)!! if (savedInstanceState == null) { replaceFragment(tripDetailFragment) currentFragment = tripDetailFragment } else { currentFragment = supportFragmentManager.findFragmentById(R.id.fragContainer) } currentFragment?.let { binding.bottomNavigation.selectedItemId = it.id } setupBottomNavigationBar() } override fun onResume() { super.onResume() } private fun setupBottomNavigationBar() { binding.bottomNavigation.setOnItemSelectedListener { item -> val newFragment = when (item.itemId) { R.id.expense -> expenseFragment R.id.editTrip -> editTripFragment R.id.delete -> { AlertDialogHelper.showAlertDialog( this, "Delete TRIP?", R.drawable.bin, "DELETE", "CANCEL", { Toast.makeText(this, "Positive Button Clicked", Toast.LENGTH_SHORT) .show() }, { replaceFragment(tripDetailFragment) } ) currentFragment } else -> tripDetailFragment } if (newFragment != currentFragment) { replaceFragment(newFragment as Fragment) currentFragment = newFragment if (newFragment != tripDetailFragment) { binding.bottomNavigation.visibility = View.GONE } Log.d("current frag", currentFragment.toString()) } true } } private fun replaceFragment(fragment: Fragment) { if (fragment == tripDetailFragment) { binding.bottomNavigation.visibility = View.VISIBLE } val bundle = Bundle().apply { putParcelable(Constants.ARG_SELECTED_TRIP, currentTrip) } fragment.arguments = bundle val transaction = supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_to_left, R.anim.left_to_right) transaction.replace(R.id.fragContainer, fragment) transaction.addToBackStack(null) transaction.commit() } override fun onBackPressed() { if (currentFragment == tripDetailFragment) { finish() } else { super.onBackPressed() } } }
TripDetailFragment.kt
class TripDetailFragment : Fragment() { private var _binding: FragmentTripDetailBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val onBackPressedCallback = object : OnBackPressedCallback(true ) { override fun handleOnBackPressed() { navigateBack() } } requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentTripDetailBinding.inflate(layoutInflater) return binding.root } private fun navigateBack() { parentFragmentManager.popBackStack() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) } override fun onDestroyView() { super.onDestroyView() _binding = null } }
EditTripFragment.kt
class EditTripFragment : Fragment() { private var _binding: FragmentEditTripBinding? = null private val binding get() = _binding!! private lateinit var startDate: String private lateinit var endDate: String private var _toolbarBinding: ToolBarBinding? = null private val toolbarBinding get() = _toolbarBinding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requireActivity().onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { showDiscard() } }) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentEditTripBinding.inflate(layoutInflater) _toolbarBinding = ToolBarBinding.bind(binding.toolbar.root) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val trip:TripTable? = arguments?.getParcelable(Constants.ARG_SELECTED_TRIP) setTripData(trip) toolbarBinding.title.text="Edit Trip" toolbarBinding.icon.setOnClickListener { showDiscard() } } private fun setTripData(trip: TripTable?) { if (trip != null) { val date="${trip.startDate} - ${trip.endDate}" binding.tripName.text=trip.tripName.toEditable() binding.tripLocation.text=trip.tripLocation.toEditable() binding.selectedDate.visibility=View.VISIBLE binding.selectedCurr.visibility=View.VISIBLE binding.selectedCurr.text=trip.currency binding.selectedDate.text=date.toString() } else { Log.e("error in edit trip", error("some error occurred")) } } override fun onDestroyView() { super.onDestroyView() _binding = null _toolbarBinding = null } fun showDiscard():Boolean{ AlertDialogHelper.showAlertDialog( requireActivity(), "Discard Changes?", R.drawable.bin, "DISCARD", "", { parentFragmentManager.popBackStack() }, {} ) return false } // Extension function to convert String to Editable private fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this) }
修复方案
问题根源
- 回退栈冗余:每次切换Fragment都调用
addToBackStack(null),导致A→B→A操作后回退栈存在[A, B, A]三个实例,点击返回时弹出栈顶A后回到B,但currentFragment仍指向A,触发Activity直接finish,导致白屏。 - 返回拦截逻辑错误:TripDetailFragment拦截返回键直接调用
popBackStack(),当回退栈仅剩初始A时,弹出后容器为空,无法触发Activity关闭。
1. 优化底部导航切换逻辑
修改TripActivity的导航逻辑,避免重复添加A到回退栈,切换回A时直接回退到已有实例:
private fun setupBottomNavigationBar() { binding.bottomNavigation.setOnItemSelectedListener { item -> when (item.itemId) { R.id.expense -> switchFragment(expenseFragment) R.id.editTrip -> switchFragment(editTripFragment) R.id.delete -> { AlertDialogHelper.showAlertDialog( this, "删除行程?", R.drawable.bin, "删除", "取消", { Toast.makeText(this, "已删除", Toast.LENGTH_SHORT).show() }, { backToTripDetail() } ) } else -> backToTripDetail() } true } } private fun backToTripDetail() { val hasTripDetailInStack = supportFragmentManager.backStackEntryCount > 0 && supportFragmentManager.getBackStackEntryAt(supportFragmentManager.backStackEntryCount - 1).name == "TripDetail" if (currentFragment != tripDetailFragment) { if (hasTripDetailInStack) { supportFragmentManager.popBackStack("TripDetail", 0) } else { switchFragment(tripDetailFragment) } currentFragment = tripDetailFragment binding.bottomNavigation.visibility = View.VISIBLE } } private fun switchFragment(fragment: Fragment) { if (fragment == tripDetailFragment) { binding.bottomNavigation.visibility = View.VISIBLE } else { binding.bottomNavigation.visibility = View.GONE } val bundle = Bundle().apply { putParcelable(Constants.ARG_SELECTED_TRIP, currentTrip) } fragment.arguments = bundle val transaction = supportFragmentManager.beginTransaction() .setCustomAnimations(R.anim.right_to_left, R.anim.left_to_right) .replace(R.id.fragContainer, fragment) if (fragment != tripDetailFragment) { transaction.addToBackStack("TripDetail") } transaction.commit() }
2. 修改TripDetailFragment返回逻辑
区分根Fragment场景,回退栈为空时直接关闭Activity:
private fun navigateBack() { if (parentFragmentManager.backStackEntryCount == 0) { requireActivity().finish() } else { parentFragmentManager.popBackStack() } }
3. 调整TripActivity的onBackPressed逻辑
让Fragment回调优先处理,避免冲突:
override fun onBackPressed() { if (!onBackPressedDispatcher.hasEnabledCallbacks()) { super.onBackPressed() } }
修改后,A→B→A操作会回退到初始A实例,点击返回键直接关闭TripActivity回到MainActivity,解决白屏问题。
内容的提问来源于stack exchange,提问作者Ayush
相关产品推荐
相关产品推荐

