feat: 月视图年月支持日期选择器跳转

- MonthHeader 新增 onYearMonthClick 回调,点击年月文字打开日期选择器
- CalendarMonthView 集成 DatePickerDialog,选择日期后调用 selectDate
- 复用现有 CalendarPager 动画,跨月跳转时自动平滑翻页

同时包含之前的改动:
- 临时隐藏 SplashActivity 入口,改由 MainActivity 作为 LAUNCHER
- benchmark build type 关闭 R8 混淆与资源压缩,保证 profile 可读性
This commit is contained in:
xfy 2026-06-16 14:19:41 +08:00
parent 85bfba241c
commit a731507b3b
4 changed files with 79 additions and 9 deletions

View File

@ -54,9 +54,11 @@ android {
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += listOf("release") matchingFallbacks += listOf("release")
// isDebuggable=false 使 macrobenchmark 在模拟器上稳定运行,且 Partial 编译模式可用。 // isDebuggable=false 使 macrobenchmark 在模拟器上稳定运行,且 Partial 编译模式可用。
// 代价是生成的 baseline-prof.txt / startup-prof.txt 会包含 R8 混淆后的类名;功能上仍然有效。 // 关闭混淆,保证生成的 baseline-prof.txt / startup-prof.txt 使用原始类名,
// 若需要可人工维护的可读 profile请在真机上使用 debuggable build 或引入 Baseline Profile Gradle plugin // 避免 R8 混淆签名导致 profile 匹配与维护风险
isDebuggable = false isDebuggable = false
isMinifyEnabled = false
isShrinkResources = false
} }
} }

View File

@ -9,20 +9,26 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.YaYa"> android:theme="@style/Theme.YaYa">
<!--
SplashActivity 已临时禁用,不再作为应用入口。
如需恢复启动页,将下面的 MAIN/LAUNCHER intent-filter 从 MainActivity
移回 SplashActivity 即可。
-->
<activity <activity
android:name=".SplashActivity" android:name=".SplashActivity"
android:exported="true" android:exported="true"
android:theme="@style/Theme.YaYa.Splash"> android:theme="@style/Theme.YaYa.Splash" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".MainActivity"
android:exported="true" />
<activity <activity
android:name=".AboutActivity" android:name=".AboutActivity"
android:exported="false" /> android:exported="false" />

View File

@ -1,3 +1,5 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package plus.rua.project.ui package plus.rua.project.ui
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
@ -42,11 +44,16 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Menu import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@ -74,9 +81,12 @@ import kotlinx.coroutines.launch
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import kotlinx.datetime.Month import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.number import kotlinx.datetime.number
import kotlinx.datetime.plus import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime
import kotlinx.datetime.todayIn import kotlinx.datetime.todayIn
import kotlin.time.Instant
import plus.rua.project.CalendarViewModel import plus.rua.project.CalendarViewModel
import plus.rua.project.ShiftKind import plus.rua.project.ShiftKind
import plus.rua.project.composeTraceBeginSection import plus.rua.project.composeTraceBeginSection
@ -125,6 +135,7 @@ fun CalendarMonthView(
var rowHeightPx by remember { mutableIntStateOf(0) } var rowHeightPx by remember { mutableIntStateOf(0) }
var screenWidthPx by remember { mutableIntStateOf(0) } var screenWidthPx by remember { mutableIntStateOf(0) }
var isMenuExpanded by remember { mutableStateOf(false) } var isMenuExpanded by remember { mutableStateOf(false) }
var showDatePicker by remember { mutableStateOf(false) }
// 视图切换时自动关闭菜单 // 视图切换时自动关闭菜单
LaunchedEffect(isYearView) { LaunchedEffect(isYearView) {
@ -214,7 +225,8 @@ fun CalendarMonthView(
month = currentMonth, month = currentMonth,
weekNumber = weekNumber, weekNumber = weekNumber,
showToday = selectedDate != today, showToday = selectedDate != today,
onToday = onToday onToday = onToday,
onYearMonthClick = { showDatePicker = true }
) )
WeekdayHeader( WeekdayHeader(
modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp) modifier = Modifier.fillMaxWidth().padding(bottom = ROW_PADDING_DP.dp)
@ -439,6 +451,34 @@ fun CalendarMonthView(
} }
} }
} }
if (showDatePicker) {
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = selectedDate.toEpochMillis()
)
DatePickerDialog(
onDismissRequest = { showDatePicker = false },
confirmButton = {
TextButton(
onClick = {
datePickerState.selectedDateMillis?.let { millis ->
viewModel.selectDate(millis.toLocalDate())
}
showDatePicker = false
}
) {
Text("确定")
}
},
dismissButton = {
TextButton(onClick = { showDatePicker = false }) {
Text("取消")
}
}
) {
DatePicker(state = datePickerState)
}
}
} }
} }
@ -611,3 +651,19 @@ private fun MenuItem(
) )
} }
} }
/**
* [LocalDate] 转换为 UTC 午夜的 epoch 毫秒
*
* 用于 DatePicker 初始选中值 [Long.toLocalDate] 成对使用
*/
private fun LocalDate.toEpochMillis(): Long =
this.atStartOfDayIn(TimeZone.UTC).toEpochMilliseconds()
/**
* epoch 毫秒转换为 UTC 日期的 [LocalDate]
*
* DatePicker 返回选中日期的 UTC 午夜毫秒经此函数得到本地逻辑日期
*/
private fun Long.toLocalDate(): LocalDate =
Instant.fromEpochMilliseconds(this).toLocalDateTime(TimeZone.UTC).date

View File

@ -34,6 +34,7 @@ import androidx.compose.ui.unit.sp
* @param weekNumber 当前 ISO 周号 * @param weekNumber 当前 ISO 周号
* @param showToday 是否显示今天按钮 selectedDate today * @param showToday 是否显示今天按钮 selectedDate today
* @param onToday 点击今天按钮跳转今天 * @param onToday 点击今天按钮跳转今天
* @param onYearMonthClick 点击"年月"文字打开日期选择器
* @param modifier 外部布局修饰符 * @param modifier 外部布局修饰符
*/ */
@Composable @Composable
@ -43,6 +44,7 @@ fun MonthHeader(
weekNumber: Int, weekNumber: Int,
showToday: Boolean, showToday: Boolean,
onToday: (() -> Unit)? = null, onToday: (() -> Unit)? = null,
onYearMonthClick: () -> Unit = {},
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Row( Row(
@ -66,7 +68,11 @@ fun MonthHeader(
Text( Text(
text = "${y}${m}", text = "${y}${m}",
color = MaterialTheme.colorScheme.onBackground, color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge,
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.clickable(onClick = onYearMonthClick)
.padding(horizontal = 4.dp, vertical = 2.dp)
) )
} }
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))