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中全新的文本框。
@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实现上述满足我们日常用法的输入框呢,代码如下:
@Composable
fun ShowTextField(context: MainActivity) {
//初始化文本变量
var text by remember { mutableStateOf("") }
TextField(
value = 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状态进行更改,文本框将处于错误状态,下划线将一直是红色的;大功告成之后,现在来看下文本框的样式:
可以看到,当失去焦点时,下划线是我们指定 unfocusedIndicatorColor
的绿色;获取到焦点并输入文本时,下划线是指定 focusedIndicatorColor
的透明颜色;至于不可用状态和错误状态下的颜色,大家可以模拟场景进行测试;
轮廓式文本框OutlinedTextFieldOutlinedTextField几乎和普通的TextField有着相同的API,但它还能实现文本框描边,顾名思义,轮廓式文本框自带了轮廓描边,能够轻易实现产品和UI眼中的效果可以来个简单demo演示一下:
@Composable
fun OutlinedTextFieldDemo() {
var text by remember { mutableStateOf("") }
OutlinedTextField(value = text,
label = { Text(text = "Input something") },//这里label为文本框未输入时显示的文本
onValueChange = { text = it })
}
用法和TextField一致,只是在样式上有不同:
基本文本框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 = input, // 显示文本
onValueChange = { input = it }, // 文字改变时,就赋值给text
)
}
展示效果如下:
emmm咋就一个hello呢,文本框的边界都看不到,那如果我设定初始值为空,那岂不连这个文本框都不知道在哪♀️;真的是够原始,真的够Basic,但正是因为这样所以能去高度自定义文本框样式,让我们看看 BasicTextField
正确的打开方式是怎样的:首先给它来加个背景吧,要不然连它在哪都不知道了:
modifier = Modifier.background(Color.Green, RoundedCornerShape(8.dp)),//设置绿底色,8dp圆角的背景,也可通过上述方法来设置半圆角
效果如下:
怎么可能仅仅满足于只加个背景颜色呢,让我们画个文本框,添加如下代码:
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上有着一定的差异,可以高度自定义样式;
以上便是本次分享的全部内容,希望对你有所帮助^_^喜欢的话别忘了 分享、点赞、收藏 三连哦~
欢迎关注公众号 程序员巴士,一辆有趣、有范儿、有温度的程序员巴士,涉猎大厂面经、程序员生活、实战教程、技术前沿等内容,关注我,交个朋友。