Composable 自定义 - 一些自用控件的功能实现

一、去除点击涟漪(水波纹)效果

1.1 Modifier.clickable { }

针对的是给控件添加点击功能的场景(如Text),通过 Modifier 扩展函数实现。

fun Modifier.clickableNoRipple(
    onClick: () -> Unit
) = composed {
    this.then(
        Modifier.clickable(
            onClick = onClick,
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        )
    )
}

1.2 onClick( )

针对的是自带点击效果的控件,通过主题实现。

@Composable
fun AppTheme(
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalRippleTheme provides NoRippleTheme
    ) {
        content()
    }
}

object NoRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor(): Color {
        return Color.Transparent
    }
    @Composable
    override fun rippleAlpha(): RippleAlpha {
        return RippleAlpha(0f, 0f, 0f, 0f)
    }
}

二、倒计时

private val countDownTimer = flow {
    repeat(5) {
        emit(5 - it)    //从0开始的
        delay(1000)
    }
}

@Composable
fun CountDownTimerDialog(
    isShowDialog: Boolean,
    onDismissRequest: () -> Unit,
    text: String,
) {
    if (isShowDialog) {
        val countDownTimer = countDownTimer.collectAsState(initial = 0).value
        Dialog(
            onDismissRequest = onDismissRequest,
            properties = DialogProperties(
                dismissOnBackPress = false,
                dismissOnClickOutside = false
            )
        ) {
            Text(
                modifier = Modifier.wrapContentSize(Alignment.Center),
                text = "倒计时:${ countDownTimer}秒"
            )
        }
    }
}

三、下拉菜单

@Preview
@Composable
private fun PulldownMenuPreview() {
    var selectedIndex by remember{ mutableStateOf(0) }
    Box(modifier = Modifier.fillMaxSize()) {
        PulldownMenu(
            modifier = Modifier.width(200.dp),
            items = immutableListOf("1级部门","2级部门","3级部门"),
            selectedIndex = selectedIndex,
            onSelectedIndexChange =  { selectedIndex = it }
        )
    }
}

@Composable
fun PulldownMenu(
    items: List<String>,
    selectedIndex: Int,
    onSelectedIndexChange:(Int) -> Unit,
    modifier: Modifier = Modifier,
    isDefaultHint: Boolean = true    //第一个默认选项是否显示灰色
) {
    var expanded by remember { mutableStateOf(false)}
    BoxWithConstraints(
        modifier = modifier,
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .height(AppDimens.heightButton)
                .border(
                    width = 1.dp,
                    color = AppColors.Hint,
                    shape = RoundedCornerShape(AppDimens.radius)
                )
                .clickableNoRipple { if (items.isNotEmpty()) expanded = !expanded }
                .padding(horizontal = AppDimens.paddingContent),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier.weight(1F),
                text = items[selectedIndex],
                maxLines = 1,
                fontSize = AppDimens.textPrimary,
                color = if (isDefaultHint && selectedIndex == 0) {
                    AppColors.Hint
                } else {
                    AppColors.Black
                }
            )
            Icon(
                imageVector = if (expanded) {
                    Icons.Default.KeyboardArrowUp
                } else {
                    Icons.Default.KeyboardArrowDown
                },
                contentDescription = null,
                tint = AppColors.Hint
            )
        }

        DropdownMenu(
            modifier = Modifier
                .width(maxWidth)
                .background(AppColors.White)
                .padding(horizontal = AppDimens.marginContent)
                .sizeIn(maxHeight = 400.dp),  //设置下拉项最大显示高度
            expanded = expanded,
            onDismissRequest = { expanded = false }
        ) {
            items.forEachIndexed { index, label ->
                DropdownMenuItem(
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(
                            color = if (selectedIndex == index) AppColors.Primary else AppColors.White,
                            shape = RoundedCornerShape(AppDimens.radius)
                        ),
                    text = {
                        Text(
                            modifier = Modifier.fillMaxWidth().padding(start = AppDimens.marginContent),
                            text = label,
                            color = if (selectedIndex == index) AppColors.White else AppColors.Black,
                        )
                    },
                    onClick = {
                        onSelectedIndexChange(index)
                        expanded =false
                    },
                    contentPadding = PaddingValues(0.dp)
                )
            }
        }
    }
}

