Android NDK开发详解Wear之滑动关闭
滑动关闭动画用于表现在用户转到上一页时的过渡。
SwipeToDismissBox
SwipeToDismissBox(
state: SwipeToDismissBoxState,
modifier: Modifier = Modifier,
backgroundScrimColor: Color = MaterialTheme.colors.background,
contentScrimColor: Color = MaterialTheme.colors.background,
backgroundKey: Any = SwipeToDismissKeys.Background,
contentKey: Any = SwipeToDismissKeys.Content,
hasBackground: Boolean = true,
content: BoxScope.(isBackground: Boolean) -> Unit
): Unit
fun
This function is deprecated.
This overload is provided for backwards compatibility. A newer overload is available that uses androidx.wear.compose.foundation.SwipeToDismissBoxState.
Wear Material SwipeToDismissBox that handles the swipe-to-dismiss gesture. Takes a single slot for the background (only displayed during the swipe gesture) and the foreground content.
Example of a SwipeToDismissBox with stateful composables:
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
import androidx.wear.compose.material.Icon
import androidx.wear.compose.material.SplitToggleChip
import androidx.wear.compose.material.SwipeToDismissBox
import androidx.wear.compose.material.Text
// State for managing a 2-level navigation hierarchy between
// MainScreen and ItemScreen composables.
// Alternatively, use SwipeDismissableNavHost from wear.compose.navigation.
var showMainScreen by remember {
mutableStateOf(true) }
val saveableStateHolder = rememberSaveableStateHolder()
// Swipe gesture dismisses ItemScreen to return to MainScreen.
val state = rememberSwipeToDismissBoxState()
LaunchedEffect(state.currentValue) {
if (state.currentValue == SwipeToDismissValue.Dismissed) {
state.snapTo(SwipeToDismissValue.Default)
showMainScreen = !showMainScreen
}
}
// Hierarchy is ListScreen -> ItemScreen, so we show ListScreen as the background behind
// the ItemScreen, otherwise there's no background to show.
SwipeToDismissBox(
state = state,
hasBackground = !showMainScreen,
backgroundKey = if (!showMainScreen) "MainKey" else "Background",
contentKey = if (showMainScreen) "MainKey" else "ItemKey",
) {
isBackground ->
if (isBackground || showMainScreen) {
// Best practice would be to use State Hoisting and leave this composable stateless.
// Here, we want to support MainScreen being shown from different destinations
// (either in the foreground or in the background during swiping) - that can be achieved
// using SaveableStateHolder and rememberSaveable as shown below.
saveableStateHolder.SaveableStateProvider(
key = "MainKey",
content = {
// Composable that maintains its own state
// and can be shown in foreground or background.
val checked = rememberSaveable {
mutableStateOf(true) }
Column(
modifier = Modifier
.fillMaxSize().padding(horizontal = 8.dp, vertical = 8.dp),
verticalArrangement =
Arrangement.spacedBy(4.dp, Alignment.CenterVertically),
) {
SplitToggleChip(
checked = checked.value,
label = {
Text("Item details") },
modifier = Modifier.height(40.dp),
onCheckedChange = {
v -> checked.value = v },
onClick = {
showMainScreen = false },
toggleControl = {
Icon(
imageVector = ToggleChipDefaults.checkboxIcon(
checked = checked.value
),
contentDescription = null,
)
}
)
}
}
)
} else {
Column(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("Show details here...", color = MaterialTheme.colors.onPrimary)
Text("Swipe right to dismiss", color = MaterialTheme.colors.onPrimary)
}
}
}
滑动关闭的动画详情与 RSB 按下类似。手指最多可控制 50% 的动画进度。
应用视图上还有一个与关闭手势关联的动画。应用视图上显示的移动量与手指需要移动的距离并不完全相同。应用视图绝不应离开屏幕边缘,显示带有一些阻力的类似挤压的效果。
实现
Wear 有自己的 Box 版本,即 SwipeToDismissBox。这增加了对滑动关闭手势(与移动设备上的返回按钮类似)的支持。
SwipeToDismissBox 是一个可以通过向右滑动来关闭的可组合项。
如需使用 SwipeToDismissBox,您必须先创建一个状态。该状态包含滑动方向、动画是否正在运行、当前值和目标等信息。以下示例展示了如何设计一个简单的滑动关闭操作:
val state = rememberSwipeToDismissBoxState()
SwipeToDismissBox(
onDismissed = {
/* navigateBack */ },
) {
isBackground ->
if (isBackground) {
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.secondaryVariant))
} else {
Column(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colors.primary),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("Swipe to dismiss", color = MaterialTheme.colors.onPrimary)
}
}
}
如需详细了解如何将 SwipeToDismissBox 与 Navigation 库结合使用,请参阅 Wear Compose Navigation 库的参考文档。
设计
在设计滑动关闭操作时,请牢记以下两条原则:
屏幕边缘
应考虑到可滑动的其他界面元素,例如分页的应用视图。如果可以滑动关闭,请预留 20% 的屏幕边缘,用于触发该动作。
如需查看内容可水平滚动时的边缘滑动示例,请参阅此示例。
决定是返回还是留在应用视图的阈值
如果用户手指拖动超过屏幕宽度的 50%,应用应触发剩余的滑动返回动画。如果不到此阈值,应用应迅速恢复完整的应用视图。
如果手势速度很快,请忽略 50% 阈值规则并滑动返回。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2022-09-07。