From c7d1e62aa2a1ac678b9b6ad9e85a88f962403f9f Mon Sep 17 00:00:00 2001 From: meyou <2636699780@qq.com> Date: Mon, 18 May 2026 23:24:04 +0800 Subject: [PATCH] =?UTF-8?q?MiniMonth=20=E7=BA=AF=20Canvas=20=E7=BB=98?= =?UTF-8?q?=E5=88=B6=EF=BC=9A=E6=B6=88=E9=99=A4=2096=20=E4=B8=AA=20Text=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84=E6=B5=8B=E9=87=8F=E5=BC=80=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trace 显示 TextStringSimpleNode::measure 累计 100+ms,是 measure 阶段 主要开销。MiniMonth 中 12 个标题 Text + 84 个星期标签 Text = 96 个 Text 组件。 修复: 1. YearGridView 中预计算 titleLayouts + weekdayLayouts 2. MiniMonth 改为单个 Canvas 统一绘制标题 + 星期行 + 日期 3. 消除所有 Text 组件,TextStringSimpleNode::measure 归零 --- .../plus/rua/project/ui/YearGridView.kt | 105 +++++++++++------- 1 file changed, 67 insertions(+), 38 deletions(-) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt index 9ca71ca..b84e7ad 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/YearGridView.kt @@ -26,12 +26,10 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.TextMeasurer import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.drawText import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.datetime.DatePeriod @@ -107,6 +105,30 @@ fun YearGridView( }.toMap() } + // P0-H: 预测量月份标题(选中/非选中两种颜色) + val titleLayouts = remember(textMeasurer, colors) { + (1..12).flatMap { month -> + val text = "${month}月" + listOf( + (month to true) to textMeasurer.measure( + text, + TextStyle(fontSize = 10.sp, color = colors.titleSelected, fontWeight = FontWeight.Bold) + ), + (month to false) to textMeasurer.measure( + text, + TextStyle(fontSize = 10.sp, color = colors.titleNormal) + ) + ) + }.toMap() + } + + // P0-H: 预测量星期标签 + val weekdayLayouts = remember(textMeasurer, colors) { + WEEKDAY_LABELS.associate { label -> + label to textMeasurer.measure(label, TextStyle(fontSize = 8.sp, color = colors.weekday)) + } + } + Column( modifier = modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally @@ -132,9 +154,9 @@ fun YearGridView( today = today, days = monthDays[month - 1], colors = colors, - textMeasurer = textMeasurer, - dayTextStyle = dayTextStyle, dayLayouts = dayLayouts, + titleLayouts = titleLayouts, + weekdayLayouts = weekdayLayouts, onClick = { onMonthClick(month) }, modifier = Modifier.weight(1f) ) @@ -147,7 +169,9 @@ fun YearGridView( } /** - * 精简版月历:月份标题 + 星期行 + 日期数字网格。 + * 精简版月历:月份标题 + 星期行 + 日期数字网格,全部 Canvas 绘制。 + * + * 消除 Text 组件避免 TextStringSimpleNode::measure 开销。 */ @Composable private fun MiniMonth( @@ -156,13 +180,20 @@ private fun MiniMonth( today: LocalDate, days: List, colors: MiniMonthColors, - textMeasurer: TextMeasurer, - dayTextStyle: TextStyle, dayLayouts: Map, androidx.compose.ui.text.TextLayoutResult>, + titleLayouts: Map, androidx.compose.ui.text.TextLayoutResult>, + weekdayLayouts: Map, onClick: () -> Unit, modifier: Modifier = Modifier ) { - val titleColor = if (isSelected) colors.titleSelected else colors.titleNormal + val density = LocalDensity.current + val dayRowCount = days.size / 7 + val titleHeightPx = with(density) { 14.sp.toPx() } + val weekdayHeightPx = with(density) { 12.sp.toPx() } + val dayCellHeightPx = with(density) { (12.sp.toPx() + 4.dp.toPx()) } + val totalHeight = with(density) { + (titleHeightPx + weekdayHeightPx + dayRowCount * dayCellHeightPx).toDp() + } Column( modifier = modifier @@ -171,42 +202,40 @@ private fun MiniMonth( .padding(vertical = 2.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - // 月份标题 - Text( - text = "${month}月", - color = titleColor, - fontSize = 10.sp, - fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal, - textAlign = TextAlign.Center - ) - // 星期行 - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly - ) { - WEEKDAY_LABELS.forEach { label -> - Text( - text = label, - color = colors.weekday, - fontSize = 8.sp, - textAlign = TextAlign.Center, - modifier = Modifier.weight(1f) + Canvas(modifier = Modifier.fillMaxWidth().height(totalHeight)) { + val cellWidth = size.width / 7f + + // 1. 绘制标题 + val titleLayout = titleLayouts[month to isSelected]!! + drawText( + textLayoutResult = titleLayout, + topLeft = Offset( + (size.width - titleLayout.size.width) / 2f, + 0f + ) + ) + + // 2. 绘制星期行 + val weekdayY = titleHeightPx + WEEKDAY_LABELS.forEachIndexed { i, label -> + val layout = weekdayLayouts[label]!! + drawText( + textLayoutResult = layout, + topLeft = Offset( + i * cellWidth + (cellWidth - layout.size.width) / 2f, + weekdayY + (weekdayHeightPx - layout.size.height) / 2f + ) ) } - } - // 日期网格 — Canvas 绘制 - val density = LocalDensity.current - val dayRowCount = days.size / 7 - val canvasHeight = with(density) { (dayRowCount * (12.sp.toPx() + 4.dp.toPx())).toDp() } - Canvas(modifier = Modifier.fillMaxWidth().height(canvasHeight)) { - val cellWidth = size.width / 7f - val rowHeightPx = size.height / dayRowCount + + // 3. 绘制日期网格 + val dayGridY = titleHeightPx + weekdayHeightPx days.forEachIndexed { index, dayData -> val row = index / 7 val col = index % 7 val centerX = col * cellWidth + cellWidth / 2f - val centerY = row * rowHeightPx + rowHeightPx / 2f + val centerY = dayGridY + row * dayCellHeightPx + dayCellHeightPx / 2f val isToday = dayData.date == today && dayData.isCurrentMonth val dayNum = if (dayData.isCurrentMonth) dayData.date.day else 0 @@ -217,7 +246,7 @@ private fun MiniMonth( } if (isToday) { - val radius = cellWidth.coerceAtMost(rowHeightPx) / 2f * 0.8f + val radius = cellWidth.coerceAtMost(dayCellHeightPx) / 2f * 0.8f drawCircle( color = colors.todayBg, radius = radius,