四、对话框

4.1 单按钮对话框

enum class DialogPic {
    SUCCESS, ERROR, NOTIFY
}

@Preview
@Composable
private fun SingleButtonDialogPreview() {
    SingleButtonDialog(
        isShowDialog = true,
        onDismissRequest = {},
        pic = DialogPic.SUCCESS,
        contentText = "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈",
        buttonText = "确认",
        onButtonClick = {}
    )
}

/**
 * 单按钮对话框
 */
@Composable
fun SingleButtonDialog(
    isShowDialog: Boolean,
    onDismissRequest: () -> Unit,
    pic: DialogPic,
    contentText: String,
    onButtonClick: () -> Unit = onDismissRequest,
    buttonText: String = "确认",
) {
    if (isShowDialog) {
        Dialog(
            onDismissRequest = onDismissRequest,
            properties = DialogProperties(
                dismissOnBackPress = false,
                dismissOnClickOutside = false
            )
        ) {
            Column(
                modifier = Modifier
                    .width(AppDimens.widthDialog)
                    .height(AppDimens.heightDialog)
                    .background(
                        color = AppColors.White,
                        shape = RoundedCornerShape(AppDimens.radius)
                    )
                    .padding(AppDimens.paddingContent),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.SpaceEvenly
            ) {
                Image(
                    modifier = Modifier.size(AppDimens.dialogPic),
                    painter = painterResource(
                        when (pic) {
                            DialogPic.SUCCESS -> R.drawable.pic_success
                            DialogPic.ERROR -> R.drawable.pic_error
                            DialogPic.NOTIFY -> R.drawable.pic_notify
                        }
                    ),
                    contentDescription = null
                )
                Text(
                    modifier = Modifier.wrapContentSize(Alignment.Center),
                    text = contentText,
                    fontWeight = FontWeight.Bold,
                    textAlign = TextAlign.Center,
                    fontSize = AppDimens.textTitle
                )
                FilledButton(
                    modifier = Modifier.width(128.dp),
                    text = buttonText,
                    onButtonClicked = onButtonClick
                )
            }
        }
    }
}

4.2 双按钮对话框

enum class DialogPic {
    SUCCESS, ERROR, NOTIFY
}

@Preview
@Composable
private fun TwoButtonDialogPreview() {
    TwoButtonDialog(
        isShowDialog = true,
        onDismissRequest = {},
        pic = DialogPic.NOTIFY,
        contentText = "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈",
        noteText = "说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明说明",
        positiveButtonText = "确认",
        negativeButtonText = "取消",
        isShowNote = true,
        onPositiveButtonClick = {},
        onNegativeButtonClicked = {}
    )
}

/**
 * 双按钮对话框
 */
@Composable
fun TwoButtonDialog(
    isShowDialog: Boolean,
    onDismissRequest: () -> Unit,
    pic: DialogPic,
    contentText: String,
    onPositiveButtonClick: () -> Unit,
    positiveButtonText: String = "确认",
    negativeButtonText: String = "取消",
    onNegativeButtonClicked: () -> Unit = onDismissRequest,
    isShowNote: Boolean = false,
    noteText: String = ""
) {
    if (isShowDialog) {
        Dialog(
            onDismissRequest = onDismissRequest,
            properties = DialogProperties(
                dismissOnBackPress = false,
                dismissOnClickOutside = false
            )
        ) {
            Column(
                modifier = Modifier
                    .width(AppDimens.widthDialog)
                    .height(AppDimens.heightDialog)
                    .background(
                        color = AppColors.White,
                        shape = RoundedCornerShape(AppDimens.radius)
                    )
                    .padding(AppDimens.paddingContent),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.SpaceEvenly
            ) {
                Image(
                    modifier = Modifier.size(AppDimens.dialogPic),
                    painter = painterResource(
                        when (pic) {
                            DialogPic.SUCCESS -> R.drawable.pic_success
                            DialogPic.ERROR -> R.drawable.pic_error
                            DialogPic.NOTIFY -> R.drawable.pic_notify
                        }
                    ),
                    contentDescription = null
                )
                Text(
                    text = contentText,
                    fontWeight = FontWeight.Bold,
                    textAlign = TextAlign.Center,
                    fontSize = AppDimens.textTitle
                )
                if (isShowNote) {
                    Text(
                        text = noteText,
                        fontSize = AppDimens.textPrimary,
                        textAlign = TextAlign.Center
                    )
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    StrokedButton(
                        text = negativeButtonText,
                        onButtonClicked = onNegativeButtonClicked
                    )
                    FilledButton(
                        modifier = Modifier.padding(start = 16.dp),
                        text = positiveButtonText,
                        onButtonClicked = onPositiveButtonClick
                    )
                }
            }
        }
    }
}

