|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 1.2 MiB |
@ -1,170 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
@ -1,30 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 55 KiB |
@ -1,9 +1,12 @@
|
||||
package plus.rua.project
|
||||
|
||||
import android.os.Build
|
||||
import androidx.activity.BackEventCompat
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.PredictiveBackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class AndroidPlatform : Platform {
|
||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
||||
@ -22,19 +25,20 @@ actual fun PredictiveBackHandler(
|
||||
onBack: () -> Unit,
|
||||
onCancel: () -> Unit
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
rememberCoroutineScope()
|
||||
androidx.activity.compose.PredictiveBackHandler(enabled) { progress ->
|
||||
try {
|
||||
progress.collect { backEvent ->
|
||||
onProgress(backEvent.progress)
|
||||
}
|
||||
onBack()
|
||||
} catch (e: CancellationException) {
|
||||
onCancel()
|
||||
// 官方 PredictiveBackHandler — Flow 模式:collect 完成=返回,CancellationException=取消
|
||||
PredictiveBackHandler(enabled = enabled) { progress: Flow<BackEventCompat> ->
|
||||
try {
|
||||
progress.collect { event ->
|
||||
onProgress(event.progress)
|
||||
}
|
||||
onBack()
|
||||
} catch (e: CancellationException) {
|
||||
onCancel()
|
||||
}
|
||||
} else {
|
||||
androidx.activity.compose.BackHandler(enabled = enabled, onBack = onBack)
|
||||
}
|
||||
|
||||
// 降级:部分设备(如 OPPO/ColorOS)不通过 OnBackInvokedCallback 分发返回事件
|
||||
BackHandler(enabled = enabled) {
|
||||
onBack()
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 1.2 MiB |
@ -1,117 +1,203 @@
|
||||
package plus.rua.project
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.GraphicsLayerScope
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import plus.rua.project.ui.AboutScreen
|
||||
import plus.rua.project.ui.CalendarMonthView
|
||||
import plus.rua.project.ui.LicensesScreen
|
||||
import plus.rua.project.ui.lerp
|
||||
|
||||
private enum class Screen { Main, About, Licenses }
|
||||
|
||||
/** 返回手势动画:顶层页面滑出 + 淡出 + 缩小 + 圆角阴影 */
|
||||
private fun GraphicsLayerScope.applyDismissTransform(progress: Float) {
|
||||
// 二次缓动:小幅手势产生更柔和的视觉变化,大幅手势仍达到完整效果
|
||||
val p = progress * progress
|
||||
translationX = p * size.width * 0.5f
|
||||
scaleX = 1f - p * 0.08f
|
||||
scaleY = 1f - p * 0.08f
|
||||
alpha = 1f - p
|
||||
shadowElevation = 32.dp.toPx() * p
|
||||
shape = RoundedCornerShape(28.dp * p)
|
||||
clip = p > 0.01f
|
||||
}
|
||||
|
||||
/** 底层页面缩放:随返回进度从 baseScale 放大到 1.0 */
|
||||
private fun GraphicsLayerScope.applyRevealTransform(
|
||||
progress: Float,
|
||||
forwardProgress: Float,
|
||||
isForwardAnimating: Boolean
|
||||
) {
|
||||
val p = progress * progress
|
||||
val baseScale = 0.92f + 0.08f * p
|
||||
val scale = if (isForwardAnimating) lerp(1f, baseScale, forwardProgress) else baseScale
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
|
||||
/** 前向导航动画:新页面从右侧滑入 */
|
||||
private fun GraphicsLayerScope.applyEnterTransform(progress: Float) {
|
||||
translationX = (1f - progress) * size.width
|
||||
alpha = progress
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用入口 Composable,根据系统主题切换明暗 ColorScheme 并管理页面导航。
|
||||
*
|
||||
* 使用 Box 分层布局替代 AnimatedContent,支持预测性返回手势:
|
||||
* - 底层页面始终组合(状态保持),缩放显现
|
||||
* - 顶层页面在手势期间平滑位移、缩放、圆角、阴影
|
||||
* - 前向导航从右侧滑入,返回导航跟手驱动
|
||||
*/
|
||||
@Composable
|
||||
@Preview(name = "Calendar App")
|
||||
fun App() {
|
||||
var currentScreen by remember { mutableStateOf(Screen.Main) }
|
||||
var backProgress by remember { mutableFloatStateOf(0f) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val handleBack: () -> Unit = {
|
||||
backProgress = 0f
|
||||
when (currentScreen) {
|
||||
Screen.About -> currentScreen = Screen.Main
|
||||
Screen.Licenses -> currentScreen = Screen.About
|
||||
else -> {}
|
||||
val backAnimProgress = remember { Animatable(0f) }
|
||||
val effectiveBackProgress by remember {
|
||||
derivedStateOf { maxOf(backProgress, backAnimProgress.value) }
|
||||
}
|
||||
|
||||
var forwardTarget by remember { mutableStateOf<Screen?>(null) }
|
||||
val forwardProgress = remember { Animatable(1f) }
|
||||
|
||||
var isHandlingBack by remember { mutableStateOf(false) }
|
||||
val handleBack: () -> Unit = lambda@{
|
||||
if (isHandlingBack) return@lambda
|
||||
isHandlingBack = true
|
||||
scope.launch {
|
||||
backAnimProgress.snapTo(backProgress)
|
||||
backProgress = 0f
|
||||
backAnimProgress.animateTo(1f, spring(stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy))
|
||||
currentScreen = when (currentScreen) {
|
||||
Screen.About -> Screen.Main
|
||||
Screen.Licenses -> Screen.About
|
||||
else -> currentScreen
|
||||
}
|
||||
backAnimProgress.animateTo(0f, spring(stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy))
|
||||
isHandlingBack = false
|
||||
}
|
||||
}
|
||||
|
||||
val handleCancel: () -> Unit = {
|
||||
backProgress = 0f
|
||||
scope.launch {
|
||||
backAnimProgress.snapTo(backProgress)
|
||||
backProgress = 0f
|
||||
backAnimProgress.animateTo(0f, spring(stiffness = Spring.StiffnessMediumLow))
|
||||
}
|
||||
}
|
||||
|
||||
val navigateTo: (Screen) -> Unit = { target ->
|
||||
if (forwardTarget == null) {
|
||||
scope.launch {
|
||||
forwardTarget = target
|
||||
currentScreen = target
|
||||
forwardProgress.snapTo(0f)
|
||||
forwardProgress.animateTo(1f, tween(350, easing = FastOutSlowInEasing))
|
||||
forwardTarget = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()
|
||||
MaterialTheme(colorScheme = colorScheme) {
|
||||
AnimatedContent(
|
||||
targetState = currentScreen,
|
||||
transitionSpec = {
|
||||
if (targetState.ordinal > initialState.ordinal) {
|
||||
// 正向导航:新页面从右侧滑入覆盖,旧页面略微左移+淡出
|
||||
(slideInHorizontally { it } + fadeIn()) togetherWith
|
||||
(slideOutHorizontally { -it / 4 } + fadeOut())
|
||||
} else {
|
||||
// 返回导航:新页面从左侧滑入,旧页面向右侧滑出
|
||||
(slideInHorizontally(animationSpec = tween(250)) { -it } + fadeIn(
|
||||
animationSpec = tween(
|
||||
250
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// Layer 0: CalendarMonthView(始终组合以保持状态)
|
||||
CalendarMonthView(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
if (currentScreen != Screen.Main) {
|
||||
applyRevealTransform(
|
||||
effectiveBackProgress,
|
||||
forwardProgress.value,
|
||||
forwardTarget != null
|
||||
)
|
||||
)) togetherWith
|
||||
(slideOutHorizontally(animationSpec = tween(250)) { it } + fadeOut(
|
||||
animationSpec = tween(250)
|
||||
))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) { screen ->
|
||||
when (screen) {
|
||||
Screen.Main -> CalendarMonthView(
|
||||
modifier = Modifier,
|
||||
onNavigateToAbout = { currentScreen = Screen.About }
|
||||
}
|
||||
},
|
||||
onNavigateToAbout = { navigateTo(Screen.About) }
|
||||
)
|
||||
|
||||
// Layer 1: AboutScreen(About 或 Licenses 页面时组合)
|
||||
if (currentScreen == Screen.About || currentScreen == Screen.Licenses) {
|
||||
AboutScreen(
|
||||
onBack = {
|
||||
if (currentScreen == Screen.About) handleBack()
|
||||
},
|
||||
onNavigateToLicenses = {
|
||||
if (currentScreen == Screen.About) navigateTo(Screen.Licenses)
|
||||
},
|
||||
modifier = Modifier.graphicsLayer {
|
||||
when (currentScreen) {
|
||||
Screen.Licenses -> applyRevealTransform(
|
||||
effectiveBackProgress,
|
||||
forwardProgress.value,
|
||||
forwardTarget == Screen.Licenses
|
||||
)
|
||||
|
||||
Screen.About -> {
|
||||
val bp = effectiveBackProgress
|
||||
val fp = forwardProgress.value
|
||||
when {
|
||||
bp > 0.001f -> applyDismissTransform(bp)
|
||||
fp < 0.999f && forwardTarget == Screen.About -> applyEnterTransform(fp)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Screen.About -> {
|
||||
PredictiveBackHandler(
|
||||
enabled = backProgress == 0f,
|
||||
onProgress = { backProgress = it },
|
||||
onBack = handleBack,
|
||||
onCancel = handleCancel
|
||||
)
|
||||
AboutScreen(
|
||||
onBack = { currentScreen = Screen.Main },
|
||||
onNavigateToLicenses = { currentScreen = Screen.Licenses },
|
||||
modifier = Modifier.graphicsLayer {
|
||||
translationX = backProgress * size.width * 0.3f
|
||||
scaleX = 1f - backProgress * 0.05f
|
||||
scaleY = 1f - backProgress * 0.05f
|
||||
// Layer 2: LicensesScreen(Licenses 页面时组合)
|
||||
if (currentScreen == Screen.Licenses) {
|
||||
LicensesScreen(
|
||||
onBack = handleBack,
|
||||
modifier = Modifier.graphicsLayer {
|
||||
val bp = effectiveBackProgress
|
||||
val fp = forwardProgress.value
|
||||
when {
|
||||
bp > 0.001f -> applyDismissTransform(bp)
|
||||
fp < 0.999f && forwardTarget == Screen.Licenses -> applyEnterTransform(fp)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Screen.Licenses -> {
|
||||
PredictiveBackHandler(
|
||||
enabled = backProgress == 0f,
|
||||
onProgress = { backProgress = it },
|
||||
onBack = handleBack,
|
||||
onCancel = handleCancel
|
||||
)
|
||||
LicensesScreen(
|
||||
onBack = { currentScreen = Screen.About },
|
||||
modifier = Modifier.graphicsLayer {
|
||||
translationX = backProgress * size.width * 0.3f
|
||||
scaleX = 1f - backProgress * 0.05f
|
||||
scaleY = 1f - backProgress * 0.05f
|
||||
}
|
||||
)
|
||||
}
|
||||
// 预测性返回手势
|
||||
if (currentScreen != Screen.Main) {
|
||||
PredictiveBackHandler(
|
||||
enabled = !backAnimProgress.isRunning && !isHandlingBack && forwardTarget == null,
|
||||
onProgress = { backProgress = it },
|
||||
onBack = handleBack,
|
||||
onCancel = handleCancel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||