From fa61bcca38396df531680314114f41216a90e1a7 Mon Sep 17 00:00:00 2001 From: zhushenwudi <55681140@163.com> Date: Sat, 3 Oct 2026 23:20:16 +0800 Subject: [PATCH] optimize transfer ui --- .../ui/components/DriveModeCarousel.kt | 6 +- .../ui/components/PairingPageLayout.kt | 43 +++++---- .../ui/screens/DrawerFeatureScreens.kt | 87 ++++++++++++------- 3 files changed, 86 insertions(+), 50 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/DriveModeCarousel.kt b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/DriveModeCarousel.kt index 6a208eb..c15bd9a 100644 --- a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/DriveModeCarousel.kt +++ b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/DriveModeCarousel.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.distinctUntilChanged import com.zhushenwudi.lovelivemusicplayer.domain.Music @@ -88,12 +89,13 @@ fun DriveModeCarousel( } } -/** 驾驶模式与歌曲快传共用的封面 Pager,保证两处运动效果完全一致。 */ +/** 共用封面 Pager;传输页可关闭驾驶模式的纵向偏移,让封面中心水平对齐。 */ @Composable internal fun CoverCarouselPager( state: PagerState, modifier: Modifier = Modifier, userScrollEnabled: Boolean = true, + verticalOffset: Dp = (-40).dp, pageContent: @Composable (Int) -> Unit, ) { BoxWithConstraints(modifier.fillMaxSize()) { @@ -124,7 +126,7 @@ internal fun CoverCarouselPager( scaleX = scale scaleY = scale this.alpha = alpha - translationY = scale * -40.dp.toPx() + translationY = scale * verticalOffset.toPx() }, contentAlignment = Alignment.Center, ) { diff --git a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/PairingPageLayout.kt b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/PairingPageLayout.kt index e8c9c01..c2bad44 100644 --- a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/PairingPageLayout.kt +++ b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/components/PairingPageLayout.kt @@ -32,7 +32,8 @@ import com.zhushenwudi.lovelivemusicplayer.ui.theme.LocalIsLandscape internal fun PairingPageLayout( modifier: Modifier = Modifier, illustration: @Composable (Dp) -> Unit, - content: @Composable ColumnScope.() -> Unit, + landscapeHeader: (@Composable () -> Unit)? = null, + content: @Composable ColumnScope.(isSplitLayout: Boolean) -> Unit, ) { val landscape = LocalIsLandscape.current BoxWithConstraints( @@ -41,23 +42,29 @@ internal fun PairingPageLayout( ), ) { if (landscape && maxWidth >= 560.dp) { - val viewportHeight = maxHeight - val illustrationSize = minOf(maxHeight * 0.8f, maxWidth * 0.4f, 280.dp) - Row(Modifier.fillMaxSize().padding(horizontal = 24.dp)) { - Box( - Modifier.weight(1f).fillMaxHeight(), - contentAlignment = Alignment.Center, - ) { - illustration(illustrationSize) + Column(Modifier.fillMaxSize()) { + landscapeHeader?.invoke() + BoxWithConstraints(Modifier.weight(1f).fillMaxWidth()) { + val viewportHeight = maxHeight + val illustrationSize = minOf(maxHeight * 0.8f, maxWidth * 0.4f, 280.dp) + Row(Modifier.fillMaxSize().padding(horizontal = 24.dp)) { + Box( + Modifier.weight(1f).fillMaxHeight(), + contentAlignment = Alignment.Center, + ) { + illustration(illustrationSize) + } + Column( + Modifier.weight(1f).fillMaxHeight() + .verticalScroll(rememberScrollState()) + .heightIn(min = viewportHeight).padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + content(true) + } + } } - Column( - Modifier.weight(1f).fillMaxHeight() - .verticalScroll(rememberScrollState()) - .heightIn(min = viewportHeight).padding(24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - content = content, - ) } } else { val illustrationSize = minOf(maxWidth - 48.dp, 343.dp).coerceAtLeast(1.dp) @@ -70,7 +77,7 @@ internal fun PairingPageLayout( illustration(illustrationSize) } Spacer(Modifier.height(32.dp)) - content() + content(false) Spacer(Modifier.height(24.dp)) } } diff --git a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/screens/DrawerFeatureScreens.kt b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/screens/DrawerFeatureScreens.kt index 0ddab70..c1d2f39 100644 --- a/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/screens/DrawerFeatureScreens.kt +++ b/composeApp/src/commonMain/kotlin/com/zhushenwudi/lovelivemusicplayer/ui/screens/DrawerFeatureScreens.kt @@ -80,6 +80,8 @@ import org.jetbrains.compose.resources.painterResource import com.zhushenwudi.lovelivemusicplayer.AppContainer import com.zhushenwudi.lovelivemusicplayer.Const import com.zhushenwudi.lovelivemusicplayer.generated.resources.Res +import com.zhushenwudi.lovelivemusicplayer.generated.resources.llmp_logo_dark +import com.zhushenwudi.lovelivemusicplayer.generated.resources.llmp_logo_light import com.zhushenwudi.lovelivemusicplayer.generated.resources.logo_logo import com.zhushenwudi.lovelivemusicplayer.lyric.LoadedLyric import com.zhushenwudi.lovelivemusicplayer.lyric.LrcParser @@ -504,34 +506,23 @@ fun TransferScreen(onBack: () -> Unit, onOpenScan: () -> Unit) { }, ) } else { - SvgIcon(asset = SvgAsset("drawable/sync_icon_data_sync.svg"), size = minOf(size, 300.dp)) + TransferDefaultLogo( + Modifier.size(minOf(size, 300.dp)).clip(RoundedCornerShape(24.dp)), + ) } }, - ) { + landscapeHeader = { + if (connected || state.completed) { + TransferSongInfo(displayedSong, displayedArtist) + Spacer(Modifier.height(6.dp)) + } + }, + ) { isSplitLayout -> if (connected || state.completed) { - Column( - Modifier.height(60.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - if (displayedSong.isNotBlank()) { - Text( - displayedSong, - color = if (dark) ColorMs.White else ColorMs.Text333, - fontSize = hSp(15f), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - Text( - displayedArtist, - color = ColorMs.Gray999, - fontSize = hSp(12f), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } + if (!isSplitLayout) { + TransferSongInfo(displayedSong, displayedArtist) + Spacer(Modifier.height(24.dp)) } - Spacer(Modifier.height(24.dp)) TransferProgressRing( progress = state.progress, total = state.total, @@ -566,6 +557,44 @@ fun TransferScreen(onBack: () -> Unit, onOpenScan: () -> Unit) { } } +@Composable +private fun TransferSongInfo(song: String, artist: String) { + val dark = LocalDarkTheme.current + Column( + Modifier.fillMaxWidth().height(60.dp).padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + if (song.isNotBlank()) { + Text( + song, + color = if (dark) ColorMs.White else ColorMs.Text333, + fontSize = hSp(15f), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + Text( + artist, + color = ColorMs.Gray999, + fontSize = hSp(12f), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } +} + +@Composable +private fun TransferDefaultLogo(modifier: Modifier = Modifier) { + val dark = LocalDarkTheme.current + Image( + painter = painterResource(if (dark) Res.drawable.llmp_logo_dark else Res.drawable.llmp_logo_light), + contentDescription = null, + contentScale = ContentScale.Fit, + modifier = modifier, + ) +} + @Composable private fun KeepScreenOn() { DisposableEffect(Unit) { @@ -636,6 +665,7 @@ private fun TransferCoverBlock( CoverCarouselPager( state = pagerState, userScrollEnabled = false, + verticalOffset = 0.dp, modifier = Modifier .fillMaxWidth() .height(size), @@ -677,13 +707,10 @@ private fun TransferCoverCircle( contentAlignment = Alignment.Center, ) { // 默认图始终垫在底层,即使图片请求重建或超时也不会露出白色空帧。 - Image( - painter = painterResource(Res.drawable.logo_logo), - contentDescription = null, - contentScale = ContentScale.Fit, - modifier = Modifier + TransferDefaultLogo( + Modifier .fillMaxSize() - .padding(36.dp), + .padding(size * 0.15f), ) if (cover.url.isBlank() || loadFailed) { LaunchedEffect(cover.key, loadFailed) { latestOnReady() }