五、输入框

5.1 提示

5.1.1 无边框

@Preview
@Composable
private fun BasicInputBoxPreview() {
    var text by remember { mutableStateOf("") }
    Box(
        modifier = Modifier
            .width(100.dp)
            .height(50.dp),
        contentAlignment = Alignment.Center
    ) {
        BasicInputBox(
            modifier = Modifier
                .width(194.dp)
                .height(37.dp),
            value = text,
            onValueChange = { text = it },
            hint = "请输入文字"
        )
    }
}

/**
 * 带提示的输入框
 */
@Composable
fun BasicInputBox(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    hint: String = "",
    singleLine: Boolean = true,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
    BasicTextField(
        modifier = modifier,
        value = value,
        onValueChange = onValueChange,
        keyboardOptions = keyboardOptions,
        keyboardActions = keyboardActions,
        singleLine = singleLine,
        textStyle = TextStyle.Default.copy(color = AppColors.Black, fontSize = AppDimens.textPrimary),
        decorationBox = { innerTextField ->
            Row(
                modifier = Modifier.padding(horizontal = AppDimens.marginContent),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Box(contentAlignment = Alignment.CenterStart) {
                    if (value.isEmpty()) {
                        Text(
                            text = hint,
                            color = AppColors.Hint,
                            fontSize = AppDimens.textPrimary,
                            textAlign = TextAlign.Center
                        )
                    }
                    innerTextField()
                }
            }
        }
    )
}

5.1.2 有边框

@Preview
@Composable
private fun BasicBorderInputBoxPreview() {
    var text by remember { mutableStateOf("") }
    Box(
        modifier = Modifier
            .width(250.dp)
            .height(50.dp),
        contentAlignment = Alignment.Center
    ) {
        BasicInputBox(
            modifier = Modifier
                .width(194.dp)
                .height(37.dp),
            value = text,
            onValueChange = { text = it },
            hint = "请输入文字",
        )
    }
}

/**
 * 带边框的基本输入框
 */
@Composable
fun BasicBorderInputBox(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    hint: String = "",
    singleLine: Boolean = true,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
    BasicTextField(
        modifier = modifier,
        value = value,
        onValueChange = onValueChange,
        keyboardOptions = keyboardOptions,
        keyboardActions = keyboardActions,
        singleLine = singleLine,
        textStyle = TextStyle.Default.copy(color = AppColors.TextContent, fontSize = 13.sp),
        decorationBox = { innerTextField ->
            Row(
                modifier = Modifier
                    .border(
                        width = 1.dp,
                        color = AppColors.TextHint,
                        shape = RoundedCornerShape(AppDimens.Radius_InputCorner)
                    )
                    .padding(horizontal = 10.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Box(contentAlignment = Alignment.CenterStart) {
                    if (value.isEmpty()) {
                        Text(text = hint, color = AppColors.TextHint, textAlign = TextAlign.Center)
                    }
                    innerTextField()
                }
            }
        }
    )
}

5.2 标签

@Preview
@Composable
private fun LabelInputBoxPreview() {
    Box(modifier = Modifier.padding(10.dp)) {
        TextInputItem(
            title = "姓名",
            value = "",
            hint = "请输入姓名",
            required = true,
            onValueChange = {}
        )
    }
}

/**
 * 带标签的输入框
 */
@Composable
fun LabelInputBox(
    title: String,
    hint: String,
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    required: Boolean = false,   //是否必填(显示星号)
    singleLine: Boolean = true,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
    Row(
        modifier = modifier,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            text = buildAnnotatedString {
                if (required){
                    withStyle(
                        SpanStyle(fontSize = AppDimens.Text_InputLabel, color = AppColors.TextBlue)
                    ) { append("* ") }
                }
                withStyle(
                    SpanStyle(fontSize = AppDimens.Text_InputHint, color = AppColors.TextOption, fontWeight = FontWeight.Bold)
                ) { append(title) }
            }
        )
        BasicBorderInputBox(
            modifier = Modifier
                .padding(start = 10.dp)
                .width(194.dp)
                .height(37.dp),
            value = value,
            onValueChange = onValueChange,
            hint = hint,
            keyboardOptions = keyboardOptions,
            keyboardActions = keyboardActions,
            singleLine = singleLine
        )
    }
}

