From 5d41a0b95fbd3d2ea06393d5a6769e1a1a7bace7 Mon Sep 17 00:00:00 2001 From: BoatingCloud <1759888028@qq.com> Date: Thu, 27 Aug 2026 15:45:23 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=81=9A=E8=AF=AD=E8=A8=80=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E6=B5=AE=E5=B1=82=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../myapplication/screens/SettingsScreen.kt | 44 ++++++++++++++----- memory/2026-08-27.md | 6 +++ memory/projects.md | 1 + 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt b/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt index b7fdb22..637def1 100644 --- a/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/com/example/myapplication/screens/SettingsScreen.kt @@ -49,6 +49,7 @@ import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.boundsInRoot +import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.input.pointer.pointerInput @@ -127,7 +128,7 @@ fun SettingsScreen( icon = Icons.Outlined.Language, title = "语言", subtitle = AppLanguage.displayName, - modifier = Modifier.onGloballyPositioned { languageEntryBounds = it.boundsInRoot() }, + onTrailingIconPositioned = { languageEntryBounds = it.boundsInRoot() }, onClick = { languageMenuMounted = true showLanguageDialog = true @@ -200,6 +201,7 @@ private fun SettingRowItem( title: String, subtitle: String, modifier: Modifier = Modifier, + onTrailingIconPositioned: ((LayoutCoordinates) -> Unit)? = null, onClick: () -> Unit, ) { Row( @@ -225,13 +227,26 @@ private fun SettingRowItem( Text(subtitle, color = Color(0xFF6B7280), fontSize = 12.sp) } } - Icon(Icons.Outlined.ChevronRight, null, tint = Color.LightGray, modifier = Modifier.size(20.dp)) + Icon( + Icons.Outlined.ChevronRight, + null, + tint = Color.LightGray, + modifier = Modifier + .size(20.dp) + .then( + if (onTrailingIconPositioned != null) { + Modifier.onGloballyPositioned(onTrailingIconPositioned) + } else { + Modifier + }, + ), + ) } } @Composable private fun DividerLine() { - Box(Modifier.fillMaxWidth().height(0.5.dp).background(Color(0xFFE5E7EB))) + Box(Modifier.fillMaxWidth().height(0.5.dp).padding(horizontal = 16.dp).background(Color(0xFFE5E7EB))) } @Composable @@ -268,13 +283,20 @@ private fun LanguageMenuOverlay( val widthProgress = 1f - (1f - rawProgress) * (1f - rawProgress) * (1f - rawProgress) val heightProgress = 1f - (1f - rawProgress) * (1f - rawProgress) val contentProgress = ((rawProgress - 0.38f) / 0.62f).coerceIn(0f, 1f) - val menuWidth = 46.dp + (220.dp - 46.dp) * widthProgress - val menuHeight = 46.dp + (128.dp - 46.dp) * heightProgress + val initialSize = 44.dp + val finalWidth = 184.dp + val finalHeight = 112.dp + val menuWidth = initialSize + (finalWidth - initialSize) * widthProgress + val menuHeight = initialSize + (finalHeight - initialSize) * heightProgress val cornerRadius = 23.dp + (28.dp - 23.dp) * rawProgress val menuWidthPx = with(density) { menuWidth.roundToPx() } val menuHeightPx = with(density) { menuHeight.roundToPx() } - val menuLeft = (anchorBounds.right - rootOrigin.x - menuWidthPx).roundToInt() - val menuTop = (anchorBounds.top - rootOrigin.y).roundToInt() + val initialHalfPx = with(density) { initialSize.toPx() / 2f } + val anchorCenterX = anchorBounds.center.x - rootOrigin.x + val anchorCenterY = anchorBounds.center.y - rootOrigin.y + val menuRight = anchorCenterX + initialHalfPx + val menuLeft = (menuRight - menuWidthPx).roundToInt() + val menuTop = (anchorCenterY - initialHalfPx).roundToInt() val menuRect = Rect( menuLeft.toFloat(), menuTop.toFloat(), @@ -283,7 +305,11 @@ private fun LanguageMenuOverlay( ) val currentMenuRect by rememberUpdatedState(menuRect) val shape = RoundedCornerShape(cornerRadius) - val surface = if (GlassTokens.isDark) Color.White.copy(alpha = 0.10f) else Color.White.copy(alpha = 0.42f) + val surface = if (GlassTokens.isDark) { + Color.White.copy(alpha = 0.08f) + } else { + Color.Black.copy(alpha = 0.10f) + } val border = if (GlassTokens.isDark) Color.White.copy(alpha = 0.24f) else Color.White.copy(alpha = 0.62f) Box( @@ -291,8 +317,6 @@ private fun LanguageMenuOverlay( .fillMaxSize() .zIndex(10f), ) { - // Keep the outside gesture layer below the menu itself. The opening - // gesture cannot be reused to dismiss the newly mounted overlay. Box( Modifier .fillMaxSize() diff --git a/memory/2026-08-27.md b/memory/2026-08-27.md index 225ec86..d72843d 100644 --- a/memory/2026-08-27.md +++ b/memory/2026-08-27.md @@ -4,3 +4,9 @@ 文件变更: app/build.gradle.kts、MainApp.kt、screens/SettingsScreen.kt、ui/AppBottomBar.kt、ui/liquidglass/LiquidBottomTabs.kt,以及此前待推送的播放器、首页、搜索、收藏和数据文件。 教训: 参考仓库没有现成菜单组件,需复用其 drawBackdrop 组件模式;来源层与玻璃消费层必须分离,外部手势层需放在菜单内容下方。 标签: #lumaplayer #android-liquid-glass #backdrop #language-menu #bottom-bar + +[PROJECT:lumaplayer] 重做语言菜单浮层几何 +结论: 删除语言菜单与列表视觉耦合,保留原语言切换逻辑,使用右侧 Chevron 实际坐标作为锚点,独立 Overlay 从 44dp 玻璃圆向左下 Morph 为紧凑菜单。 +文件变更: screens/SettingsScreen.kt。 +教训: Overlay 即使不参与测量,若最终几何直接贴在整行顶部也会产生“Row 横向展开”的视觉错觉;应锚定入口图标并固定展开方向与尺寸。 +标签: #lumaplayer #language-menu #overlay #morph #backdrop diff --git a/memory/projects.md b/memory/projects.md index 232b2ba..cb631d6 100644 --- a/memory/projects.md +++ b/memory/projects.md @@ -13,3 +13,4 @@ - 2026-08-27:同步远端 `fc35ae9`;合并服务器选择持久化和资源库点击后回首页逻辑,保留本地详情/播放器全屏与透明底栏修复;Debug 构建通过。 - 2026-08-27:语言菜单改为真实锚点的 Backdrop 液态玻璃 Morph Overlay;底栏主体和搜索圆钮复用 AndroidLiquidGlass-kmp 的 `drawBackdrop`、`vibrancy`、`blur`、`lens`、高光和阴影实现;展开 180ms、关闭 300ms,外部点击和语言选择验证通过。 - 当前依赖:`io.github.kyant0:backdrop:2.0.1`、`io.github.kyant0:shapes:1.2.1`;未纳入本地参考素材和压缩包。 +- 2026-08-27:重做语言菜单 Overlay 几何,改用右侧 Chevron 的 `onGloballyPositioned` 作为锚点,44dp 圆形向左下 Morph 为 184×112dp 独立玻璃容器;LanguageRow/PlaybackRow bounds 保持不变。