feat: 迁移 NavigationBackHandler + 替换应用图标
- 将 PredictiveBackHandler 迁移到 NavigationBackHandler (navigationevent-compose 1.0.1) - 添加 org.jetbrains.androidx.navigationevent:navigationevent-compose 依赖 - 提取 applyDismissTransform/applyRevealTransform/applyEnterTransform 辅助函数 - 替换所有密度的启动图标和关于页图标 - 移除旧的自适应图标 XML 配置
|
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 |
@ -16,6 +16,7 @@ material3 = "1.10.0-alpha05"
|
||||
kotlinx-datetime = "0.8.0"
|
||||
tyme4kt = "1.4.5"
|
||||
sketch = "4.4.0"
|
||||
navigationevent = "1.0.1"
|
||||
|
||||
[libraries]
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
@ -34,6 +35,7 @@ tyme4kt = { module = "cn.6tail:tyme4kt", version.ref = "tyme4kt" }
|
||||
sketch-compose = { module = "io.github.panpf.sketch4:sketch-compose", version.ref = "sketch" }
|
||||
sketch-animated-gif = { module = "io.github.panpf.sketch4:sketch-animated-gif", version.ref = "sketch" }
|
||||
sketch-compose-resources = { module = "io.github.panpf.sketch4:sketch-compose-resources", version.ref = "sketch" }
|
||||
navigationevent-compose = { module = "org.jetbrains.androidx.navigationevent:navigationevent-compose", version.ref = "navigationevent" }
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
@ -49,6 +49,7 @@ kotlin {
|
||||
implementation(libs.sketch.compose)
|
||||
implementation(libs.sketch.animated.gif)
|
||||
implementation(libs.sketch.compose.resources)
|
||||
implementation(libs.navigationevent.compose)
|
||||
}
|
||||
commonTest.dependencies {
|
||||
implementation(libs.kotlin.test)
|
||||
|
||||
@ -2,8 +2,11 @@ package plus.rua.project
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.navigationevent.NavigationEventInfo
|
||||
import androidx.navigationevent.NavigationEventTransitionState
|
||||
import androidx.navigationevent.compose.NavigationBackHandler
|
||||
import androidx.navigationevent.compose.rememberNavigationEventState
|
||||
|
||||
class AndroidPlatform : Platform {
|
||||
override val name: String = "Android ${Build.VERSION.SDK_INT}"
|
||||
@ -22,19 +25,19 @@ 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()
|
||||
}
|
||||
val navState = rememberNavigationEventState(NavigationEventInfo.None)
|
||||
|
||||
NavigationBackHandler(
|
||||
state = navState,
|
||||
isBackEnabled = enabled,
|
||||
onBackCancelled = onCancel,
|
||||
onBackCompleted = onBack
|
||||
)
|
||||
|
||||
LaunchedEffect(navState.transitionState) {
|
||||
val ts = navState.transitionState
|
||||
if (ts is NavigationEventTransitionState.InProgress) {
|
||||
onProgress(ts.latestEvent.progress)
|
||||
}
|
||||
} else {
|
||||
androidx.activity.compose.BackHandler(enabled = enabled, onBack = onBack)
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 1.2 MiB |
@ -21,6 +21,7 @@ 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
|
||||
@ -32,6 +33,35 @@ import plus.rua.project.ui.lerp
|
||||
|
||||
private enum class Screen { Main, About, Licenses }
|
||||
|
||||
/** 返回手势动画:顶层页面滑出 + 淡出 + 缩小 + 圆角阴影 */
|
||||
private fun GraphicsLayerScope.applyDismissTransform(progress: Float) {
|
||||
translationX = progress * size.width * 0.5f
|
||||
scaleX = 1f - progress * 0.08f
|
||||
scaleY = 1f - progress * 0.08f
|
||||
alpha = 1f - progress * 0.8f
|
||||
shadowElevation = 32.dp.toPx() * progress
|
||||
shape = RoundedCornerShape(28.dp * progress)
|
||||
clip = progress > 0.01f
|
||||
}
|
||||
|
||||
/** 底层页面缩放:随返回进度从 baseScale 放大到 1.0 */
|
||||
private fun GraphicsLayerScope.applyRevealTransform(
|
||||
progress: Float,
|
||||
forwardProgress: Float,
|
||||
isForwardAnimating: Boolean
|
||||
) {
|
||||
val baseScale = 0.92f + 0.08f * progress
|
||||
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 并管理页面导航。
|
||||
*
|
||||
@ -59,7 +89,7 @@ fun App() {
|
||||
scope.launch {
|
||||
backAnimProgress.snapTo(backProgress)
|
||||
backProgress = 0f
|
||||
backAnimProgress.animateTo(1f, tween(200, easing = FastOutSlowInEasing))
|
||||
backAnimProgress.animateTo(1f, tween(250, easing = FastOutSlowInEasing))
|
||||
currentScreen = when (currentScreen) {
|
||||
Screen.About -> Screen.Main
|
||||
Screen.Licenses -> Screen.About
|
||||
@ -96,14 +126,11 @@ fun App() {
|
||||
CalendarMonthView(
|
||||
modifier = Modifier.graphicsLayer {
|
||||
if (currentScreen != Screen.Main) {
|
||||
val baseScale = 0.92f + 0.08f * effectiveBackProgress
|
||||
val scale = if (forwardTarget != null) {
|
||||
lerp(1f, baseScale, forwardProgress.value)
|
||||
} else {
|
||||
baseScale
|
||||
}
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
applyRevealTransform(
|
||||
effectiveBackProgress,
|
||||
forwardProgress.value,
|
||||
forwardTarget != null
|
||||
)
|
||||
}
|
||||
},
|
||||
onNavigateToAbout = { navigateTo(Screen.About) }
|
||||
@ -120,34 +147,18 @@ fun App() {
|
||||
},
|
||||
modifier = Modifier.graphicsLayer {
|
||||
when (currentScreen) {
|
||||
Screen.Licenses -> {
|
||||
val baseScale = 0.92f + 0.08f * effectiveBackProgress
|
||||
val scale = if (forwardTarget == Screen.Licenses) {
|
||||
lerp(1f, baseScale, forwardProgress.value)
|
||||
} else {
|
||||
baseScale
|
||||
}
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
Screen.Licenses -> applyRevealTransform(
|
||||
effectiveBackProgress,
|
||||
forwardProgress.value,
|
||||
forwardTarget == Screen.Licenses
|
||||
)
|
||||
|
||||
Screen.About -> {
|
||||
val bp = effectiveBackProgress
|
||||
val fp = forwardProgress.value
|
||||
when {
|
||||
bp > 0.001f -> {
|
||||
translationX = bp * size.width * 0.3f
|
||||
scaleX = 1f - bp * 0.05f
|
||||
scaleY = 1f - bp * 0.05f
|
||||
shadowElevation = 32.dp.toPx() * bp
|
||||
shape = RoundedCornerShape(28.dp * bp)
|
||||
clip = bp > 0.01f
|
||||
}
|
||||
|
||||
fp < 0.999f && forwardTarget == Screen.About -> {
|
||||
translationX = (1f - fp) * size.width
|
||||
alpha = fp
|
||||
}
|
||||
bp > 0.001f -> applyDismissTransform(bp)
|
||||
fp < 0.999f && forwardTarget == Screen.About -> applyEnterTransform(fp)
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,19 +176,8 @@ fun App() {
|
||||
val bp = effectiveBackProgress
|
||||
val fp = forwardProgress.value
|
||||
when {
|
||||
bp > 0.001f -> {
|
||||
translationX = bp * size.width * 0.3f
|
||||
scaleX = 1f - bp * 0.05f
|
||||
scaleY = 1f - bp * 0.05f
|
||||
shadowElevation = 32.dp.toPx() * bp
|
||||
shape = RoundedCornerShape(28.dp * bp)
|
||||
clip = bp > 0.01f
|
||||
}
|
||||
|
||||
fp < 0.999f && forwardTarget == Screen.Licenses -> {
|
||||
translationX = (1f - fp) * size.width
|
||||
alpha = fp
|
||||
}
|
||||
bp > 0.001f -> applyDismissTransform(bp)
|
||||
fp < 0.999f && forwardTarget == Screen.Licenses -> applyEnterTransform(fp)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||