浮层
一个使用 Jetpack Compose
实现的浮层组件,最大的特色就是不采用 Dialog
或者 PopupWindow
这些子 Window
实现,所以也就没有使用它们的各种坑点。
依赖引入
implementation("cn.qhplus.emo:modal")
Dialog
简单消息类型
view.emoDialog { modal ->
EmoDialogMsg(
modal,
"这是标题",
"这是一丢丢有趣但是没啥用的内容",
listOf(
EmoModalAction("取 消", MaterialTheme.colorScheme.primary) {
it.dismiss()
},
EmoModalAction("确 定", MaterialTheme.colorScheme.primary) {
view.emoToast("确定啦!!!")
it.dismiss()
}
)
)
}.show()
列表类型
view.emoDialog { modal ->
EmoDialogList(modal, maxHeight = 500.dp) {
items(200) { index ->
Item(title = "第${index + 1}项") {
view.emoToast("你点了第${index + 1}项")
}
}
}
}.show()
单选类型
view.emoDialog { modal ->
val list = remember {
val items = arrayListOf<String>()
for (i in 0 until 500) {
items.add("Item $i")
}
items
}
// 选中 index
var markIndex by remember {
mutableStateOf(20)
}
EmoDialogMarkList(
modal,
maxHeight = 500.dp,
list = list,
markIndex = markIndex
) { _, index ->
markIndex = index
view.emoToast("你点了第${index + 1}项")
}
}.show()
多选类型
view.emoDialog { modal ->
val list = remember {
val items = arrayListOf<String>()
for (i in 0 until 500) {
items.add("Item $i")
}
items
}
// 选中项
val checked = remember {
mutableStateListOf(0, 5, 10, 20)
}
// 不可选项
val disable = remember {
mutableStateListOf(5, 10)
}
Column {
EmoDialogMutiCheckList(
modal,
maxHeight = 500.dp,
list = list,
checked = checked.toSet(),
disabled = disable.toSet()
) { _, index ->
if (checked.contains(index)) {
checked.remove(index)
} else {
checked.add(index)
}
}
EmoDialogActions(
modal = modal,
actions = listOf(
EmoModalAction("取 消", MaterialTheme.colorScheme.primary) {
it.dismiss()
},
EmoModalAction("确 定", MaterialTheme.colorScheme.primary) {
view.emoToast("你选择了: ${checked.joinToString(",")}")
it.dismiss()
}
)
)
}
}.show()
Toast
这个也没有用系统的 Toast
组件,使用和效果类似,但可以更自定义化
view.emoToast("这只是个 Toast!")
// 更自定义的写法
view.emoToast(
duration: Long = 1000, // 保留时间
modalHostProvider: ModalHostProvider = DefaultModalHostProvider, // 承载主题,默认在 id 为 ID_ANDROID_CONTENT 的 view 下
alignment: Alignment = Alignment.BottomCenter, // 显示位置
horEdge: Dp = DefaultToastHorEdgeProtectionMargin,
verEdge: Dp = DefaultToastVerEdgeProtectionMargin,
radius: Dp = 8.dp,
background: Color = Color.Black,
enter: EnterTransition = xxx // 进入动画
exit: ExitTransition = xxx // 退出动画
) {
// Compose 内容
}
Tip
显示加载中、加载成功、加载失败的阻塞用户操作的浮层
val flow = MutableStateFlow<TipStatus>(TipStatus.Loading())
val tip = view.emoTip(status = flow).show()
scope.launch {
// 耗时操作
delay(1000)
flow.value = TipStatus.Error()
delay(100)
tip.dismiss()
}
Popup
// 提供点击位置检测功能
ClickPositionCheckerBox(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceVariant),
onClick = {
// 显示 popup
view.emoPopup(
offset = it,
widthCal = {
// 计算 popup 宽度
200.dp
},
background = { MaterialTheme.colorScheme.secondaryContainer }
){
Text(
text = "This is Popup Content\nThis is Popup Content",
modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}.show()
}
) {
Text(
text = "PopUp",
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp)
)
}
QuickAction
// 提供点击位置检测功能
ClickPositionCheckerBox(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.clip(RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.surfaceVariant),
onClick = {
// 显示 quick action
view.emoQuickAction(
offset = it,
actions = listOf(
QuickAction(R.drawable.quick_action_ic_copy, "Copy") {
// 点击事件
view.emoToast("What do you want to copy?").show()
},
QuickAction(R.drawable.quick_action_ic_delete, "Delete") {
// 点击事件
view.emoToast("What do you want to delete?").show()
}
),
actionWidth = 48.dp
).show()
}
) {
Text(
text = "Quick Action",
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp))
}
BottomSheet
view.emoBottomSheet {
EmoBottomSheetList(it) {
items(200) { index ->
Item(title = "第${index + 1}项") {
view.emoToast("你点了第${index + 1}项")
}
}
}
}.show()
自定义 Modal
view.emoModal(
mask: Color = DefaultMaskColor, // mask color
systemCancellable: Boolean = true, // 系统返回操作是否可以取消浮层
maskTouchBehavior: MaskTouchBehavior = MaskTouchBehavior.Dismiss, // mask 点击行为:穿透/取消浮层/不做响应
uniqueId: Long = SystemClock.elapsedRealtimeNanos(), // 如果相同,会把已存在的浮层 dismiss 掉
modalHostProvider: ModalHostProvider = DefaultModalHostProvider,
enter: EnterTransition = fadeIn(tween(), 0f),
exit: ExitTransition = fadeOut(tween(), 0f)
){
// Compose 内容
}.show()
更多信息可查看公众号文章 Android 浮层的重新思考与设计
关注公众号,入群聊聊~