与 Jetpack 导航一起使用时,TextField 会中断组合?

问题描述

我一直在尝试使用 Compose 1.0.0-beta09、Kotlin 1.5.10 和 Jetpack Navigation 2.3.4 将一个简单的应用程序组合在一起。 该应用有一个活动和两个片段。

一个(主要)片段/屏幕(点击按钮将我带到第二个片段/屏幕):Screen One screenshot

第二个片段:/屏幕:Screen Two screenshot

问题:在与第一个屏幕上的 TextField 交互(将光标置于其中)并随后单击按钮后,第二个屏幕加载但为空(调用了 SecondFragment 的 onCreateView 但 setContent 不起作用/屏幕没有重新组合?)。 如果我不与 TextField 交互,问题就不会发生。 我已经在 API 级别 28 和 30、compose 1.0.0-beta0709、Kotlin 1.4.32 和 1.5.10 的模拟器上进行了测试,结果相似。

Empty Screen Two

主要课程:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AndroidViewBinding(ContentMainBinding::inflate)
        }
    }
}
class FirstFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
    ): View = ComposeView(inflater.context).apply {
        setContent {
            FirstScreen( onButtonClick = {
                findNavController().navigate(R.id.nav_second_fragment)
            })
        }
    }
}
@Composable
fun FirstScreen(onButtonClick: () -> Unit) {
    Column {
        Text("Screen One",color = Color.Blue,fontSize = 30.sp)
        Button(
            onClick = {
                onButtonClick() },content = {
                Text(text = "go to Screen Two",color = Color.White)
            })
        TextField(
            value = "",onValueChange = {},label = { Text(stringResource(R.string.label_enter_value)) },)
    }
}
class SecondFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,savedInstanceState: Bundle?
    ): View = ComposeView(inflater.context).apply {

         setContent {
            Column {
                Text("Screen Two",fontSize = 30.sp)
            }
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.example.composewithnavigation"
        minSdk 28
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinoptions {
        jvmTarget = '1.8'
        useIR = true
    }
    buildFeatures {
        compose true
        viewBinding true
    }
    compoSEOptions {
        kotlinCompilerExtensionVersion compose_version
        kotlinCompilerVersion '1.5.10'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.5.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    implementation "androidx.compose.ui:ui-viewbinding:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.3.0-beta01'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
}

解决方法

据我所知,Compose 中不鼓励使用 Fragment 进行导航。您应该创建多个屏幕,例如此处的 FirstScreen Composable。无论如何,我认为没有调用 setContent 的原因是它是从第一个片段的另一个 setContent 中调用的。在 setContent 中,您进行导航调用,将其重定向到第二个片段,该片段再次调用 setContent。因为第二个片段已经显示在 FirstScreen Composable 的 setContent 中,您实际上是在嵌套它, compose 可能不支持它,(我的意思是,有道理)。

这就是为什么我们建议放弃 Fragment 并使用 Composables 代替,这不需要显式调用 setContent