5.3 按钮(搜索栏)

@Preview(showBackground = true)
@Composable
private fun PreviewSearchBar() {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(100.dp),
        contentAlignment = Alignment.Center
    ) {
        SearchBar(
            text = "",
            onTextChange = {},
            onSearchButtonClicked = {},
        )
    }
}

/**
 * 搜索栏
 */
@Composable
fun SearchBar(
    text: String,
    onTextChange: (String) -> Unit,
    onSearchButtonClicked: () -> Unit,
    modifier: Modifier = Modifier
) {
    Row(
        modifier = modifier
            .fillMaxWidth()
            .background(
                color = AppColors.White,
                shape = RoundedCornerShape(AppDimens.Radius_Background)
            )
            .padding(10.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        BasicBorderInputBox(
            modifier = Modifier
                .height(43.dp)
                .width(213.dp),
            value = text,
            onValueChange = onTextChange,
            hint = "请输入签离人员的姓名/手机号",
            singleLine = true
        )
        FilledButton(
            modifier = Modifier
                .padding(start = 10.dp)
                .height(43.dp)
                .width(72.dp),
            text = "查询",
            onButtonClicked = onSearchButtonClicked
        )
    }
}

六、标题栏

6.1 屏幕标题栏

@Preview
@Composable
private fun TitleBarPreview() {
    ScreenTitleBar(
        title = "默认标题",
        isShowLeftButton = true,
        isShowRightButton = true
    )
}

@Composable
fun ScreenTitleBar(
    title: String,
    modifier: Modifier = Modifier,
    isShowLeftButton: Boolean = false,
    isShowLeftText: Boolean = false,
    isShowRightButton: Boolean = false,
    @DrawableRes leftPic: Int = R.drawable.icon_title_menu,
    @DrawableRes rightPic: Int = R.drawable.icon_title_home,
    leftText: String = "返回",
    onLeftClick: () -> Unit = {},
    onRightClick: () -> Unit = {}
) {
    Box(
        modifier = modifier
            .fillMaxWidth()
            .height(AppDimens.heightTitleBar)
            .background(color = AppColors.Primary)
            .padding(horizontal = 10.dp),
    ) {
        Row(
            modifier= Modifier
                .align(Alignment.CenterStart)
                .clickableNoRipple { onLeftClick() },
            verticalAlignment = Alignment.CenterVertically
        ) {
            if (isShowLeftButton) {
                Icon(
                    modifier = Modifier.size(20.dp),
                    painter = painterResource(id = leftPic),
                    contentDescription = null,
                    tint = AppColors.White
                )
            }
            if (isShowLeftText) {
                Text(
                    modifier = Modifier.padding(start = AppDimens.marginContent),
                    text = leftText,
                    color = AppColors.White,
                    fontSize = AppDimens.textTitle
                )
            }
        }
        Text(
            modifier= Modifier.align(Alignment.Center),
            text = title,
            color = AppColors.White,
            fontSize = AppDimens.textTitle,
            textAlign = TextAlign.Center
        )
        if (isShowRightButton) {
            Icon(
                modifier= Modifier
                    .size(25.dp)
                    .align(Alignment.CenterEnd)
                    .clickableNoRipple { onRightClick() },
                painter = painterResource(id = rightPic),
                contentDescription = null,
                tint = AppColors.White
            )
        }
    }
}

6.2 对话框标题栏

@Preview
@Composable
private fun DialogTitleBarPreview() {
    DialogTitleBar(
        title = "默认标题",
        onCloseButtonClick = {}
    )
}

@Composable
fun DialogTitleBar(
    title: String,
    onCloseButtonClick: () -> Unit
) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(AppDimens.heightDialogTitleBar)
            .background(AppColors.Primary)
    ) {
        Text(
            modifier = Modifier.align(Alignment.Center),
            text = title,
            color = AppColors.White,
            fontSize = AppDimens.textTitle
        )
        Icon(
            modifier = Modifier
                .padding(end = AppDimens.paddingContent)
                .size(20.dp)
                .align(Alignment.CenterEnd)
                .clickableNoRipple { onCloseButtonClick() },
            painter = painterResource(R.drawable.icon_close),
            contentDescription = null,
            tint = AppColors.White
        )
    }
}

