fix: 预测性返回兼容性、跟手感与防重入

- 添加 BackHandler 降级,确保 OPPO/ColorOS 等设备基本返回可用
- handleBack 加 isHandlingBack 防重入,避免双 handler 触发
- 完成动画改用 spring 替代 tween,根据手势释放位置自然调速
- dismiss/reveal 变换使用二次缓动(progress²),小幅滑动更柔和跟手
This commit is contained in:
meyou 2026-05-19 23:35:43 +08:00
parent 4219527428
commit 6542362f6f
No known key found for this signature in database
2 changed files with 25 additions and 12 deletions

View File

@ -1,6 +1,7 @@
package plus.rua.project package plus.rua.project
import android.os.Build import android.os.Build
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.navigationevent.NavigationEventInfo import androidx.navigationevent.NavigationEventInfo
@ -40,4 +41,9 @@ actual fun PredictiveBackHandler(
onProgress(ts.latestEvent.progress) onProgress(ts.latestEvent.progress)
} }
} }
// 降级:部分设备(如 OPPO/ColorOS不通过 OnBackInvokedCallback 分发返回事件
BackHandler(enabled = enabled) {
onBack()
}
} }

View File

@ -35,13 +35,15 @@ private enum class Screen { Main, About, Licenses }
/** 返回手势动画:顶层页面滑出 + 淡出 + 缩小 + 圆角阴影 */ /** 返回手势动画:顶层页面滑出 + 淡出 + 缩小 + 圆角阴影 */
private fun GraphicsLayerScope.applyDismissTransform(progress: Float) { private fun GraphicsLayerScope.applyDismissTransform(progress: Float) {
translationX = progress * size.width * 0.5f // 二次缓动:小幅手势产生更柔和的视觉变化,大幅手势仍达到完整效果
scaleX = 1f - progress * 0.08f val p = progress * progress
scaleY = 1f - progress * 0.08f translationX = p * size.width * 0.5f
alpha = 1f - progress scaleX = 1f - p * 0.08f
shadowElevation = 32.dp.toPx() * progress scaleY = 1f - p * 0.08f
shape = RoundedCornerShape(28.dp * progress) alpha = 1f - p
clip = progress > 0.01f shadowElevation = 32.dp.toPx() * p
shape = RoundedCornerShape(28.dp * p)
clip = p > 0.01f
} }
/** 底层页面缩放:随返回进度从 baseScale 放大到 1.0 */ /** 底层页面缩放:随返回进度从 baseScale 放大到 1.0 */
@ -50,7 +52,8 @@ private fun GraphicsLayerScope.applyRevealTransform(
forwardProgress: Float, forwardProgress: Float,
isForwardAnimating: Boolean isForwardAnimating: Boolean
) { ) {
val baseScale = 0.92f + 0.08f * progress val p = progress * progress
val baseScale = 0.92f + 0.08f * p
val scale = if (isForwardAnimating) lerp(1f, baseScale, forwardProgress) else baseScale val scale = if (isForwardAnimating) lerp(1f, baseScale, forwardProgress) else baseScale
scaleX = scale scaleX = scale
scaleY = scale scaleY = scale
@ -85,17 +88,21 @@ fun App() {
var forwardTarget by remember { mutableStateOf<Screen?>(null) } var forwardTarget by remember { mutableStateOf<Screen?>(null) }
val forwardProgress = remember { Animatable(1f) } val forwardProgress = remember { Animatable(1f) }
val handleBack: () -> Unit = { var isHandlingBack by remember { mutableStateOf(false) }
val handleBack: () -> Unit = lambda@{
if (isHandlingBack) return@lambda
isHandlingBack = true
scope.launch { scope.launch {
backAnimProgress.snapTo(backProgress) backAnimProgress.snapTo(backProgress)
backProgress = 0f backProgress = 0f
backAnimProgress.animateTo(1f, tween(250, easing = FastOutSlowInEasing)) backAnimProgress.animateTo(1f, spring(stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy))
currentScreen = when (currentScreen) { currentScreen = when (currentScreen) {
Screen.About -> Screen.Main Screen.About -> Screen.Main
Screen.Licenses -> Screen.About Screen.Licenses -> Screen.About
else -> currentScreen else -> currentScreen
} }
backAnimProgress.animateTo(0f, tween(100, easing = FastOutSlowInEasing)) backAnimProgress.animateTo(0f, spring(stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy))
isHandlingBack = false
} }
} }
@ -186,7 +193,7 @@ fun App() {
// 预测性返回手势 // 预测性返回手势
if (currentScreen != Screen.Main) { if (currentScreen != Screen.Main) {
PredictiveBackHandler( PredictiveBackHandler(
enabled = !backAnimProgress.isRunning && forwardTarget == null, enabled = !backAnimProgress.isRunning && !isHandlingBack && forwardTarget == null,
onProgress = { backProgress = it }, onProgress = { backProgress = it },
onBack = handleBack, onBack = handleBack,
onCancel = handleCancel onCancel = handleCancel