学习记录
最开始用到这个
如果用的系统组件scaffold或者backdropscaffold的话,组件里自带snackbar,要显示snackbar只要如下操作即可
//state传给scaffold的构造参数里
val state = rememberBackdropScaffoldState(BackdropValue.Revealed)
//下边的方法要在launch里调用,本身是个suspend方法,可以用LaunchEffect或者rememberCoroutineScope()
state.snackbarHostState.showSnackbar(showContent, "cancel")
scaffold构造方法简化
fun BackdropScaffold(
//
scaffoldState: BackdropScaffoldState = rememberBackdropScaffoldState(Concealed),
//
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) }
)
snackbarHost 默认有个snackbar组件的
fun SnackbarHost(
hostState: SnackbarHostState,
modifier: Modifier = Modifier,
snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) }
)
snackbar的两种构造方法 看下源码里,这种系统默认写好了,content就是个text,action是个textButton
fun Snackbar(
snackbarData: SnackbarData,
modifier: Modifier = Modifier,
actionOnNewLine: Boolean = false,
shape: Shape = MaterialTheme.shapes.small,
backgroundColor: Color = SnackbarDefaults.backgroundColor,
contentColor: Color = MaterialTheme.colors.surface,
actionColor: Color = SnackbarDefaults.primaryActionColor,
elevation: Dp = 6.dp
)
这种构造方法content和action是两个组件,可以自定义,不是上边那种默认写好的,
fun Snackbar(
modifier: Modifier = Modifier,
action: @Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = MaterialTheme.shapes.small,
backgroundColor: Color = SnackbarDefaults.backgroundColor,
contentColor: Color = MaterialTheme.colors.surface,
elevation: Dp = 6.dp,
content: @Composable () -> Unit
)
没有使用scaffold的情况
其实也简单,仿照scaffold的写法即可,【当然了因为snackbar本身就是个组件,你直接添加这个组件也行,不过得自己处理显示隐藏,而且也没动画,所以一般不这样做,而是利用snackbarhost】
下边举例用法,也很简单:
item{
val state = remember {
SnackbarHostState()
}
val scope= rememberCoroutineScope()
SnackbarHost(hostState = state)//默认这个host不显示的,只有state调用show才会显示
Button(onClick = {
scope.launch {
state.showSnackbar("message","close")
}
}) {
Text(text = "click")
}
}
另外你也可以自定义snackbar的ui,如下
SnackbarHost(hostState = state) {
Snackbar(action = {
Icon(painter = painterResource(id = R.drawable.ic_baseline_clear_24),
contentDescription = "close",Modifier.clickable { it.performAction() })
}, content = {
Column {
Text(text = it.message)
Text(text = "other compose")
}
},
modifier = Modifier.padding(horizontal = 80.dp),)
}
SnackbarHost 源码分析
简单分析下SnackbarHost是咋做到显示,时间到了隐藏的 先看下数据类
interface SnackbarData {
val message: String
val actionLabel: String?
val duration: SnackbarDuration
fun performAction()
fun dismiss()
}
snackbar里边那个action点击会执行performaction方法
onClick = { snackbarData.performAction() },
SnackbarHost的代码
fun SnackbarHost(
hostState: SnackbarHostState,
modifier: Modifier = Modifier,
snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) }
) {
val currentSnackbarData = hostState.currentSnackbarData//开始的时候这个是null的
val accessibilityManager = LocalAccessibilityManager.current
LaunchedEffect(currentSnackbarData) {
if (currentSnackbarData != null) {
val duration = currentSnackbarData.duration.toMillis(
currentSnackbarData.actionLabel != null,
accessibilityManager
)
delay(duration)
currentSnackbarData.dismiss()//恢复挂载函数,snackbardata就会在finally的地方被置空,组件就重组了,snackbarhost会重新加载。
}
}
//current不为空的话下边这个就动画显示snackbar组件了,开始是null,所以不显示的
FadeInFadeOutWithScale(
current = hostState.currentSnackbarData,
modifier = modifier,
content = snackbar
)
}
常用的hostState,默认里边数据都是null
//系统组件scaffold里用到的三种
rememberScaffoldState()
rememberBottomSheetScaffoldState()
rememberBackdropScaffoldState(BackdropValue.Revealed)
//普通的
val state = remember {
SnackbarHostState()
}
调用hostState的show方法的时候才会赋值,如下 这里用到了挂载函数suspendCancellableCoroutine,代码块里初始化了snackbardata,里边的performaction和dismiss的实现方法,作用就是恢复挂载函数
suspend fun showSnackbar(
message: String,
actionLabel: String? = null,
duration: SnackbarDuration = SnackbarDuration.Short
): SnackbarResult = mutex.withLock {
try {
return suspendCancellableCoroutine { continuation ->
currentSnackbarData = SnackbarDataImpl(message, actionLabel, duration, continuation)
//这里赋值以后,上边的LaunchedEffect(currentSnackbarData)就会开始倒计时执行了。
}
} finally {
currentSnackbarData = null
}
}
简单看下SnackbarDataImpl,下边两个方法都是用来恢复上边的挂载函数的,挂载函数恢复完以后,就会执行上边的finally,把snackbardata置为null
override fun performAction() {
if (continuation.isActive) continuation.resume(SnackbarResult.ActionPerformed)
}
override fun dismiss() {
if (continuation.isActive) continuation.resume(SnackbarResult.Dismissed)
}