compose Android dropPopwindow 和 bottomPopWindow

562 阅读1分钟

Compose封装Drop形式popwindow

compose 提供Popup组件包含属性如下

@Composable
fun Popup(
    alignment: Alignment = Alignment.TopStart,
    offset: IntOffset = IntOffset(0, 0),
    onDismissRequest: (() -> Unit)? = null,
    properties: PopupProperties = PopupProperties(),
    content: @Composable () -> Unit
)
  1. alignment控制显示位置
  2. offset控制x,y偏移量
  3. onDismissRequest 监听取消动作
  4. properties控制弹框外边缘是否可取消是否强弹框
  5. content绘制弹框内容

存在问题位置锚点和偏移量调整麻烦,官网提供一种约束布局ConstraintLayout满足对齐不过针对的是控件,所以在Popup外套一层布局比如Box在用包裹特性包裹Popup

  1. 封装简单Popup满足列表格式单选
@Composable
fun dropPopWindow(
    isShowPop: MutableState<Boolean>,
    array: Array<String>,
    alignment: Alignment = Alignment.TopStart,
    offset: IntOffset = IntOffset(0, 0), onItemClick: (Int) -> Unit
) {

    if (isShowPop.value) {
        Popup(
            content = {
                Surface(
                    shadowElevation = 8.dp,
                    tonalElevation = 8.dp,
                    shape = RoundedCornerShape(8.dp)
                ) {
                    Column(
                        modifier = Modifier
                            .background(
                                Color.White,
                                RoundedCornerShape(6.dp)
                            )
                            .width(170.dp)
                    ) {
                        array.forEachIndexed { index, s ->
                            Row(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .wrapContentHeight()
                                    .clickable {
                                        isShowPop.value = false
                                        onItemClick(index)
                                    }, verticalAlignment = CenterVertically
                            ) {
                                Spacer(modifier = Modifier.width(23.dp))
                                Text(
                                    s,
                                    fontSize = TextUnit(16f, TextUnitType.Sp),
                                    modifier = Modifier
                                        .padding(12.dp, 12.dp)
                                )
                            }
                        }


                    }
                }

            },
            properties = PopupProperties(dismissOnBackPress = true, dismissOnClickOutside = false),
            onDismissRequest = {
                isShowPop.value = false
            },
            alignment = alignment,
            offset = offset
        )
    }
}
  1. 给Popup套壳并且和Button关联(也可以是其他组件关联)存放在约束布局中ConstraintLayout位置关联方法 Modifier.constrainAs,这里使用组件位于Button正下方,示例如下
ConstraintLayout {
    var popState by remember {
        mutableStateOf(mutableStateOf(false))
    }

    val (center, desc) = createRefs()
    Button(onClick = {
        popState.value = true
    }, modifier = Modifier.constrainAs(center) {
        centerTo(parent)
    }) {
        Text(text = "Center")
    }
    val context = LocalContext.current
    if (popState.value) {
        Box(modifier = Modifier
            .constrainAs(desc) {
                top.linkTo(center.bottom)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            }
            .wrapContentSize()) {
            dropPopWindow(
                isShowPop = popState,
                array = arrayOf("Item1", "Item2", "Item3"),
                onItemClick = {
                    Toast.makeText(context, "点击了$it", Toast.LENGTH_SHORT).show()
                }, alignment = Alignment.TopCenter, offset = IntOffset.Zero
            )
        }
    }
}

效果如图:

BD7B8CEE-C9F7-4d72-9405-C7E6B26000DB.png

Compose封装Bottom Sheet形式popwindow列表单选

这里选择布局组件ModalDrawerSheet进行封装,先介绍官方组件


@Composable
fun ModalDrawerSheet(
    modifier: Modifier = Modifier,-要应用于此抽屉内容的修饰符
    drawerShape: Shape = DrawerDefaults.shape,-定义此抽屉容器的形状
    drawerContainerColor: Color = MaterialTheme.colorScheme.surface,-用于此抽屉背景的颜色。使用颜色。透明表示没有颜色。
    drawerContentColor: Color = contentColorFor(drawerContainerColor),-此抽屉中内容的首选颜色。默认为drawerContainerColor的匹配内容颜色,或者不是主题中的颜色,则默认为当前LocalContentColor。
    drawerTonalElevation: Dp = DrawerDefaults.ModalDrawerElevation,-当drawerContainerColor为ColorScheme.surface时,半透明的原色覆盖将应用于容器顶部。色调高程值越高,浅色主题的颜色越深,深色主题的颜色就越浅。另请参见:曲面。
    windowInsets: WindowInsets = DrawerDefaults.windowInsets,-类似于边距
    content: @Composable ColumnScope.() -> Unit-模态导航抽屉内的内容
) 

默认content内容是铺满全屏的,设想用比重关系切割内容和背景板背景板设置半透明遮罩,是弹框还得补充标题和关闭按钮。用Box作为遮罩部分背景色设置0x80000000,用LazyColumn存放标题关闭按钮和列表内容主体

@Composable
fun BottomSheetPopWindow(
    title: String = "请选择",
    isShowPop: MutableState<Boolean>,
    array: Array<String>,
    onItemClick: (Int) -> Unit,
    onOutSideDismiss: (MutableState<Boolean>) -> Unit
) {
    if (isShowPop.value) {
        ModalDrawerSheet(
            modifier = Modifier
                .fillMaxSize(),
            drawerShape = RoundedCornerShape(0.dp),
            drawerContainerColor = Color(0x80000000),
            windowInsets = WindowInsets(0.dp)
        ) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .weight(1f, true).clickable {    onOutSideDismiss(isShowPop) }
            ) {

            }
            val h = LocalConfiguration.current.screenHeightDp
            LazyColumn(
                contentPadding = PaddingValues(horizontal = 12.dp),
                verticalArrangement = Arrangement.spacedBy(10.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .wrapContentHeight()
                    .defaultMinSize(minHeight = h.div(3).dp)
                    .background(Color.White, RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp))
            ) {
                item {
                    Box(
                        Modifier
                            .fillMaxWidth()
                            .height(60.dp)
                    ) {
                        Text(
                            text = title,
                            color = Color.Black,
                            fontWeight = FontWeight.Bold,
                            fontSize = TextUnit(16f, TextUnitType.Sp),
                            modifier = Modifier.align(Center)
                        )
                        Image(
                            imageVector = Icons.Default.Close,
                            contentDescription = null,
                            modifier = Modifier
                                .align(
                                    CenterEnd
                                )
                                .size(48.dp)
                                .clickable {
                                    isShowPop.value = false
                                },
                            contentScale = ContentScale.Inside
                        )
                    }
                }

                items(array.size) {
                    TextButton(
                        onClick = {
                            isShowPop.value = false
                            onItemClick(it)
                        }, modifier = Modifier
                            .fillMaxWidth()
                            .height(48.dp)
                    ) {
                        Text(
                            text = array[it],
                            color = Color.DarkGray,
                            fontSize = TextUnit(15f, TextUnitType.Sp)
                        )
                    }
                }
            }
        }
    }


}

引用方式

BottomSheetPopWindow(
    isShowPop = popState,
    array = arrayOf("Item1", "Item2", "Item3"),
    onItemClick = {
        Toast.makeText(context, "点击了$it", Toast.LENGTH_SHORT).show()
    },
    onOutSideDismiss = {
        it.value = false
    })

实现效果

E7E1C294-741D-4507-B949-4BB0DF9D844D.png

实现底部弹框还存在很多方式,特别场景还需要弹出动画,需要可自行去官网学习