添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天, 点击查看活动详情

本篇分别对常用的组件:图标(Icon) 按钮(Button) 输入框(TextField)的使用方法及各参数使用进行讲解,参考了不少文章,且费了不少时间去时间去一一实践,希望对各位带来些帮助 :blush:

本系列以往文章请查看此分类链接 jetpackcompose学习

图标Icon使用

Icon接收三种参数,如下图

//第一种就不多说,就是一个drawble对象
//获取图片资源,R.drawble.xx或R.mipmap.xx
Icon(painter = painterResource(id = R.drawable.head1_1024), null)
//自带的图标
Icon(Icons.Filled.Search, null)

Compose内置了几十个常用的图标,我们使用枚举类型即可使用

Icons里面定了5种类型Outlined Filled Sharp TwoTone Rounded,可以根据自己的需要选择不同的类型,如填充型(Filled)或者是轮廓型(Outlined)

Icon的构造方法参数简单说明下 contentDescription是给无障碍人使用的文本描述,考虑到一些视觉障碍的人使用,所以有个这个属性,会使用TTS语音播放将contentDescription属性读出来,告知用户此按钮的作用

tint则是图标颜色的设置

Row() {
    Icon(Icons.Outlined.Settings, contentDescription = null, tint = Color.Red)
    Icon(Icons.Filled.Settings, contentDescription = null, tint = Color.Blue)
    Icon(Icons.Sharp.Settings, contentDescription = null, tint = Color.Green)
    Icon(Icons.TwoTone.Settings, contentDescription = null, tint = Color.Red)
    Icon(Icons.Rounded.Settings, contentDescription = null, tint = Color.Black)

效果如下图所示

PS:具体的图标名称写的时候会有代码提示

不过默认常用的就那40几个,其他的图标就没有包含在内,当然,如果你想用的话,也有方法实现,需要导入material-icons-extended依赖即可

dependencies {
  implementation "androidx.compose.material:material-icons-extended:$compose_version"

但是全套图标会导致打包后的apk文件过大,所以官方推荐使用导入图标文件的方法,详情可参考官方文档

按钮 Button

Button这个组件,官方已经实现了Material Design的效果,一般来说我们直接使用这个即可

除此之外,官方也是给我们封装了不同类型的Button,分别为IconButton TextButton OutlinedButton IconToggleButton

上面我们刚讲了图标,下面就先讲些图标按钮IconButton的使用方式吧

和以往我们使用的按钮不一样,这里的按钮可以看做是一个布局控件,我们需要设置文字也就是往里面添加一个Text组件,这就是compose和传统Android的xml的不同之处

由上面这点,所以我们在代码层面就十分灵活,可以实现各种效果(如带有图标的按钮),下面来个例子

Button(onClick = { println("点击了按钮")}){
    Icon(Icons.Default.Search,contentDescription = null)
    Text(text = "测试")

上面的代码实现的效果就是有个图标在左侧

我们先看下Button的定义,其实封装好的方法,代码如下所示

fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    elevation: ButtonElevation? = ButtonDefaults.elevation(),
    shape: Shape = MaterialTheme.shapes.small,
    border: BorderStroke? = null,
    colors: ButtonColors = ButtonDefaults.buttonColors(),
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
    content: @Composable RowScope.() -> Unit

Buttoncontent参数(也就是上面lambda),传入多个组件,Button会将其按照水平方式排列(即Button可视为Row布局)

由于kotlin的语法特性,所以我们可以在后面以花括号写个lambda函数

这里先讲下比较简单的参数:

  • onClick是点击事件.也是接收一个函数
  • modifier是修饰符,本章先不使用,之后出个篇文章,专门讲解下这个的用法
  • enabled按钮是否可用(不可用默认是灰色,可用默认是蓝色),当然这里的默认的禁用和可用的颜色可可以调整,详情请见下面的colors参数
  • 接下来就是稍微有点复杂的参数说明了,因为用法与之前原生Button有所区别,这里特别分成一小节讲解,方便目录查阅

    1.elevation 阴影

    Button的阴影参数是有有默认值的,我们也可以使用下面的方法进行数值的修改

    ButtonDefaults.elevation(defaultElevation,pressedElevation,disabledElevation)
    
  • defaultElevation表示默认的阴影
  • pressedElevation表示按下时的阴影
  • disabledElevation表示未启用时候的阴影
  • Button(
        enabled = true,
        onClick = { /*TODO*/ },
        elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp)
        Text(text = "阴影按钮")
    Button(
        enabled = false,
        onClick = { /*TODO*/ },
        elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp)
        Text(text = "禁用状态的阴影按钮")
    

    PS:使用的时候,发现导包会失败,给了些奇怪的东西...建议复制下ButtonDefaults.elevation(),再输入参数

    2.shape 形状

    Android官方给我们提供了以下四种形状,我从代码提示里只看到有这四种147

  • RoundedCornerShape 圆角形状
  • CutCornerShape 切角形状
  • AbsoluteRoundedCornerShape 绝对圆角形状
  • AbsoluteCutCornerShape 绝对切角形状
  • 这里从字面翻译知道其的意思,但是具体圆角形状和绝对圆角形状有什么区别,实际测试也有,但是没法看出来有什么区别,官方的文档也是解释的有点模糊

    后来者如果知道,可以在评论区回复下,感谢~

    上面四种类的接收参数其实是一样的,这里就截个图给大家看看

    我们常用就是使用dp定位进行设置,如

    RoundedCornerShape(10.dp) //设置10dp的圆角
    RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp)
    
  • topStart 左上角
  • topEnd 右上角
  • bottomStart 左下角
  • bottomEnd 右下角
  • PS: 记住start是左,end是右,上面就比较好记了

    Button(
        onClick = { /*TODO*/ },
        elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp),
        shape = RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp)
        Text(text = "按钮")
    

    我们可以实现如下图的效果

    代码如下:

    Modifier.size(50.dp,50.dp)是用来设置宽高的

    Row() {
    	//固定长宽一样,圆角设置为50%即为圆形
        Button(
            modifier = Modifier.size(50.dp,50.dp),
            onClick = { /*TODO*/ },
            shape = RoundedCornerShape(50),
            Text(text = "")
    	//固定长宽一样,切角设置为50%即为菱形
        Button(
            modifier = Modifier.size(50.dp,50.dp),
            onClick = { /*TODO*/ },
            shape = CutCornerShape(50.dp),
            Text(text = "")
    	//左上角设置圆角
        Button(
            onClick = { /*TODO*/ },
            shape = RoundedCornerShape(topStart = 20.dp),
            Text(text = "按钮")
    	//圆角设置为50%
        Button(
            onClick = { /*TODO*/ },
            shape = RoundedCornerShape(50),
            border = BorderStroke(1.dp, Color.Green),
            colors = ButtonDefaults.buttonColors(),
            Text(text = "按钮111")
        Button(
            modifier = Modifier.size(50.dp,50.dp),
            onClick = { /*TODO*/ },
            shape = CutCornerShape(25),
            border = BorderStroke(1.dp, Color.Green),
            colors = ButtonDefaults.buttonColors(),
            Text(text = "按钮111")
    

    3.border 边框

    边框就简单了,使用BorderStroke,接收两个参数,一个是边框的宽度,另外一个则是边框的颜色

    BorderStroke(1.dp,color = Color.Black)
    
    Button(
        onClick = { /*TODO*/ },
        elevation = ButtonDefaults.elevation(4.dp, 10.dp, 0.dp),
        shape = RoundedCornerShape(topStart = 5.dp,topEnd = 6.dp,bottomEnd = 10.dp,bottomStart = 10.dp),
        border = BorderStroke(1.dp, Color.Green)
        Text(text = "边框按钮")
    

    4.colors 颜色

    可以通过下面的方法进行颜色的参数的设置

    ButtonDefaults.buttonColors(backgroundColor,contentColor,disabledBackgroundColor,disabledContentColor)
    
  • backgroundColor表示设置背景颜色
  • contentColor表示设置内容颜色这里比如说是登录文本的颜色
  • disabledBackgroundColor表示enable等于false的时候的背景颜色
  • disabledContentColor表示enable等于false时候的内容的颜色
  • PS:这个和之前的一样,直接导包会报错,使用复制大法ButtonDefaults.buttonColors()解决

    5.contentPadding 内容内边距

    contentPadding参数接收一个PaddingValues对象,这个对象的构造方法如下:

  • PaddingValues(all)
  • PaddingValues(horizontal: Dp, vertical: Dp)
  • PaddingValues(start: Dp = 0.dp,top: Dp = 0.dp,end: Dp = 0.dp,bottom: Dp = 0.dp)
  • PaddingValues(10.dp) //所有内边距为10dp
    PaddingValues(10.dp,20.dp) //左右内边距ge10dp,上下内边距各20dp
    PaddingValues(10.dp,15.dp,20.dp,25.dp) //左内边距10dp,上内边距15dp,右内边距20dp,下内边距25dp
    

    6.interactionSource 状态变化

    这个主要是用来按钮的状态说明,我们可以使用这个来达到动态切换按钮样式的效果(如按下按钮的样式效果,松开后按钮的样式),类似我们之前常用selector的xml文件给按钮设置样式

    可以处理状态的,比如按下的时候什么效果,正常时候什么效果。类似之前再布局文件里写Selector

    interactionSource是一个接口,我们需要使用其的实现类MutableInteractionSource

    MutableInteractionSource中提供了三个属性用来获取状态

  • collectIsPressedAsState 按压状态
  • collectIsDraggedAsState 拖动状态
  • collectIsFocusedAsState 焦点状态
  • 我们可以可以此状态来动态更改按钮的样式,如下面的代码

    @Preview(showBackground = true)
    @Composable
    fun DefaultPreview2() {
        val myInteractionSource = remember {
            MutableInteractionSource()
        val pressState = myInteractionSource.collectIsPressedAsState()
        //如果是按压状态则是切角形状,否则则是圆角形状
        val myShape = if(pressState.value) CutCornerShape(10.dp) else RoundedCornerShape(10.dp)
        Column(
            Modifier.padding(20.dp)
            Button(
                onClick = { /*TODO*/ },
                //设置我们定义的shape
                shape = myShape,
                //设置创建的MutableInteractionSource对象
                interactionSource = myInteractionSource
                Text("你好")
    

    效果如下(要按住才会变化):

    构造一个可观察的状态对象可以使用下面的三种方法,唯一有所区别的是,返回值不一样

    val mutableState = remember { mutableStateOf(default) }
    var value by remember { mutableStateOf(default) }
    val (value, setValue) = remember { mutableStateOf(default) }
    

    如下面有个例子:

    val mutableState = remember { mutableStateOf("") } //mutableState是State<String>对象
    var value by remember { mutableStateOf("") } //value是String对象
    val (value, setValue) = remember { mutableStateOf("") } 
    

    一般选用by关键字的那种,代码就比较方便,如果是第一种的话,需要通过mutableState.value才能拿到其保存的数值

    这里强烈建议看下官方的文档状态和Jetpack Compose

    图标按钮IconButton

    IconButton 可以帮助我们生成一个可点击的图标按钮,点击按钮默认会有水波涟漪的点击效果

    IconButton(onClick = { /*TODO*/ }) {
        Icon(Icons.Filled.Search, null)
    

    其实这里里面也可以传多个组件,但是效果可能会变得怪怪的,所以我们就是按照规范来使用吧

    TextButton

    这个其实是扁平按钮,之前有个FlatButton,然后改名成这个了,用法和Button一样,就是样式有所调整

    TextButton(onClick = { /*TODO*/ }) {
        Icon(Icons.Default.Search,contentDescription = null)
        Text(text = "测试")
    

    OutlinedButton

    这个的话,看效果觉得应该是带有边框的按钮,我们也可以根据实际需求改造

    OutlinedButton(onClick = { /*TODO*/ }) {
        Text(text = "测试")
    

    TextField

    TextField在第一篇登录页面也是有提及到,这里再深入了解下各个属性

    TextField 实现分为两个级别:

    1.TextField 是 Material Design 实现。我们建议您选择此实现,因为它遵循的是 Material Design 指南: > - 默认样式为填充 > - OutlinedTextField 是轮廓样式版本 2.BasicTextField 允许用户通过硬件或软件键盘编辑文字,但没有提供提示或占位符等装饰

    TextField(value = "", on
    
    
    
    
        
    ValueChange = {},label = {Text("用户名")})
    OutlinedTextField(value = "", onValueChange = {},label = {Text("用户名")})
    BasicTextField(value = "", onValueChange = {})
    

    简单来说,就是**BasicTextField是超级原生的输入框,其什么样式都没有,可以让我们进行高度的自定义**,而TextFieldOutlinedTextField则是Android官方给我们封装好Material Design样式的控件

    1.label

    获得输入焦点,顶头的文字提示,接收一个组件的lambda表达式,一般传Text,示例代码如下

    TextField(value = "", onValueChange = {},label = {Text("用户名")})
    

    效果如下图所示

    2.leadingIcon

    输入框左边显示内容,leadingIcon接收来自一个组件的lambda表达式,可以是图标、文本或者其他组件

    TextField(
        value = text,
        onValueChange = {
            text = it
        leadingIcon = {
            Icon(Icons.Filled.Search, null)
    

    3.trailingIcon

    输入框右边的内容,和上面的leadingIcon一样的使用,这里不再赘述

    PS:可以在右边放个x的图标,点击删除输入全部文本功能哦 :wink:

    4.singleLine

    设置是否单行,接收一个boolean值

    注: 此参数不能和maxLines参数联用

    TextField(
        value = text,
        onValueChange = {
            text = it
        singleLine =true,
    

    5.color

    设置各种颜色,参数如下(参数真的多,应该够灵活了吧:rofl:)

    @Composable
    fun textFieldColors(
        // 输入的文字颜色
        textColor: Color = LocalContentColor.current.copy(LocalContentAlpha.current),
        // 禁用 TextField 时,已有的文字颜色
        disabledTextColor: Color = textColor.copy(ContentAlpha.disabled),
        // 输入框的背景颜色,当设置为 Color.Transparent 时,将透明
        backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = BackgroundOpacity),
        // 输入框的光标颜色
        cursorColor: Color = MaterialTheme.colors.primary,
        // 当 TextField 的 isError 参数为 true 时,光标的颜色
        errorCursorColor: Color = MaterialTheme.colors.error,
        // 当输入框处于焦点时,底部指示器的颜色
        focusedIndicatorColor: Color = MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high),
        // 当输入框不处于焦点时,底部指示器的颜色
        unfocusedIndicatorColor: Color = MaterialTheme.colors.onSurface.copy(alpha = UnfocusedIndicatorLineOpacity),
        // 禁用 TextField 时,底部指示器的颜色
        disabledIndicatorColor: Color = unfocusedIndicatorColor.copy(alpha = ContentAlpha.disabled),
        // 当 TextField 的 isError 参数为 true 时,底部指示器的颜色
        errorIndicatorColor: Color = MaterialTheme.colors.error,
        // TextField 输入框前头的颜色
        leadingIconColor: Color = MaterialTheme.colors.onSurface.copy(alpha = IconOpacity),
        // 禁用 TextField 时 TextField 输入框前头的颜色
        disabledLeadingIconColor: Color = leadingIconColor.copy(alpha = ContentAlpha.disabled),
        // 当 TextField 的 isError 参数为 true 时 TextField 输入框前头的颜色
        errorLeadingIconColor: Color = leadingIconColor,
        // TextField 输入框尾部的颜色
        trailingIconColor: Color = MaterialTheme.colors.onSurface.copy(alpha = IconOpacity),
        // 禁用 TextField 时 TextField 输入框尾部的颜色
        disabledTrailingIconColor: Color = trailingIconColor.copy(alpha = ContentAlpha.disabled),
        // 当 TextField 的 isError 参数为 true 时 TextField 输入框尾部的颜色
        errorTrailingIconColor: Color = MaterialTheme.colors.error,
        // 当输入框处于焦点时,Label 的颜色
        focusedLabelColor: Color = MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high),
        // 当输入框不处于焦点时,Label 的颜色
        unfocusedLabelColor: Color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),
        // 禁用 TextField 时,Label 的颜色
        disabledLabelColor: Color = unfocusedLabelColor.copy(ContentAlpha.disabled),
        // 当 TextField 的 isError 参数为 true 时,Label 的颜色
        errorLabelColor: Color = MaterialTheme.colors.error,
        // Placeholder 的颜色
        placeholderColor: Color = MaterialTheme.colors.onSurface.copy(ContentAlpha.medium),
        // 禁用 TextField 时,placeholder 的颜色
        disabledPlaceholderColor: Color = placeholderColor.copy(ContentAlpha.disabled)
    

    代码使用:

    TextField(
        value = text,
        onValueChange = {
            text = it
        leadingIcon = {
            Icon(Icons.Filled.Search, null)
        colors = TextFieldDefaults.textFieldColors(
            textColor = Color(0xFF0079D3),
            backgroundColor = Color.Transparent
    

    6.visualTransformation 视图变化

    视图变化是我自己翻译出来的,也不知道准不准确,个人更倾向于理解成输入类型(inputType) :smiley:

    这个有点类似之前原生的inputType,可以改变输入的字符串(如密码或者是输入手机号时候多个-),不过官方目前只实现了PasswordVisualTransformation,其他的需要我们自定义

    使用的话也很简单

    var inputText by remember { mutableStateOf("") }
    TextField(value = inputText, onValueChange = {value-> inputText= value},visualTransformation = PasswordVisualTransformation())
    

    我们如果想实现Android那种带有个图标,点击可以显示密码的输入框,该怎么实现呢?

    其实也很简单,设置个可观察的boolean值,点击图标改变数值即可,具体可参考下面代码

    //密码内容 var inputText by remember { mutableStateOf("") } //是否展示密码(默认是false) var isShowPwd by remember { mutableStateOf(false) } //显示效果(true:显示内偶然你 false:显示密码的"*"好 val myVisualTransformation = if (isShowPwd) VisualTransformation.None else PasswordVisualTransformation() TextField( value = inputText, colors=TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent), onValueChange = { value -> inputText = value }, visualTransformation = myVisualTransformation, trailingIcon = { //根据表示不同,显示不同的图标 if (isShowPwd) { //当前是显示密码,则图标为眼睛 IconButton(onClick = { //更改标志 isShowPwd = !isShowPwd Icon(painter = painterResource(id = R.drawable.eye_show), null) } else { //当前是隐藏密码,则图标为眼睛禁止 IconButton(onClick = { //更改标志 isShowPwd = !isShowPwd Icon(painter = painterResource(id = R.drawable.eye_hide), null)

    上面的两个图标是我自己去iconfont-阿里巴巴矢量图标库上找,效果如下:

    补充(自定义VisualTransformation)

    注意: 经过实践发现,这个只是改变了显示的数值而已:sweat_smile:,实际上你输入什么,保存的数值还是那个,单纯只是TextField没显示而已,不是很清楚这个操作,那这样是不能实现限制长度的功能,密码显示星号这种效果应该没啥问题

    此功能有待讨论,或者是可能官方后面会更新长度限制等功能?

    上面说到官方只实现了一个简单的密码输入类型,那如果我们想自定义该如何实现呢?

    好在官方也是在API文档中给了个例子,可以实现输入信用卡号,以-隔开的效果

    我们先看下官方的代码及效果(有点坑,官方只给出了一部分代码,稍微琢磨了一番才知道它是实现了VisualTransformation接口,并重写了filter()方法)

    class CardVisualTransformation : VisualTransformation{
        override fun filter(text: AnnotatedString): TransformedText {
            // Making XXXX-XXXX-XXXX-XXXX string.
            val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
            var out = ""
            for (i in trimmed.indices) {
                out += trimmed[i]
                if (i % 4 == 3 && i != 15) out += "-"
             * The offset translator should ignore the hyphen characters, so conversion from
             *  original offset to transformed text works like
             *  - The 4th char of the original text is 5th char in the transformed text.
             *  - The 13th char of the original text is 15th char in the transformed text.
             *  Similarly, the reverse conversion works like
             *  - The 5th char of the transformed text is 4th char in the original text.
             *  - The 12th char of the transformed text is 10th char in the original text.
            val creditCardOffsetTranslator = object : OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    if (offset <= 3) return offset
                    if (offset <= 7) return offset + 1
                    if (offset <= 11) return offset + 2
                    if (offset <= 16) return offset + 3
                    return 19
                override fun transformedToOriginal(offset: Int): Int {
                    if (offset <= 4) return offset
                    if (offset <= 9) return offset - 1
                    if (offset <= 14) return offset - 2
                    if (offset <= 19) return offset - 3
                    return 16
            return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
    

    之后我们将TextField设置为上面的对象,代码如下

    //密码内容
    var inputText by remember { mutableStateOf("") }
    //我们定义的卡号VisualTransformation
    val myVisualTransformation = CardVisualTransformation()
    TextField(
        value = inputText,
        label={
              Text(text = "卡号")
        colors=TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent),
        onValueChange = { value -> inputText = value },
        visualTransformation = myVisualTransformation
    

    效果如下:

    可以看见,每输入4个字符,后面会自动加上-,且输入了16个字符后,就无法继续输入了,删除的时候,也会自动将-删除,我们分析下代码

    //这里的text是之前提到的AnnotatedString类型
    //最大程度过滤,只要16个字符,大于16个字符,后面的字符就忽略掉
    val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
    //TextFiel显示的数据,满足条件即追加"-"
    var out = ""
    for (i in trimmed.indices) {
        out += trimmed[i]
        //最后个字符不需要加"-"(即中间每隔四个字符追加"-")
        if (i % 4 == 3 && i != 15) out += "-"
    

    接下来是实现了一个接口OffsetMapping

    官方文档关于此类说明: 提供原始文本和转换文本(transformed text)之间的双向偏移映射

    看到这里,相信各位对原理已经有了一定的了解,VisualTransformation这个类其实就是将原始文本转为转换文本,所以我们看到filter(text: AnnotatedString)最后是返回的一个TransformedText对象

    val creditCardOffsetTranslator = object : OffsetMapping {
        //原始文本对应的转换文本的下标映射
        override fun originalToTransformed(offset: Int): Int {
            if (offset <= 3) return offset
            if (offset <= 7) return offset + 1
            if (offset <= 11) return offset + 2
    
    
    
    
        
    
            if (offset <= 16) return offset + 3
            //转换文本最大长度为19
            return 19
        //转换文本对应的原始文本下标映射
        override fun transformedToOriginal(offset: Int): Int {
            //4 9 14都是"-"的下标位置
            if (offset <= 4) return offset
            if (offset <= 9) return offset - 1
            if (offset <= 14) return offset - 2
            if (offset <= 19) return offset - 3
            //原始文本的最大长度为16
            return 16
    

    映射这里稍微想下就明白了,如有个abcdefgh,其对应的转换文本就为abcd-efgh,其中,a-d是下标没变,都对应得上,但从e开始,由于多了个-,所以原始文本中e的下标为4,而在转换文本中,e的下标变为了5,后面的以此类推,反过来也是同理

    我们根据官方的,改下手机号的,代码如下

    class PhoneVisualTransformation : VisualTransformation{
        override fun filter(text: AnnotatedString): TransformedText {
            val trimmed = if (text.text.length >= 11) text.text.substring(0,11) else text.text
            var out = ""
            for (i in trimmed.indices) {
                out += trimmed[i]
                if (i==2 || i==6 ) out += "-"
            // 147-9611-3406
            // 14796113406
            val creditCardOffsetTranslator = object : OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    if (offset <= 2) return offset
                    if(offset<=6) return offset + 1
                    if (offset <= 11) return offset + 2
                    return 13
                override fun transformedToOriginal(offset: Int): Int {
                    if (offset <= 3) return offset
                    if (offset <= 8) return offset - 1
                    if (offset <= 14) return offset - 2
                    return 11
            return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
    

    效果如下所示:

    7.KeyboardOptions

    KeyboardOptions属性主要是用来设置键盘的相关键盘操作,如自动拼写更正,限制输入类型及键盘的操作

    构造参数如下:

    KeyboardOptions(
        capitalization: KeyboardCapitalization,
        autoCorrect: Boolean,
        keyboardType: KeyboardType,
        imeAction: ImeAction
    
  • capitalization: 通知键盘是否自动大写字符、单词或句子,可选值如下表所示
  • 属性说明
    KeyboardCapitalization.Character将所有字符大写。
    KeyboardCapitalization.None不要自动大写文本。
    KeyboardCapitalization.Sentences将每个句子的第一个字符大写。
    KeyboardCapitalization.Words将每个单词的第一个字符大写。

    autoCorrect: 通知键盘是否启用自动更正,一个boolean值

    keyboardType: 在此文本字段中使用的键盘类型,可选值如下表所示

    属性说明
    KeyboardType.Ascii用于请求能够输入 ASCII 字符的 IME 的键盘类型。
    KeyboardType.Decimal用于请求能够输入小数的 IME 的键盘类型。
    KeyboardType.Email用于请求能够输入电子邮件地址的 IME 的键盘类型。
    KeyboardType.Number用于请求能够输入数字的 IME 的键盘类型。
    KeyboardType.NumberPassword一种用于请求能够输入数字密码的 IME 的键盘类型。
    KeyboardType.Password一种用于请求能够输入密码的 IME 的键盘类型。
    KeyboardType.Phone用于请求能够输入电话号码的 IME 的键盘类型。
    KeyboardType.Text用于请求显示常规键盘的 IME 的键盘类型。
    KeyboardType.Uri用于请求能够输入 URI 的 IME 的键盘类型。
  • imeAction: IME 操作,这个设置会触发输入法键盘出现对应的图标(如回车,下一个等)
  • 属性说明
    ImeAction.Default使用平台和键盘默认值,让键盘决定操作。
    ImeAction.Done表示用户已完成向一组输入提供输入。
    ImeAction.Go表示用户想要转到输入中文本的目标,即访问一个 URL。
    ImeAction.Next表示用户已完成当前输入,并希望移至下一个输入,即移至表单中的下一个字段。
    ImeAction.None表示不希望键盘执行任何操作。
    ImeAction.Previous表示用户想要返回到上一个输入,即返回到表单中的上一个字段。
    ImeAction.Search表示用户想要执行搜索,即网页搜索查询。
    ImeAction.Send表示用户想要发送输入中的文本,即短信。
    属性键盘上的操作按钮图片
    ImeAction.Search:用户想要执行搜索时使用。
    ImeAction.Send:用户想要发送输入字段中的文本时使用。
    ImeAction.Go:用户想要跳转到输入文本的目的地时使用。

    PS:但单独使用是无法实现切换到下个输入框的效果,还需要设置keyboardActions属性切换焦点才可以实现,keyboardActions在下面介绍

    8.keyboardActions

    KeyboardActions(
        onDone: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?,
        onGo: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?,
        onNext: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?,
        onPrevious: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?,
        onSearch: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?,
        onSend: (@ExtensionFunctionType KeyboardActionScope.() -> Unit)?
    

    KeyboardActions接收的lambda函数,与上面的ImeAction一一对应

    下面的例子,展示了切换焦点的效果:

    Column() {
        //获取焦点
        val focusManager = LocalFocusManager.current
        TextField(
            //省略了一些属性设置
            keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Next),
            keyboardActions =  KeyboardActions(onNext = {
                //移动焦点在下方
                focusManager.moveFocus(FocusDirection.Down)
        TextField(
            //省略了一些属性设置
            keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done)
            ,keyboardActions = KeyboardActions(onDone = {
                //清除焦点
                focusManager.clearFocus()
    

    效果如下图所示:

    关于焦点的使用说明,可以查看官方文档焦点方向 | 安卓开发者,本文就不过多赘述了

  • TextField - Jetpack Compose
  • Jetpack Compose - Button_乐翁龙-CSDN博客
  • Compose Button - 简书
  • Jetpack Compose Button,IconButton等各种Button的讲解 - 掘金
  • API文档——PasswordVisualTransformation
  • 状态和Jetpack Compose
  • 计算自定义小费
  •