侧边栏壁纸
博主头像
Stars-one博主等级

所有的创作都是具有价值的
Android Kotlin Java开发者,喜欢折腾搞机,开发小工具
赞赏发电,给予支持

  • 累计撰写 157 篇文章
  • 累计创建 44 个标签
  • 累计收到 13 条评论

目 录CONTENT

文章目录

Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用

Stars-one
2021-09-08 / 0 评论 / 0 点赞 / 1,648 阅读 / 5,878 字

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

本系列以往文章请查看此分类链接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 = "", onValueChange = {},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的图标,点击删除输入全部文本功能哦 😉

4.singleLine

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

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

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

5.color

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

@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) 😃

这个有点类似之前原生的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)

注意: 经过实践发现,这个只是改变了显示的数值而已😅,实际上你输入什么,保存的数值还是那个,单纯只是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()
        })
    )
}

效果如下图所示:

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

参考

0

评论区