Merge pull request #4 from xunrua/main
MiniMonth 纯 Canvas 绘制:消除 96 个 Text 组件的测量开销
This commit is contained in:
commit
3a0a4f885a
@ -26,12 +26,10 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.text.TextMeasurer
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.drawText
|
import androidx.compose.ui.text.drawText
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import kotlinx.datetime.DatePeriod
|
import kotlinx.datetime.DatePeriod
|
||||||
@ -107,6 +105,30 @@ fun YearGridView(
|
|||||||
}.toMap()
|
}.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(
|
Column(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
@ -132,9 +154,9 @@ fun YearGridView(
|
|||||||
today = today,
|
today = today,
|
||||||
days = monthDays[month - 1],
|
days = monthDays[month - 1],
|
||||||
colors = colors,
|
colors = colors,
|
||||||
textMeasurer = textMeasurer,
|
|
||||||
dayTextStyle = dayTextStyle,
|
|
||||||
dayLayouts = dayLayouts,
|
dayLayouts = dayLayouts,
|
||||||
|
titleLayouts = titleLayouts,
|
||||||
|
weekdayLayouts = weekdayLayouts,
|
||||||
onClick = { onMonthClick(month) },
|
onClick = { onMonthClick(month) },
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
@ -147,7 +169,9 @@ fun YearGridView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 精简版月历:月份标题 + 星期行 + 日期数字网格。
|
* 精简版月历:月份标题 + 星期行 + 日期数字网格,全部 Canvas 绘制。
|
||||||
|
*
|
||||||
|
* 消除 Text 组件避免 TextStringSimpleNode::measure 开销。
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun MiniMonth(
|
private fun MiniMonth(
|
||||||
@ -156,13 +180,20 @@ private fun MiniMonth(
|
|||||||
today: LocalDate,
|
today: LocalDate,
|
||||||
days: List<MiniDayData>,
|
days: List<MiniDayData>,
|
||||||
colors: MiniMonthColors,
|
colors: MiniMonthColors,
|
||||||
textMeasurer: TextMeasurer,
|
|
||||||
dayTextStyle: TextStyle,
|
|
||||||
dayLayouts: Map<Pair<Int, Color>, androidx.compose.ui.text.TextLayoutResult>,
|
dayLayouts: Map<Pair<Int, Color>, androidx.compose.ui.text.TextLayoutResult>,
|
||||||
|
titleLayouts: Map<Pair<Int, Boolean>, androidx.compose.ui.text.TextLayoutResult>,
|
||||||
|
weekdayLayouts: Map<String, androidx.compose.ui.text.TextLayoutResult>,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
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(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@ -171,42 +202,40 @@ private fun MiniMonth(
|
|||||||
.padding(vertical = 2.dp),
|
.padding(vertical = 2.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
// 月份标题
|
Canvas(modifier = Modifier.fillMaxWidth().height(totalHeight)) {
|
||||||
Text(
|
val cellWidth = size.width / 7f
|
||||||
text = "${month}月",
|
|
||||||
color = titleColor,
|
// 1. 绘制标题
|
||||||
fontSize = 10.sp,
|
val titleLayout = titleLayouts[month to isSelected]!!
|
||||||
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal,
|
drawText(
|
||||||
textAlign = TextAlign.Center
|
textLayoutResult = titleLayout,
|
||||||
)
|
topLeft = Offset(
|
||||||
// 星期行
|
(size.width - titleLayout.size.width) / 2f,
|
||||||
Row(
|
0f
|
||||||
modifier = Modifier.fillMaxWidth(),
|
)
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly
|
)
|
||||||
) {
|
|
||||||
WEEKDAY_LABELS.forEach { label ->
|
// 2. 绘制星期行
|
||||||
Text(
|
val weekdayY = titleHeightPx
|
||||||
text = label,
|
WEEKDAY_LABELS.forEachIndexed { i, label ->
|
||||||
color = colors.weekday,
|
val layout = weekdayLayouts[label]!!
|
||||||
fontSize = 8.sp,
|
drawText(
|
||||||
textAlign = TextAlign.Center,
|
textLayoutResult = layout,
|
||||||
modifier = Modifier.weight(1f)
|
topLeft = Offset(
|
||||||
|
i * cellWidth + (cellWidth - layout.size.width) / 2f,
|
||||||
|
weekdayY + (weekdayHeightPx - layout.size.height) / 2f
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// 日期网格 — Canvas 绘制
|
// 3. 绘制日期网格
|
||||||
val density = LocalDensity.current
|
val dayGridY = titleHeightPx + weekdayHeightPx
|
||||||
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
|
|
||||||
|
|
||||||
days.forEachIndexed { index, dayData ->
|
days.forEachIndexed { index, dayData ->
|
||||||
val row = index / 7
|
val row = index / 7
|
||||||
val col = index % 7
|
val col = index % 7
|
||||||
val centerX = col * cellWidth + cellWidth / 2f
|
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 isToday = dayData.date == today && dayData.isCurrentMonth
|
||||||
val dayNum = if (dayData.isCurrentMonth) dayData.date.day else 0
|
val dayNum = if (dayData.isCurrentMonth) dayData.date.day else 0
|
||||||
@ -217,7 +246,7 @@ private fun MiniMonth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isToday) {
|
if (isToday) {
|
||||||
val radius = cellWidth.coerceAtMost(rowHeightPx) / 2f * 0.8f
|
val radius = cellWidth.coerceAtMost(dayCellHeightPx) / 2f * 0.8f
|
||||||
drawCircle(
|
drawCircle(
|
||||||
color = colors.todayBg,
|
color = colors.todayBg,
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user