七、按钮

7.1 填充

@Preview(showBackground = true)
@Composable
private fun FilledButtonPreview() {
    Column(
        modifier = Modifier.padding(10.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        FilledButton(
            text = "默认效果",
            onButtonClicked = {},
        )
        FilledButton(
            modifier = Modifier.padding(top = 10.dp),
            text = "显示图标",
            onButtonClicked = {},
            isShowLeft = true,
        )
    }
}

/**
 * 填充按钮
 */
@Composable
fun FilledButton(
    text: String,
    onButtonClicked: () -> Unit,
    modifier: Modifier = Modifier,
    textColor: Color = AppColors.White,
    backgroundColor: Color = AppColors.Primary,
    isShowLeft: Boolean = false,
    leftPic:  ImageVector? = null
) {
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .background(
                color = backgroundColor,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = if (isShowLeft) 13.dp else 25.dp)
            .clickableNoRipple { onButtonClicked() },
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        if (isShowLeft) {
            Icon(
                modifier = Modifier
                    .padding(end = 5.dp)
                    .size(17.dp),
                imageVector = leftPic ?: Icons.Default.TouchApp,
                contentDescription = null,
                tint = AppColors.White
            )
        }
        Text(
            text = text,
            color = textColor,
            textAlign = TextAlign.Center,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
    }

}

7.2 描边

@Preview(showBackground = true)
@Composable
private fun StrokedButtonPreview() {
    Column(
        modifier = Modifier.padding(10.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        StrokedButton(
            text = "默认效果",
            onButtonClicked = {}
        )
        StrokedButton(
            modifier = Modifier.padding(top = 10.dp),
            text = "显示图标",
            onButtonClicked = {},
            isShowLeft = true,
        )
    }
}

/**
 * 描边按钮
 */
@Composable
fun StrokedButton(
    text: String,
    onButtonClicked: () -> Unit,
    modifier: Modifier = Modifier,
    isShowLeft: Boolean = false,
    leftPic:  ImageVector? = null
) {
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .border(
                width = 1.dp,
                color = if (isShowLeft) AppColors.Primary else AppColors.Hint,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = if (isShowLeft) 13.dp else 25.dp)
            .clickableNoRipple { onButtonClicked() },
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        if (isShowLeft) {
            Icon(
                modifier = Modifier
                    .padding(end = 5.dp)
                    .size(17.dp),
                imageVector = leftPic ?: Icons.Default.TouchApp,
                contentDescription = null,
                tint = AppColors.Primary
            )
        }
        Text(
            text = text,
            color = if (isShowLeft) AppColors.Primary else AppColors.Black,
            textAlign = TextAlign.Center,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
    }
}

7.3 展开

@Preview(showBackground = true)
@Composable
private fun ExpandButtonPreview() {
    Column(
        modifier = Modifier.padding(10.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        ExpandButton(
            isExpand = true,
            onButtonClicked = {}
        )
        ExpandButton(
            modifier = Modifier.padding(top = 10.dp),
            isExpand = false,
            onButtonClicked = {}
        )
    }
}

/**
 * 展开按钮
 */
@Composable
fun ExpandButton(
    isExpand: Boolean,
    onButtonClicked: () -> Unit,
    modifier: Modifier = Modifier,
    expandText: String = "点击展开",
    collapseText: String = "点击收起",
    color: Color = AppColors.Primary,
) {
    Row(
        modifier = modifier
            .clickableNoRipple {
                onButtonClicked()
            },
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            text = if (isExpand) collapseText else expandText,
            color = color,
            fontSize = AppDimens.textPrimary
        )
        Icon(
            imageVector = if (isExpand) {
                Icons.Default.KeyboardArrowUp
            } else {
                Icons.Default.KeyboardArrowDown
            },
            contentDescription = null,
            tint = color
        )
    }
}

八、日期选择器

8.1 单日期

@Preview
@Composable
private fun SingleButtonDatePickerPreView() {
    var value by remember { mutableStateOf("选择时间") }
    PreviewView {
        SingleButtonDatePickerView(
            value = value,
            onValueChange = { value = it },
        )
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SingleButtonDatePickerView(
    modifier: Modifier = Modifier,
    value: String = "选择时间",
    onValueChange: (String) -> Unit,
    borderColor: Color = AppColors.Hint,
) {
    var isOpenDialog by remember { mutableStateOf(false) }
    var isDefaultValue by remember { mutableStateOf(true) }
    val datePickerState = rememberDatePickerState()
    val sdf = remember { SimpleDateFormat("yyy-MM-dd") }
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .border(
                width = 1.dp,
                color = borderColor,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = AppDimens.paddingContent)
            .clickableNoRipple { isOpenDialog = true },
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = value,
            maxLines = 1,
            color = if(isDefaultValue) AppColors.Hint else AppColors.Black,
            fontSize = AppDimens.textPrimary,
        )
        Icon(
            modifier = Modifier.padding(start = 5.dp),
            imageVector = Icons.Default.CalendarMonth,
            contentDescription = null,
            tint = AppColors.Hint
        )
    }
    if (isOpenDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),   //背景色(这个在下面的DatePicker中设置无效)
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = datePickerState.selectedDateMillis
                            if (date != null) {
                                onValueChange(sdf.format(date))
                                isDefaultValue = false
                            } else {
                                isDefaultValue = true
                            }
                            isOpenDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = datePickerState,
                colors = DatePickerDefaults.colors(
                    todayDateBorderColor = AppColors.Primary,   //默认选中的当天日期的边框色
                    selectedDayContentColor = AppColors.White,  //选中的文字颜色
                    selectedDayContainerColor = AppColors.Primary,  //选中的填充颜色
                )
            )
        }
    }
}

