You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
}
修复方案

问题根源

  1. 回退栈冗余:每次切换Fragment都调用addToBackStack(null),导致A→B→A操作后回退栈存在[A, B, A]三个实例,点击返回时弹出栈顶A后回到B,但currentFragment仍指向A,触发Activity直接finish,导致白屏。
  2. 返回拦截逻辑错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 11:19:54