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

Fragment切换返回后ComposeView组件消失问题求助

ComposeView内容在Fragment切换返回后消失(replace策略)

在基于FragmentContainerView的Activity中管理多个Fragment,首个Fragment采用XML嵌入ComposeView的混合开发模式。使用replace策略切换Fragment,且将Fragment实例预存在列表中,但从第二个Fragment切回首个Fragment时,ComposeView中的内容会消失。已尝试修改ViewCompositionStrategy仍未解决,使用的Compose BOM版本为2022.10.00至2023.01.00。

相关代码

NavigationActivity.kt

import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.navigation.NavDeepLinkRequest
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.ActivityNavigationBinding

class NavigationActivity : AppCompatActivity() {

    private val binding by viewBinding(ActivityNavigationBinding::inflate)

    private val listOfFragment = listOf(FragmentMainNav(), FragmentDetailNav())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        binding.btnHome.setOnClickListener{
            replace(HOME_NAV)
        }
        binding.btnDetail.setOnClickListener{
            replace(DETAIL_NAV)
        }
        Navigation.createNavigateOnClickListener(R.id.fragmentDetailNav, null)
    }

    private fun replace(nav: String) {
        supportFragmentManager
            .beginTransaction()
            .replace(
                binding.fragmentContainerView.id,
                if (nav == HOME_NAV) listOfFragment[0] else listOfFragment[1]
            )
            .commit()
    }

    companion object {
        const val HOME_NAV = "home_nav"
        const val DETAIL_NAV = "detail_nav"
    }
}

FragmentMainNav.kt

import android.os.Bundle
import android.view.View
import androidx.compose.material3.Text
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.FragmentNavMainBinding

class FragmentMainNav : BindingFragment<FragmentNavMainBinding>() {

    override val binding by viewBinding(FragmentNavMainBinding::inflate)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.textMain.text = "Fragment Main"

        binding.composeView.apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent { 
                Text(text = "This is Compose")
            }
        }

    }
}

fragment_nav_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment Main"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_main" />

</androidx.constraintlayout.widget.ConstraintLayout>

FragmentDetailNav.kt

import android.os.Bundle
import android.view.View
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.databinding.FragmentNavDetailBinding
import id.derysudrajat.library.databinding.FragmentNavMainBinding

class FragmentDetailNav : BindingFragment<FragmentNavDetailBinding>() {

    override val binding by viewBinding(FragmentNavDetailBinding::inflate)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textDetail.text = "Detail Navigation"
    }
}

fragment_nav_detail.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_detail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Detail Navigation"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

解决方案

问题根源:使用replace策略切换Fragment时,首个Fragment的View会被销毁,当前设置的DisposeOnViewTreeLifecycleDestroyed策略会让Compose内容随View生命周期销毁;复用预存的Fragment实例时,View重建后Compose内容未重新初始化。

方法一:绑定Fragment生命周期调整Composition策略

将ComposeView的生命周期绑定到Fragment的viewLifecycleOwner,而非ViewTree的生命周期,这样View重建后Compose会自动恢复内容:

修改FragmentMainNav.kt的onViewCreated方法:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    binding.textMain.text = "Fragment Main"

    binding.composeView.apply {
        // 绑定Fragment的View生命周期
        setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner))
        setContent { 
            Text(text = "This is Compose")
        }
    }
}

方法二:在View状态恢复后重新设置Compose内容

如果不想修改策略,可在onViewStateRestored回调中重新设置Compose内容,该回调会在View重建并恢复状态后触发:

在FragmentMainNav.kt中添加:

override fun onViewStateRestored(savedInstanceState: Bundle?) {
    super.onViewStateRestored(savedInstanceState)
    binding.composeView.setContent {
        Text(text = "This is Compose")
    }
}

额外检查

确认自定义BindingFragment在onDestroyView中正确解绑Binding,避免持有旧View引用导致的异常。


内容的提问来源于stack exchange,提问作者Dery Sudrajat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:40:55