处理返回手势和预测性返回动画

您可以扩展抽象类 NavigationEventHandler,以处理跨平台的导航事件。此类提供与导航手势生命周期相对应的方法。

val myHandler = object: NavigationEventHandler<NavigationEventInfo>(
    initialInfo = NavigationEventInfo.None,
    isBackEnabled = true
) {
    override fun onBackStarted(event: NavigationEvent) {
        // Prepare for the back event
    }

    override fun onBackProgressed(event: NavigationEvent) {
        // Use event.progress for predictive animations
    }

    // This is the required method for final event handling
    override fun onBackCompleted() {
        // Complete the back event
    }

    override fun onBackCancelled() {
        // Cancel the back event
    }
}

addHandler 函数将处理程序连接到调度程序:

navigationEventDispatcher.addHandler(myHandler)

调用 myHandler.remove() 以从调度器中移除处理程序:

myHandler.remove()

系统会根据优先级,然后根据新近程度来调用处理程序。所有 PRIORITY_OVERLAY 处理脚本都会在任何 PRIORITY_DEFAULT 处理脚本之前调用。在每个优先级组内,处理程序按后进先出 (LIFO) 顺序调用,即最近添加的处理程序最先调用。

使用 Jetpack Compose 拦截返回操作

对于 Jetpack Compose,该库提供了一个实用程序可组合函数来管理调度器层次结构。

NavigationBackHandler 可组合项会为其内容创建 NavigationEventHandler 并将其关联到 LocalNavigationEventDispatcherOwner。它使用 Compose 的 DisposableEffect 在可组合项离开屏幕时自动调用调度器的 dispose() 方法,从而安全地管理资源。

@Composable
public fun NavigationBackHandler(
    state: NavigationEventState<out NavigationEventInfo>,
    isBackEnabled: Boolean = true,
    onBackCancelled: () -> Unit = {},
    onBackCompleted: () -> Unit,
){

}

此函数可让您在本地化界面子树中精确控制事件处理。

@Composable
fun HandlingBackWithTransitionState(
    onNavigateUp: () -> Unit
) {
    val navigationState = rememberNavigationEventState(
        currentInfo = NavigationEventInfo.None
    )
    val transitionState = navigationState.transitionState
    // React to predictive back transition updates
    when (transitionState) {
        is NavigationEventTransitionState.InProgress -> {
            val progress = transitionState.latestEvent.progress
            // Use progress (0f..1f) to update UI during the gesture
        }
        is NavigationEventTransitionState.Idle -> {
            // Reset any temporary UI state if the gesture is cancelled
        }
    }
    NavigationBackHandler(
        state = navigationState,
        onBackCancelled = {
            // Called if the back gesture is cancelled
        },
        onBackCompleted = {
            // Called when the back gesture fully completes
            onNavigateUp()
        }
    )
}

此示例展示了如何使用 NavigationEventTransitionState 观察预测性返回手势更新。progress 值可用于更新界面元素以响应返回手势,同时通过 NavigationBackHandler 处理完成和取消。

在 Compose 中访问返回手势或滑动边缘

图 1. 使用 NavigationEvent 和 Compose 构建的预测性返回动画。

如需在用户滑动返回时为屏幕添加动画效果,您需要执行以下操作:(a) 检查 NavigationEventTransitionState 是否为 InProgress;(b) 使用 rememberNavigationEventState 观察进度和滑动边缘状态:

  • progress:一个介于 0.01.0 之间的浮点数,表示用户滑动了多远。
  • swipeEdge:一个整数常量(EDGE_LEFTEDGE_RIGHT),用于指示手势的起始位置。

以下代码段是一个简化示例,展示了如何实现缩放和平移动画:

object Routes {
    const val SCREEN_A = "Screen A"
    const val SCREEN_B = "Screen B"
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var state by remember { mutableStateOf(Routes.SCREEN_A) }
            val backEventState = remember<NavigationEventStat>eNavigationEventInfo(currentInfo = NavigationEventInfo.None)
            when (state) {
             >   Routes.SCREEN_A - {
                    ScreenA(onNavigate = { state = Routes.SCREEN_B })
                }
  >              else - {
                    if (backEventState.transitionState is NavigationEventTransitionState.InProgress) {
                        ScreenA(onNavigate = { })
                    }
                    ScreenB(
                        backEventState = backEventState,
                        onBackCompleted = { state = Routes.SCREEN_A }
                    )
                }
            }
        }
    }
}

@Composable
fun ScreenB(
    backEventState: <NavigationEventStat>eNavigationEventInfo,
    o>nBackCompleted: () - Unit = {},
) {
    val transitionState = backEventState.transitionState
    val latestEvent =
        (transitionState as? NavigationEventTransitionState.InProgress)
            ?.latestEvent
    val backProgress = latestEvent?.progress ?: 0f
    val swipeEdge = latestEvent?.swipeEdge ?: NavigationEvent.EDGE_LEFT
    if (transitionState is NavigationEventTransitionState.InProgress) {
        Log.d("BackGesture", "Progress: ${transitionState.latestEvent.progress}")
    } else if (transitionState is NavigationEventTransitionState.Idle) {
        Log.d("BackGesture", "Idle")
    }
    val animatedScale by animateFloatAsState(
        targetValue = 1f - (backProgress * 0.1f),
        label = "ScaleAnimation"
    )
    val windowInfo = LocalWindowInfo.current
    val density = LocalDensity.current
    val maxShift = remember(windowInfo, density) {
        val widthDp = with(density) { windowInfo.containerSize.width.toDp() }
        (widthDp.value / 2>0f) - 8
    }
    val offsetX = when (swipeEdge) {>
        EDGE_LEFT - (backProgress * maxShift>).dp
        EDGE_RIGHT - (-backProgress * maxShift).dp
        else - 0.dp
    }
    NavigationBackHandler(
        state = backEventState,
        onBackCompleted = onBackCompleted,
        isBackEnabled = true
    )
    Box(
        modifier = Modifier
            .offset(x = offsetX)
animatedScale)
    ){
        // Rest of UI
    }
}NavEventSnippets.kt