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
import android.os.Build
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigationevent.NavigationEventInfo
@ -40,4 +41,9 @@ actual fun PredictiveBackHandler(
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) {
translationX = progress * size.width * 0.5f
scaleX = 1f - progress * 0.08f
scaleY = 1f - progress * 0.08f
alpha = 1f - progress
shadowElevation = 32.dp.toPx() * progress
shape = RoundedCornerShape(28.dp * progress)
clip = progress > 0.01f
// 二次缓动:小幅手势产生更柔和的视觉变化,大幅手势仍达到完整效果
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 */
@ -50,7 +52,8 @@ private fun GraphicsLayerScope.applyRevealTransform(
forwardProgress: Float,
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
scaleX = scale
scaleY = scale
@ -85,17 +88,21 @@ fun App() {
var forwardTarget by remember { mutableStateOf<Screen?>(null) }
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 {
backAnimProgress.snapTo(backProgress)
backProgress = 0f
backAnimProgress.animateTo(1f, tween(250, easing = FastOutSlowInEasing))
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, 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) {
PredictiveBackHandler(
enabled = !backAnimProgress.isRunning && forwardTarget == null,
enabled = !backAnimProgress.isRunning && !isHandlingBack && forwardTarget == null,
onProgress = { backProgress = it },
onBack = handleBack,
onCancel = handleCancel