8.2 双日期

@Preview
@Composable
private fun TwoButtonDatePickerPreview() {
    var startDate by remember { mutableStateOf("到期开始时间") }
    var endDate by remember { mutableStateOf("到期结束时间") }
    PreviewView {
        TwoButtonDatePickerView(
            startDate = startDate,
            endDate = endDate,
            onStartChange = { startDate = it },
            onEndChange = { endDate = it },
        )
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TwoButtonDatePickerView(
    modifier: Modifier = Modifier,
    startDate: String = "开始时间",
    endDate: String = "结束时间",
    onStartChange: (String) -> Unit,
    onEndChange: (String) -> Unit,
    borderColor: Color = AppColors.Hint,
) {
    var isOpenStartDialog by remember { mutableStateOf(false) }
    var isOpenEndDialog by remember { mutableStateOf(false) }
    val startDatePickerState = rememberDatePickerState()
    val endDatePickerState = rememberDatePickerState()
    var isStartDateDefault by remember { mutableStateOf(true) }
    var isEndDateDefault by remember { mutableStateOf(true) }
    val sdf = remember { SimpleDateFormat("yyy-MM-dd") }
    Row(
        modifier = modifier
            .height(AppDimens.heightButton)
            .border(
                width = 1.dp,
                color = borderColor,
                shape = RoundedCornerShape(AppDimens.radius)
            )
            .padding(horizontal = AppDimens.paddingContent),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(
            modifier = Modifier.clickableNoRipple { isOpenStartDialog = true },
            text = startDate,
            color = if (isStartDateDefault) AppColors.Hint else AppColors.Black,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
        Icon(
            modifier = Modifier
                .size(25.dp)
                .padding(horizontal = 5.dp),
            painter = painterResource(id = R.drawable.icon_arrow_right),
            contentDescription = null,
            tint = AppColors.Hint
        )
        Text(
            modifier = Modifier.clickableNoRipple { isOpenEndDialog = true },
            text = endDate,
            color = if (isEndDateDefault) AppColors.Hint else AppColors.Black,
            maxLines = 1,
            fontSize = AppDimens.textPrimary
        )
        Icon(
            modifier = Modifier.padding(start = 5.dp),
            imageVector = Icons.Default.CalendarMonth,
            contentDescription = null,
            tint = AppColors.Hint
        )
    }
    if (isOpenStartDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenStartDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = startDatePickerState.selectedDateMillis
                            if (date != null) {
                                onStartChange(sdf.format(date))
                                isStartDateDefault = false
                            } else {
                                isStartDateDefault = true
                            }
                            isOpenStartDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenStartDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = startDatePickerState,
                colors = DatePickerDefaults.colors(
                    todayDateBorderColor = AppColors.Primary,
                    selectedDayContentColor = AppColors.White,
                    selectedDayContainerColor = AppColors.Primary,
                )
            )
        }
    }
    if (isOpenEndDialog) {
        DatePickerDialog(
            onDismissRequest = { isOpenEndDialog = false},
            colors = DatePickerDefaults.colors(containerColor = AppColors.White),
            confirmButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            val date = endDatePickerState.selectedDateMillis
                            if (date != null) {
                                onEndChange(sdf.format(date))
                                isEndDateDefault = false
                            } else {
                                isEndDateDefault = true
                            }
                            isOpenEndDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "确定",
                    color = AppColors.Primary,
                    fontSize = AppDimens.textTitle
                )
            },
            dismissButton = {
                Text(
                    modifier = Modifier
                        .clickableNoRipple {
                            isOpenEndDialog = false
                        }
                        .padding(end = 20.dp),
                    text = "取消",
                    color = AppColors.textGray,
                    fontSize = AppDimens.textTitle
                )
            }
        ) {
            DatePicker(
                state = endDatePickerState,
                colors = DatePickerDefaults.colors(
                    containerColor = AppColors.White,
                    todayDateBorderColor = AppColors.Primary,
                    selectedDayContentColor = AppColors.White,
                    selectedDayContainerColor = AppColors.Primary,
                )
            )
        }
    }
}

