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

Jetpack Compose复用组件场景下Lazy Column的HWUI过载优化咨询

Jetpack Compose应用HWUI性能红线突破问题优化方案

在物理设备上使用HWUI分析器测试Jetpack Compose应用时,代表性能限制阈值的红线始终被突破。已采用可复用组件,但仍需调整布局优化性能指标。

现有代码实现

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NestedScrollingTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    MyScreen()
                }
            }
        }
    }
}

@Composable
fun MyReusableScaffold(scaffoldTitle: String, scaffoldFab: @Composable () -> Unit,
                       scaffoldContent: @Composable (contentPadding: PaddingValues) -> Unit) {
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()

    Scaffold(
        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = {
            LargeTopAppBar(
                title = {
                    Text(text = scaffoldTitle)
                },
                scrollBehavior = scrollBehavior
            )
        },
        floatingActionButton = {
            scaffoldFab
        },
        content = { contentPadding ->
            scaffoldContent(contentPadding = contentPadding)
        }
    )
}

@Composable
fun <T> ReusableLazyRow(lazyRowItems: Array<T>,
                        lazyRowItemLayout: @Composable (item: T) -> Unit,
                        onClickLazyRowItem: (T) -> Unit
) {
    val listState = rememberLazyListState()

    LazyRow(
        modifier = Modifier
            .fillMaxWidth()
            .height(300.dp),
        state = listState,
        contentPadding = PaddingValues(horizontal = 16.dp)
    ) {
        items(lazyRowItems) { itemR ->
            OutlinedCard(
                modifier = Modifier
                    .height(300.dp)
                    .padding(end = 16.dp)
                    .clickable { onClickLazyRowItem(itemR) },
                shape = RoundedCornerShape(size = 16.dp),
                colors = CardDefaults.cardColors(
                    containerColor = Color.Unspecified
                )
            ) {
                lazyRowItemLayout(itemR)
            }
        }
    }
}

@Composable
fun <T> ReusableLazyColumn(
    header: @Composable () -> Unit,
    lazyColumnItems: Array<T>,
    lazyColumnItemLayout: @Composable (item: T) -> Unit,
    onClickLazyColumnItem: (T) -> Unit
) {
    val listState = rememberLazyListState()

    Box(modifier = Modifier.fillMaxSize()) {
        LazyColumn(
            state = listState,
            contentPadding = PaddingValues(top = 0.dp, bottom = 80.dp)
        ) {
            item { header() }
            item { Spacer(modifier = Modifier.height(24.dp)) }
            items(lazyColumnItems) { itemC ->
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clickable { onClickLazyColumnItem(itemC) }
                ) {
                    lazyColumnItemLayout(itemC)
                }
                // 用drawBehind减少Composable节点
                Spacer(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(1.dp)
                        .drawBehind {
                            drawRect(
                                color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f)
                            )
                        }
                )
            }
        }

        ButtonScrollToTopList(listState = listState)
    }
}

@Composable
fun ButtonScrollToTopList(
    modifier : Modifier = Modifier,
    listState : LazyListState
) {
    val coroutineScope = rememberCoroutineScope()

    val showButton by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex > 0
        }
    }

    AnimatedVisibility(visible = showButton, enter = fadeIn(), exit = fadeOut()) {
        Button(
            modifier = modifier
                .align(Alignment.BottomCenter)
                .padding(bottom = 50.dp, horizontal = 8.dp),
            onClick = {
                coroutineScope.launch {
                    listState.animateScrollToItem(index = 0)
                }
            }
        ) {
            Icon(
                imageVector = Icons.Default.VerticalAlignTop,
                contentDescription = null
            )
            Spacer(modifier = Modifier.width(5.dp))
            Text(stringResource(id = R.string.back_to_top))
        }
    }
}

@Composable
fun MyScreen() {
    MyReusableScaffold(
        scaffoldTitle = "Demo",
        scaffoldFab = { /*TODO*/ },
        scaffoldContent = {
            MyScreenContent(contentPadding = it)
        }
    )
}

@Composable
fun MyScreenContent(
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues()
) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(contentPadding)
    ) {
        ReusableLazyColumn(
            header = {
                ReusableLazyRow(
                    lazyRowItems = rowItems,
                    lazyRowItemLayout = { item ->
                        Column {
                            Box(
                                modifier = Modifier
                                    .background(Color.LightGray)
                                    .fillMaxWidth()
                                    .height(30.dp)
                            )
                            Text(
                                modifier = Modifier.padding(all = 8.dp),
                                text = item.rowItemTitle,
                                style = MaterialTheme.typography.bodyMedium
                            )
                            Text(
                                modifier = Modifier.padding(all = 8.dp),
                                text = item.rowItemDesc,
                                style = MaterialTheme.typography.bodySmall,
                                maxLines = 2,
                                overflow = TextOverflow.Ellipsis
                            )
                        }
                    },
                    onClickLazyRowItem = { /* listenerRowItems */ }
                )
            },
            lazyColumnItems = columnItems,
            lazyColumnItemLayout = { item ->
                Text(
                    text = item.columnItemTitle,
                    style = MaterialTheme.typography.titleLarge,
                    modifier = Modifier
                        .padding(16.dp)
                        .align(Alignment.CenterVertically),
                )
            },
            onClickLazyColumnItem = { /* listenerColumnItems */ }
        )
    }
}

HWUI分析截图

HWUI分析截图

核心优化点说明

  • 修复布局尺寸溢出:ReusableLazyRow的高度设为300dp,内部OutlinedCard同步使用300dp,避免因尺寸溢出导致的额外测量、绘制开销;给LazyRow添加horizontal contentPadding,优化排版同时减少无效布局计算。
  • 减少Composable节点数量:用Modifier.drawBehind绘制分割线替代Divider组件,降低UI树复杂度;移除MyScreenContent中多余的Box嵌套,直接用Column填充全屏。
  • 优化点击区域:将ReusableLazyRow中Column的clickable移到OutlinedCard上,避免嵌套触摸事件处理,减少事件分发开销。
  • 降低重组频率:将可复用组件的lambda参数调整为稳定类型,确保Composable仅在必要时重组;文本组件复用MaterialTheme内置样式,避免重复创建样式实例。
  • 优化长文本处理:给描述文本添加maxLines和overflow限制,减少文本测量的性能消耗。
  • 简化布局范围:ButtonScrollToTopList中移除不必要的fillMaxSize的Box,直接用Modifier.align定位按钮,缩小布局计算范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:54:59