Fragment返回后ComposeView无法重新创建的问题求助
问题详情
在Fragment A中集成ComposeView,导航至Nav Graph B并返回后,ComposeView无法重新生成视图,但XML部分功能正常。尝试为ComposeView设置ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed无效果,临时在Fragment#onResume()中调用composeView.disposeComposition(),但离开Fragment A时无法彻底清除状态(首次进入加载稍慢,来回切换后加载极快)。
代码展示
XML布局
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data /> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/dark_grey_09"> <include android:id="@+id/header" layout="@layout/layout_bottom_sheet_header" android:layout_width="match_parent" android:layout_height="@dimen/bottom_sheet_header_height" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> <androidx.compose.ui.platform.ComposeView android:id="@+id/composeView" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/header" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
Fragment A代码
override fun initView() { initHeader() initComposeView() } override fun onResume() { super.onResume() binding.composeView.disposeComposition() // 临时解决方案 } private fun initComposeView() { Timber.d("==> initComposeView") binding.composeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MdcTheme { val state = viewModel.state.collectAsState(initial = AppUnlockIntervalViewModel.ViewState()) Column(modifier = Modifier.padding(top = 16.dp)) { Text( text = getStringByKey(R.string.auth_setting_security_unlock_desc), style = TextStyle( color = colorResource( id = R.color.light_grey_09 ), fontFamily = FontFamily(Font(resId = R.font.inter_regular)), fontSize = 14.sp, lineHeight = 24.sp ), modifier = Modifier.padding(horizontal = 16.dp) ) Timber.d("==> initComposeView: Init text desc") Spacer(modifier = Modifier.height(16.dp)) IntervalList( list = state.value.datasource, selectedInterval = state.value.selectedInterval ) Timber.d("==> initComposeView: Init list") } } } } } @Composable private fun IntervalList(list: List<LockTimeInterval>, selectedInterval: LockTimeInterval) { LazyColumn { items(count = list.size) { position -> val selected = selectedInterval == list[position] IntervalListItem(position, list[position], selected) } } }
BaseFragment代码
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { // 复用View Binding以保存视图状态 if (!::binding.isInitialized) { binding = DataBindingUtil.inflate(inflater, layoutId, container, false) } binding.lifecycleOwner = viewLifecycleOwner return binding.root } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initData() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() initViewListener() initHeaderViewListener() initBackgroundListener() initObserver() enableSavedStateHandles() }
解决方案
核心原因分析
- View复用导致Compose内容未更新:BaseFragment复用了View Binding,
initComposeView仅在首次onViewCreated时调用一次,导航返回后不会重新触发Compose内容设置。 - 生命周期绑定错误:
DisposeOnViewTreeLifecycleDestroyed绑定的是ViewTree的生命周期,但复用的View未被销毁,导致Compose的Composition无法释放,返回后也不会重建。
修改方案
1. 调整ComposeView的生命周期绑定策略
将绑定策略改为DisposeOnLifecycleDestroyed(viewLifecycleOwner),绑定到Fragment的View生命周期,确保Fragment视图销毁时Compose Composition被释放:
private fun setupComposeViewStrategy() { binding.composeView.setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner) ) }
2. 拆分Compose内容设置逻辑,在可见时更新
将设置Compose内容的逻辑单独抽离,在onResume中调用,确保每次Fragment可见时都能重新生成视图:
override fun initView() { initHeader() setupComposeViewStrategy() } override fun onResume() { super.onResume() updateComposeContent() } private fun updateComposeContent() { binding.composeView.setContent { MdcTheme { val state = viewModel.state.collectAsState(initial = AppUnlockIntervalViewModel.ViewState()) Column(modifier = Modifier.padding(top = 16.dp)) { Text( text = getStringByKey(R.string.auth_setting_security_unlock_desc), style = TextStyle( color = colorResource(id = R.color.light_grey_09), fontFamily = FontFamily(Font(resId = R.font.inter_regular)), fontSize = 14.sp, lineHeight = 24.sp ), modifier = Modifier.padding(horizontal = 16.dp) ) Spacer(modifier = Modifier.height(16.dp)) IntervalList( list = state.value.datasource, selectedInterval = state.value.selectedInterval ) } } } }
3. 确保ViewModel状态正确更新
导航返回时,可触发ViewModel重新加载数据,或确认viewModel.state的Flow能正确发送新状态,保证Compose能感知到数据变化并刷新UI。
为什么临时方案无效
disposeComposition()仅释放了当前的Compose Composition,但未重新设置内容。由于initComposeView仅执行一次,返回后没有新的Composition被创建,导致视图无法更新。而修改后的方案每次可见时重新设置Content,结合正确的生命周期绑定,既能释放旧的Composition,又能创建新的视图。
内容的提问来源于stack exchange,提问作者Harry T.