九、预览

9.1 屏幕

/**
 * 带上主题以1920*1080预览
 */
@Composable
fun PreviewScreen(
    content: @Composable () -> Unit
) {
    AppTheme {
        Box(modifier = Modifier
            .width(960.dp)
            .height(540.dp)
            .background(color = AppColors.Background),
            contentAlignment = Alignment.Center
        ) {
            content()
        }
    }
}

9.2 控件

@Composable
fun PreviewView(
    background: Color? = null,
    isPaddingMode: Boolean = true,
    content: @Composable () -> Unit
) {
    AppTheme {
        if (isPaddingMode) {
            Box(modifier = Modifier
                .background(background ?: Color.Gray)
                .padding(10.dp),
                contentAlignment = Alignment.Center
            ) {
                content()
            }
        } else {
            Box(modifier = Modifier
                .width(360.dp)
                .height(300.dp)
                .background(background ?: Color.Gray),
                contentAlignment = Alignment.Center
            ) {
                content()
            }
        }

    }
}

十、平板强制横屏,手机强制竖屏

fun isPad(context: Context): Boolean {
    val isPad = context.resources.configuration.screenLayout and(Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE
    val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val display = wm.defaultDisplay
    val dm = DisplayMetrics()
    display.getMetrics(dm)
    val x = (dm.widthPixels / dm.xdpi).pow(2)
    val y = (dm.heightPixels / dm.ydpi).pow(2)
    val screenInches = sqrt(x + y)
    return isPad || screenInches >= 7.0
}

Activity{
    requestedOrientation = if (MachineUtil.isPad(APP.context)) {
        ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
    } else {
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值