dm-padding message-13

This commit is contained in:
eric 2026-07-09 16:13:51 +08:00
parent d5911ebde1
commit d682304a3c
2 changed files with 53 additions and 28 deletions

View file

@ -1,7 +1,12 @@
package com.example.login_v3.navigation package com.example.login_v3.navigation
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
@ -34,13 +39,7 @@ import com.example.login_v3.home.Tg_Server
import com.example.login_v3.home.setting.Tg_Setting import com.example.login_v3.home.setting.Tg_Setting
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.login_v3.home.setting.setting_detail_page.viewmodel.Theme_ViewModel import com.example.login_v3.home.setting.setting_detail_page.viewmodel.Theme_ViewModel
import androidx.hilt.navigation.compose.hiltViewModel // 確保有引入 hiltViewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.login_v3.navigation.components.AppWallpaperBackground import com.example.login_v3.navigation.components.AppWallpaperBackground
@Composable @Composable
@ -102,27 +101,43 @@ fun BottomBarAnimated(
height: Dp, height: Dp,
content: @Composable BoxScope.() -> Unit content: @Composable BoxScope.() -> Unit
) { ) {
// 🎯 核心修正:讓實體高度也參與動畫 // 🎯 高性能優化:使用單一 Transition 管理所有狀態,確保動畫同步
// 當 visible 為 false 時,這塊 Box 的高度會縮減到 0,從而讓 Scaffold 重新計算 innerPadding val transition = updateTransition(targetState = visible, label = "BottomBarTransition")
val animatedHeight by animateDpAsState(targetValue = if (visible) height else 0.dp)
// 位移與透明度動畫保持不變 // 使用彈簧動畫 (Spring) 達成更流暢且自然的物理回饋感
val offsetY by animateDpAsState(targetValue = if (visible) 0.dp else height) val progress by transition.animateFloat(
val alpha by animateFloatAsState(targetValue = if (visible) 1f else 0f) transitionSpec = {
if (targetState) {
// 彈出時帶有一點點的回彈感 (LowBouncy)
spring(dampingRatio = Spring.DampingRatioLowBouncy, stiffness = Spring.StiffnessLow)
} else {
// 隱藏時則較為快速乾脆
spring(dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessMediumLow)
}
},
label = "progress"
) { state -> if (state) 1f else 0f }
val alpha by transition.animateFloat(
transitionSpec = { tween(durationMillis = 250) },
label = "alpha"
) { state -> if (state) 1f else 0f }
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(animatedHeight) // 關鍵:動態高度決定了內容區域下方的留白 .height(height * progress) // 只有高度變化會觸發 Layout (為了回收空間)
.graphicsLayer { this.alpha = alpha } .graphicsLayer { this.alpha = alpha }
.background(Color.Transparent) .background(Color.Transparent)
) { ) {
// 內部的內容容器則維持原高度,並配合 offset 達成滑出效果 // 🚀 關鍵優化:內部內容使用 translationY,這是 GPU 加速的位移,不會觸發 Layout Pass
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(height) .height(height)
.offset(y = offsetY) .graphicsLayer {
translationY = (height.toPx() * (1f - progress))
}
) { ) {
content() content()
} }

View file

@ -45,6 +45,10 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.yield
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -171,21 +175,27 @@ fun HorizontalWheelPicker(
val isInteracting by remember { derivedStateOf { listState.isScrollInProgress } } val isInteracting by remember { derivedStateOf { listState.isScrollInProgress } }
// 使用 layoutInfo.visibleItemsInfo 計算最接近中心的 item // 🎯 性能優化:只有當「中心項」真正改變時,才觸發更新
LaunchedEffect(listState) { LaunchedEffect(listState) {
snapshotFlow { listState.layoutInfo.visibleItemsInfo } snapshotFlow {
.filter { it.isNotEmpty() } val layoutInfo = listState.layoutInfo
.collect { visibleItems -> val visibleItems = layoutInfo.visibleItemsInfo
val viewportCenter = if (visibleItems.isEmpty()) return@snapshotFlow -1
(listState.layoutInfo.viewportStartOffset + listState.layoutInfo.viewportEndOffset) / 2
val viewportCenter = (layoutInfo.viewportStartOffset + layoutInfo.viewportEndOffset) / 2
// 找到離中心最近的 Item
val closest = visibleItems.minByOrNull { vi -> val closest = visibleItems.minByOrNull { vi ->
kotlin.math.abs((vi.offset + vi.size / 2) - viewportCenter) kotlin.math.abs((vi.offset + vi.size / 2) - viewportCenter)
} ?: visibleItems.first() } ?: visibleItems.first()
val selectedIndex = closest.key as Int closest.key as Int
}
.filter { it != -1 }
.distinctUntilChanged() // 關鍵:只有 Index 改變才往下走
.collectLatest { selectedIndex ->
yield() // 給予 UI 線程呼吸空間,防止快速滑動時卡頓
onValueChange(items[selectedIndex]) onValueChange(items[selectedIndex])
} }
} }