Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你

Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

/ 前言 / Jetpack Compose是用于构建原生Android UI的现代工具包,使用更简洁的代码、强大的工具和直观的Kotlin API,简化并加速了Android上的UI开发。不同于Andorid常见的Xml+命令式Coding的UI开发范式,Compose基于Kotlin的DSL实现了一套类似React的声明式UI框架。伴随React Native、Flutter等大前端框架的兴起以及Jetpack Compose、SwiftUI等native框架的出现,声明式UI正逐渐成为客户端UI开发的新趋势,那么下面主要就来介绍一下Compose中全新的文本框。
/ 基本API介绍 /

@Composable fun TextField( value: TextFieldValue,//要展示的文本 onValueChange: (TextFieldValue) -> Unit,//监听文本变化 modifier: Modifier = Modifier,//修饰符,常用于背景设置等 enabled: Boolean = true,//是否能用 readOnly: Boolean = false,//是否只读 textStyle: TextStyle = LocalTextStyle.current,//文本格式 label: @Composable (() -> Unit)? = null,//标签 placeholder: @Composable (() -> Unit)? = null,//占位符,输入为空时展示 leadingIcon: @Composable (() -> Unit)? = null,//最左边的图标 trailingIcon: @Composable (() -> Unit)? = null,//最右边的图标 isError: Boolean = false,//当前输入是否错误 visualTransformation: VisualTransformation = VisualTransformation.None,//指定输入类型,类似inputType keyboardOptions: KeyboardOptions = KeyboardOptions.Default,//自定义键盘按键 keyboardActions: KeyboardActions = KeyboardActions(),//自定义按键事件 singleLine: Boolean = false,//单行显示 maxLines: Int = Int.MAX_VALUE,//最大行数 interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },//某个交互流 shape: Shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),//定义文本框背景 colors: TextFieldColors = TextFieldDefaults.textFieldColors()//各种cursor、文本等颜色 )

/ 基础用法 / 日常开发中,经常会使用到文本输入框;一般样式为左边是一个搜索的图标,右边是清楚文本图标,如果文本框未输入,则提示hint关键词,效果大概是以下这种:
Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

那么如何用compose实现上述满足我们日常用法的输入框呢,代码如下:
@Composable fun ShowTextField(context: MainActivity) { //初始化文本变量 var text by remember { mutableStateOf("") }TextField( value = https://www.it610.com/article/text, // 显示文本 onValueChange = { text = it }, // 监听文本变化,并赋值给text label = { Text(text ="Input") }, // 设置label leadingIcon = @Composable {// 设置左边图标 Image( imageVector = Icons.Filled.Search, contentDescription = "search", //image的无障碍描述 modifier = Modifier.clickable {// 通过modifier来设置点击事件 Toast.makeText( context, "search $text", Toast.LENGTH_SHORT ).show() }) }, trailingIcon = @Composable {//设置右边图标 Image(imageVector = Icons.Filled.Clear, contentDescription = "clear", modifier = Modifier.clickable { text = "" }) // 添加点击清空事件 }, placeholder = @Composable { Text(text = "This is placeholder") },//hint提示语 }

接入Compose的日常文本框效果就是这个样子:

可以看到,Compose作为一款全新的UI工具包,动效并不输于基于View的系统实现的文本框;重点是代码量少了 a lot有木有?! 这些就是Compose的贴心之处,能帮助开发者花更少的精力在UI和动效搭建上,这些统统都帮实现好了,从而把节省下来的时间更多的放在业务逻辑中;
现在让我们来加几行代码,看看能不能实现更炫酷的效果:
isError = true, //是否显示错误提示 visualTransformation = PasswordVisualTransformation(),//展示密文 keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),//自定义回车为搜索操作 keyboardActions = KeyboardActions(//将搜索事件自定义 onSearch = { Toast.makeText( context, "search $text", Toast.LENGTH_SHORT ).show()

新增这几行代码后效果就变成了这种效果:

相较于之前的效果,文字和输入框下划线都被标上了表示错误的红色,这里红色能不能自定义呢?将在后面进行解答,同时文本显示成被加密的密文格式,软键盘的回车键也被修改成了搜索按钮;不仅能修改成搜索,还能自定义成发送、完成等按钮,查看 ImeAction 文件可知,总共有下面这些自定义按钮类型,大家可以去自己自定义试试:
val None: ImeAction = ImeAction(0) val Default: ImeAction = ImeAction(1) val Go: ImeAction = ImeAction(2) val Search: ImeAction = ImeAction(3) val Send: ImeAction = ImeAction(4) val Previous: ImeAction = ImeAction(5) val Next: ImeAction = ImeAction(6) val Done: ImeAction = ImeAction(7)

那如果有的需求需要设置输入框圆角样式呢,非常简单,不需要繁琐的设置selector,只需要简单一行代码搞定:
shape = RoundedCornerShape(16.dp),//设置文本框圆角

那有人又问了,我只需要实现半圆角效果怎么办,同样也是一句代码就能实现:
shape = RoundedCornerShape(16.dp,16.dp,0.dp,0.dp),//设置文本框圆角

可以通过查看 RoundedCornerShape源码得知它能设置圆角的原因
fun RoundedCornerShape( topStart: Dp = 0.dp, topEnd: Dp = 0.dp, bottomEnd: Dp = 0.dp, bottomStart: Dp = 0.dp ) ...fun RoundedCornerShape(corner: CornerSize) = RoundedCornerShape(corner, corner, corner, corner) ...//outline通过设置bounds来绘制形状 override fun createOutline( size: Size, topStart: Float, topEnd: Float, bottomEnd: Float, bottomStart: Float, layoutDirection: LayoutDirection ) = if (topStart + topEnd + bottomEnd + bottomStart == 0.0f) {//如果未设圆角,则显示长方形 Outline.Rectangle(size.toRect()) } else { Outline.Rounded(//如果设置了圆角,则显示圆角形状 RoundRect( rect = size.toRect(), topLeft = CornerRadius(if (layoutDirection == Ltr) topStart else topEnd), topRight = CornerRadius(if (layoutDirection == Ltr) topEnd else topStart), bottomRight = CornerRadius(if (layoutDirection == Ltr) bottomEnd else bottomStart), bottomLeft = CornerRadius(if (layoutDirection == Ltr) bottomStart else bottomEnd) ) ) }

之前上文提到的红色下划线如何自定义呢,TextField 中有个参数 colors 供开发者来自定义文本框每个状态下的颜色:
colors = TextFieldDefaults.textFieldColors( focusedIndicatorColor = Color.Transparent,//有焦点时 底部指示条为透明 unfocusedIndicatorColor = Color.Green,//无焦点,为绿色 errorIndicatorColor = Color.Red,//错误时,为红色 disabledIndicatorColor = Color.Gray,//不可用,灰色 )

为了不让文本框一直处于错误状态,我们还需要对isError状态进行修改:
isError = false, //展示错误提示

如果不对Error状态进行更改,文本框将处于错误状态,下划线将一直是红色的;大功告成之后,现在来看下文本框的样式:Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

可以看到,当失去焦点时,下划线是我们指定 unfocusedIndicatorColor 的绿色;获取到焦点并输入文本时,下划线是指定 focusedIndicatorColor 的透明颜色;至于不可用状态和错误状态下的颜色,大家可以模拟场景进行测试;
/ 扩展 /
轮廓式文本框OutlinedTextFieldOutlinedTextField几乎和普通的TextField有着相同的API,但它还能实现文本框描边,顾名思义,轮廓式文本框自带了轮廓描边,能够轻易实现产品和UI眼中的效果可以来个简单demo演示一下:
@Composable fun OutlinedTextFieldDemo() { var text by remember { mutableStateOf("") } OutlinedTextField(value = https://www.it610.com/article/text, label = { Text(text ="Input something") },//这里label为文本框未输入时显示的文本 onValueChange = { text = it }) }

用法和TextField一致,只是在样式上有不同:
Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

基本文本框BasicTextField可以看到不管是 TextField 还是 OutlinedTextField 其实都已经帮开发者实现了常见文本框大部分的动效及功能,但实际开发中会遇到非常多特殊场景,需要一款基本文本框让开发者去高度自定义实现业务所需样式,那么大家可以尝试着使用一下 BasicTextField ,它能用来相应硬件或软键盘编辑文字,可以自定义cursor、边框等,因为边框是自定义了,label属性在 BasicTextField 中也可以剔除了;它的API和TextField存在一部分差异,可以一起来看下:
@Composable fun BasicTextField( value: String,//显示文本 onValueChange: (String) -> Unit,//监听文本变化 modifier: Modifier = Modifier,//修饰符,常用于设置背景等 enabled: Boolean = true,//是否可用 readOnly: Boolean = false,//是否只读 textStyle: TextStyle = TextStyle.Default,//文本格式 keyboardOptions: KeyboardOptions = KeyboardOptions.Default,//自定义键盘按键 keyboardActions: KeyboardActions = KeyboardActions.Default,//自定义按键事件 singleLine: Boolean = false,//单行显示 maxLines: Int = Int.MAX_VALUE,//最大行数 visualTransformation: VisualTransformation = VisualTransformation.None,//指定输入类型,类似inputType onTextLayout: (TextLayoutResult) -> Unit = {},//监听布局变化 interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },//某个组件的交互流 cursorBrush: Brush = SolidColor(Color.Black),//自定义cursor需要用到 // 用来定义装饰框,innerTextField用来绘制文本 decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit = @Composable { innerTextField -> innerTextField() } )

同样的,拿到API后我们先来写个简单demo:
@Composable fun ShowBasicTextField(context: MainActivity) { var input by remember { mutableStateOf("hello") }BasicTextField( value = https://www.it610.com/article/input, // 显示文本 onValueChange = { input = it }, // 文字改变时,就赋值给text ) }

展示效果如下:
Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

emmm咋就一个hello呢,文本框的边界都看不到,那如果我设定初始值为空,那岂不连这个文本框都不知道在哪?♀?;真的是够原始,真的够Basic,但正是因为这样所以能去高度自定义文本框样式,让我们看看 BasicTextField 正确的打开方式是怎样的:首先给它来加个背景吧,要不然连它在哪都不知道了:
modifier = Modifier.background(Color.Green, RoundedCornerShape(8.dp)),//设置绿底色,8dp圆角的背景,也可通过上述方法来设置半圆角

效果如下:
Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你
文章图片

怎么可能仅仅满足于只加个背景颜色呢,让我们画个文本框,添加如下代码:
decorationBox = @Composable { innerTextField -> // 通过canvas来画文本框的修饰等, modifier决定了文本框长宽 Canvas(modifier = Modifier.size(width = 100.dp, height =50.dp)) {、``` //画个圆 drawCircle(color = Color.Red, style = Stroke(width = 1F)) } // 然后再画文字 innerTextField() }

上述代码操作很简单,就是先通过Canvas画布对文本框的边界进行绘制,然后在内部绘制了一个圆形,最后调用innerTextField绘制文本框内部的文字,效果如下:

其实Canvas画布上不仅仅能绘制边框和圆形,查看源码之后发现还能绘制icon、占位符placeHolder等等,而且绘制文字的innerTextField方法只能调用一次:
* @param decorationBox * Composable lambda that allows to add decorations around text field, such * as icon, placeholder, helper messages or similar, and automatically increase the hit target area * of the text field. To allow you to control the placement of the inner text field relative to your * decorations, the text field implementation will pass in a framework-controlled composable * parameter "innerTextField" to the decorationBox lambda you provide. You must call * innerTextField exactly once.

/ 总结 / 如果普通的文本框没法满足业务需求,各位看官可以尝试使用BasicTextField,能高度自定义文本框样式;如果普通文本框正好是我们想要的,使用TextField即可;如果想用空心的轮廓式文本框,则使用OutlinedTextField;TextField和OutlinedTextField一个是实心一个是空心文本框,其余API完全一致;而BasicTextField在API上有着一定的差异,可以高度自定义样式;
/ 谢谢支持 / 【Compose全新文本输入框?|Compose全新文本输入框? 拿来吧你】以上便是本次分享的全部内容,希望对你有所帮助^\_^喜欢的话别忘了 分享、点赞、收藏 三连哦\~欢迎关注公众号 程序员巴士,一辆有趣、有范儿、有温度的程序员巴士,涉猎大厂面经、程序员生活、实战教程、技术前沿等内容,关注我,交个朋友。

    推荐阅读