Compose Multiplatform Android端Text字体切换不即时更新问题
问题现象
开发的阅读器应用支持字号、颜色、字体切换,基于Compose Multiplatform 1.6.10、Kotlin 2.0.0和Compose Resources技术栈开发。iOS端字体切换功能正常,但Android端存在异常:点击切换字体后,Text组件虽触发重组,但屏幕上字体无即时变化,只有当Text滚出屏幕再返回时,才会显示新字体。已覆盖debug/release版本、不同设备及Android SDK级别完成测试。
核心实现代码如下:
data class Style(val color: Color = Color.Black, val fontSize: Float = 14f, val fontFamily: FontResource = Res.font.rubik_regular) @Composable fun TestScreen() { var styleState by remember { mutableStateOf(Style()) } Column(modifier = Modifier.background(Color.White)) { LazyColumn(modifier = Modifier.weight(1.0f), verticalArrangement = Arrangement.spacedBy(24.dp)) { for (i in 0..100) { item { Text( staticText, style = style.copy(color = styleState.color, fontSize = styleState.fontSize.sp, fontFamily = FontFamily(Font(styleState.fontFamily, FontWeight.Normal))) ) } } } // 简化部分代码 Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { Text( modifier = Modifier.padding(16.dp).clickable { styleState = styleState.copy(fontFamily = Res.font.rubik_regular) }, text = "roboto", style = style ) Text( modifier = Modifier.padding(16.dp).clickable { styleState = styleState.copy(fontFamily = Res.font.HelveticaNeueMedium) }, text = "helvetica", style = style ) Text( modifier = Modifier.padding(16.dp).clickable { styleState = styleState.copy(fontFamily = Res.font.COMICSANS) }, text = "Comic Sans", style = style ) } } }
问题根源
Android端Compose对FontResource的重组感知存在延迟,直接在Text的style参数中创建FontFamily(Font(styleState.fontFamily...))时,Compose无法正确识别字体资源的变化,导致当前可见的LazyColumn列表项不会触发重新测量与绘制,只有当列表项被回收复用(滚出屏幕)时,才会重新加载新字体。
修复方案
方案1:将FontFamily包装为可观察State
通过remember结合derivedStateOf,基于styleState.fontFamily创建可观察的FontFamily实例,确保字体变化能触发Compose的正确重组流程:
@Composable fun TestScreen() { var styleState by remember { mutableStateOf(Style()) } // 绑定字体资源状态,创建可观察的FontFamily val fontFamily by remember(styleState.fontFamily) { derivedStateOf { FontFamily(Font(styleState.fontFamily, FontWeight.Normal)) } } Column(modifier = Modifier.background(Color.White)) { LazyColumn(modifier = Modifier.weight(1.0f), verticalArrangement = Arrangement.spacedBy(24.dp)) { items(101) { // 改用items符合LazyColumn最佳实践 Text( staticText, style = style.copy( color = styleState.color, fontSize = styleState.fontSize.sp, fontFamily = fontFamily ) ) } } // 下方字体切换按钮代码保持不变 } }
方案2:为LazyColumn项添加字体绑定的Key
为每个LazyColumn的item添加与字体资源绑定的key,当字体变化时强制列表项重新组合:
LazyColumn(modifier = Modifier.weight(1.0f), verticalArrangement = Arrangement.spacedBy(24.dp)) { for (i in 0..100) { item(key = styleState.fontFamily) { // 绑定字体资源作为key Text( staticText, style = style.copy( color = styleState.color, fontSize = styleState.fontSize.sp, fontFamily = FontFamily(Font(styleState.fontFamily, FontWeight.Normal)) ) ) } } }
方案3:升级Compose Multiplatform版本
部分旧版本的Compose Multiplatform在Android端存在字体资源重组的底层bug,升级到1.7.0及以上版本(需同步适配对应Kotlin版本)可能直接解决该问题。
验证说明
上述方案修复后,点击字体切换按钮,Android端的Text组件会即时更新字体,无需滚动出屏幕。方案1更符合Compose状态管理的最佳实践,方案2则属于轻量化的快速修复方案,可根据项目需求选择。
内容的提问来源于stack exchange,提问作者wojciech_maciejewski

