写一些文字相关的组件,包含
一、Text文本
文本是最常见的组件之一。在Compose中,Text是遵循Material Design规范设计的上层文本组件,如果想脱离Material Design使用,也可以直接使用更底层的文本组件BasicText。
1、参数说明
Text组件的基本参数如下:
kotlin
@Composable
fun Text(
text: String, //文本
modifier: Modifier = Modifier, //修饰符
color: Color = Color.Unspecified, //文字颜色
fontSize: TextUnit = TextUnit.Unspecified, //文字大小
fontStyle: FontStyle? = null, //绘制文本时使用的字体变体(例如加粗、斜体等)
fontWeight: FontWeight? = null, //文本的粗细
fontFamily: FontFamily? = null, //文本的字体
letterSpacing: TextUnit = TextUnit.Unspecified, //文本间距
textDecoration: TextDecoration? = null, //文本的修饰,例如下划线
textAlign: TextAlign? = null, //文本的对齐方式
lineHeight: TextUnit = TextUnit.Unspecified, //行高
overflow: TextOverflow = TextOverflow.Clip, //文本溢出的视觉效果,比如切割、省略号等等
softWrap: Boolean = true, //控制文本是否能够换行,如果为false,则会截断
maxLines: Int = Int.MAX_VALUE, //最大行数
minLines: Int = 1, //最小行数
onTextLayout: (TextLayoutResult) -> Unit = {}, //在文本发生变化之后,会回调到这里
style: TextStyle = LocalTextStyle.current //文本的风格配置,如颜色、字体、行高等
) {...}
举一个最简单的例子
kotlin
Column {
Text(text = "Hello Android")
Text(text = stringResource(id = R.string.app_name))
BasicText(text = "Hello Android")
}
Tips:除了stringResource
, Compose也提供了获取其他类型资源的方法,例如colorResource
、integerResource
、painterResource(Drawable类型资源)
等。
2、style文字样式
style
参数接受一个TextStyle
类型,TextStyle
中包含一系列设置文字样式的字段,例如行高、间距、字体大小、字体粗细等。举例如下:
kotlin
Text(
text = "Hello Android",
style = TextStyle(
fontSize = 18.sp, //字体大小
color = Color.Blue, //字体颜色
fontWeight = FontWeight.Bold, //文字粗细
background = Color.White, //背景
lineHeight = 30.sp, //行高
letterSpacing = 4.sp, //字间距
textDecoration = TextDecoration.LineThrough //删除线
)
)
UI效果
可以把常用的TextStyle
抽取出来。TextStyle
中的大部分字段也可以在Text参数中直接设置,例如fonteSize
、fontWeight
、fontStyle
等。注意Text参数会覆盖对TextStyle同名属性的设置。
3、AnnotatedString多样式文字
Text
的text
参数除了可以给String
类型还可以给AnnotatedString
类型,看下AnnotatedString
的源码:
kotlin
class AnnotatedString internal constructor(
val text: String,
internal val spanStylesOrNull: List<Range<SpanStyle>>? = null,
internal val paragraphStylesOrNull: List<Range<ParagraphStyle>>? = null,
internal val annotations: List<Range<out Any>>? = null
) : CharSequence {...}
在很多应用场景中,我们需要在一段文字中对局部内容应用特别格式以示突出,比如一个超链接或者一个电话号码等,此时需要用到AnnotatedString
。AnnotatedString
是一个数据类,除了文本值,它还包含了一个SpanStyle
和ParagraphStyle
的Range
列表。SpanStyle
用于描述在文本中子串的文字样式,ParagraphStyle
则用于描述文本中子串的段落样式,Range
确定子串的范围。
使用buildAnnotatedString{...}
,以DSL的方式构建一个AnnotatedString
。其中append
用来添加子串的文本,withStyle
为append
的子串指定文字或段落样式。代码如下所示:
kotlin
Text(
text = buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Black)) {
append("文字变色加粗")
}
withStyle(style = SpanStyle(color = Color.Blue,fontWeight = FontWeight.W900)) {
append("Jetpack Compose")
}
append("\n")
withStyle(style = SpanStyle(color = Color.Black)) {
append("文字变色加粗添加下划线")
}
withStyle(style = SpanStyle(color = Color.Blue,fontWeight = FontWeight.W900, textDecoration = TextDecoration.Underline)) {
append("Jetpack Compose")
}
}
)
UI效果
SpanStyle
继承了TextStyle
中关于文字样式相关的字段,而ParagraphStyle
继承了TextStyle
中控制段落的样式,例如textAlign
、lineHeight
等。某种意义上说SpanStyle
与ParagraphStyle
分拆了TextStyle
,可以对子串分别进行文字以及段落样式的设置。
注意:SpanStyle
或ParagraphStyle
中的设置优先于整个TextStyle
中的同名属性设置。
有一个常见的需求就是部分文字可点击又该如何实现呢?
Compose
提供了一种可点击文本组件ClickedText
,可以响应对文字的点击,并返回点击位置。可以让AnnotatdString
子串在相应的ClickedText
中点击后,做出不同的动作。例如点击一个超链接样式子串可以打开浏览器、点击数字格式子串来拨打电话等。在AnnotatedString
中可以为子串添加一个tag标签,在处理onClick
事件时,可以根据tag
实现不同的逻辑。示例代码:
kotlin
@Composable
fun Greeting() {
//文本内容
val annotatedString = buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Black)) {
append("跳转到官网:")
}
pushStringAnnotation(tag = "juejin", annotation = "https://juejin.cn/")
withStyle(style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.W900)) {
append("juejin")
}
pop()
append("\n")
withStyle(style = SpanStyle(color = Color.Black)) {
append("跳转到官网:")
}
pushStringAnnotation(tag = "baidu", annotation = "https://www.baidu.com/")
withStyle(
style = SpanStyle(color = Color.Blue, fontWeight = FontWeight.W900)
) {
append("baidu")
}
pop()
}
//可点击的文本控件
ClickableText(
text = annotatedString,
onClick = { offset ->
annotatedString.getStringAnnotations(tag = "juejin", start = offset, end = offset)
.firstOrNull()?.let { annotation ->
//跳转到掘金官网
val uri = Uri.parse(annotation.item)
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
}
annotatedString.getStringAnnotations(tag = "baidu", start = offset, end = offset)
.firstOrNull()?.let { annotation ->
//跳转到百度官网
val uri = Uri.parse(annotation.item)
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
}
}
)
}
使用可点击的文本ClickableText
,pop()
是AnnotatedString
的方法,作用是结束添加的style和annotation,让文本恢复默认样式。UI效果如下:
4、SelectionContainer选中文字
Text自身默认是不能被长按选择的,如果希望文本能被长按选中需要使用SelectionContainer
包裹。示例如下:
二、TextField输入框
TextField
组件是我们最常使用的文本输入框,它有两种风格,一种是默认的,也就是TextField
,另一种是OutlinedTextField
。它也有一个低级别的底层组件,叫作BasicTextField
,后面会介绍。
1、参数说明
TextField组件的基本参数如下:
kotlin
@Composable
fun TextField(
value: String, //输入框显示的文本
onValueChange: (String) -> Unit, //输入框的文本发生变化时的回调
modifier: Modifier = Modifier, //修饰符
enabled: Boolean = true, //是否可用
readOnly: Boolean = false, //控制输入框的可编辑状态
textStyle: TextStyle = LocalTextStyle.current, //输入框内文字的样式
label: @Composable (() -> Unit)? = null, //可选的标签,将显示在输入框内
placeholder: @Composable (() -> Unit)? = null, //占位文本,相当于hint
leadingIcon: @Composable (() -> Unit)? = null, //输入框开头显示的前置图标
trailingIcon: @Composable (() -> Unit)? = null, //输入框末尾显示的后置图标
isError: Boolean = false, //指示输入框的当前值是否有错误,当值为"true"时,标签、底部指示器和尾部图标将以错误颜色显示
visualTransformation: VisualTransformation = VisualTransformation.None, //输入框内的文本视觉,例如可以设置PasswordVisualTransformation来达到显示密码文本的效果
keyboardOptions: KeyboardOptions = KeyboardOptions.Default, //软键盘选项,包含键盘类型和ImeAction等配置
keyboardActions: KeyboardActions = KeyboardActions(), //当输入发出一个IME动作时,相应的回调会被调用
singleLine: Boolean = false, //是否单行
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, //最大行数
minLines: Int = 1, //最小行数
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, //用于监听组件状态,便于自定义组件不同状态下的样式
shape: Shape =
MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize), //输入框的外观形状
colors: TextFieldColors = TextFieldDefaults.textFieldColors() //输入框的颜色组
) {...}
下面是一个简单的示例
kotlin
var userName by remember { mutableStateOf("") }
TextField(
value = userName,
modifier = Modifier.fillMaxWidth(),
onValueChange = { userName = it },
placeholder = { Text(text = "用户名不允许使用特殊符号") },
label = { Text(text = "用户名") },
maxLines = 1
)
UI效果
2、为输入框添加装饰
(1)添加前后图标
kotlin
var userName by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
Column {
TextField(
value = userName,
onValueChange = { userName = it },
label = { Text(text = "用户名") },
leadingIcon = { //设置前置图标
Icon(
imageVector = Icons.Filled.AccountBox, //Android内置图
contentDescription = ""
)
}
)
TextField(
value = password,
onValueChange = { password = it },
label = { Text(text = "密码") },
trailingIcon = { //设置后置图标
IconButton(onClick = { showToast("显示密码") }) {
Icon(
painter = painterResource(id = R.mipmap.eye_invisiable),
contentDescription = "",
modifier = Modifier //修改图片大小
.width(20.dp)
.height(20.dp),
)
}
}
)
}
UI效果
(2) OutlinedTextField边框样式输入框
OutlinedTextField
是一个带边框的输入框,系统还提供了一个带边框的按钮OutlinedButton
,下面是一个示例,包含了如何修改自带的边框的一些属性:
kotlin
var userName by remember { mutableStateOf("") }
Column {
//带边框的输入框
OutlinedTextField(
value = userName,
onValueChange = { userName = it },
label = { Text(text = "用户名") },
leadingIcon = {
Icon(
imageVector = Icons.Filled.AccountBox,
contentDescription = ""
)
}
)
//加点间距
Spacer(modifier = Modifier.height(20.dp))
//带边框的按钮
OutlinedButton(onClick = {},
colors = ButtonDefaults.outlinedButtonColors( //设置颜色
backgroundColor = Color.Gray,
contentColor = Color.White
),
shape = MaterialTheme.shapes.small.copy( //设置边框圆角
topStart = CornerSize(30), //左上30dp
topEnd = CornerSize(10) //右上20dp
), //设置边框的圆角
border = BorderStroke(width = 2.dp, color = Color.Blue), //设置边框的宽度和边框颜色
content = {
Text(text = "带边框的按钮") //按钮的内容
})
}
UI效果
3、BasicTextField参数说明
BasicTextField
是一个更低级别的Composable
组件,与TextField、OutlinedTextField不同的是,BasicTextField拥有更多的自定义效果。
kotlin
@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 = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
visualTransformation: VisualTransformation = VisualTransformation.None,
onTextLayout: (TextLayoutResult) -> Unit = {}, //当输入框文本更新时的回调,包括了当前文本的各种信息
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
cursorBrush: Brush = SolidColor(Color.Black), //输入框光标的颜色
decorationBox: @Composable (innerTextField: @Composable () -> Unit) -> Unit =
@Composable { innerTextField -> innerTextField() } //允许在TextField周围添加修饰的Composable lambda,需要在布局中调用innerTextField()才能完成TextField的构建
) {...}
BasicTextField
的参数和TextField
有很多共同的地方,自定义BasicTextField
的关键在于最后一个参数decorationBox
。decorationBox
是一个Composable
,它回调了一个innerTextField
函数给我们。innerTextField
是框架定义好给我们使用的东西,它就是文字输入的入口,所以需要创建好一个完整的输入框界面,并在合适的地方调用这个函数。看下面的例子:
kotlin
var userName by remember { mutableStateOf("") }
var basicText by remember { mutableStateOf("") }
Column {
//TextField
TextField(
value = userName,
onValueChange = { userName = it },
modifier = Modifier.height(50.dp)
)
//加点间距
Spacer(modifier = Modifier.height(20.dp))
//BasicTextField
BasicTextField(
value = basicText,
onValueChange = { basicText = it },
modifier = Modifier.height(50.dp),
decorationBox = { innerTextField ->
//直接调用
//innerTextField()
//或添加其他的装饰
Column {
innerTextField()
//加分割线
Divider(
thickness = 2.dp, //分割线的宽度
modifier = Modifier
.fillMaxWidth()
.background(Color.Blue)
)
}
}
)
}
UI效果
代码中将TextField
和BasicTextField
放在一起对比,如果BasicTextField
不调用innerTextField()
那么点击是没有任何效果的,所以自定义BasicTextField
不要忘记调用innerTextField()
。
4、BasicTextField的简单封装示例
基于BasicTextField
可以封装多种多样的输入框,项目是二个简单示例
(1)限制输入长度的输入框
kotlin
@Composable
fun Greeting() {
var text by remember { mutableStateOf("") }
LimitedLengthTextField(
modifier = Modifier
.border(1.dp, Color.Gray, shape = RoundedCornerShape(4.dp))
.padding(8.dp),
value = text,
maxLength = 5,
onValueChange = { text = it },
)
}
//限制输入的长度
@Composable
fun LimitedLengthTextField(
modifier: Modifier = Modifier,
value: String,
maxLength: Int,
onValueChange: (String) -> Unit,
) {
var text by remember { mutableStateOf(value) }
BasicTextField(
value = text,
onValueChange = {
if (it.length <= maxLength) {
text = it
onValueChange(it)
} else {
text = it.take(maxLength) //超过就截断
onValueChange(text)
}
},
modifier = modifier,
singleLine = true,
textStyle = TextStyle(color = Color.Black),
visualTransformation = VisualTransformation.None
)
}
(2)限制只能输入字母和数字
kotlin
@Composable
fun Greeting() {
var text by remember { mutableStateOf("") }
AlphanumericTextField(
modifier = Modifier
.border(1.dp, Color.Gray, shape = RoundedCornerShape(4.dp))
.padding(8.dp),
value = text,
onValueChange = {
text = it
},
)
}
//限制只能输入大小写字母和数字
@Composable
fun AlphanumericTextField(
modifier: Modifier,
value: String,
onValueChange: (String) -> Unit
) {
var text by remember { mutableStateOf(value) }
BasicTextField(
modifier = modifier,
value = text,
onValueChange = {
// 使用正则表达式过滤非字母和非数字的字符
val filteredText = it.replace(Regex("[^a-zA-Z0-9]"), "")
text = filteredText
onValueChange(filteredText)
},
textStyle = TextStyle(color = Color.Black), // 根据需要设置文本样式
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
// 执行完成操作(如果需要的话)
showToast("点了键盘上的ok按钮")
}
)
)
}
三、 总结
在不熟练的情况下用起来还是不顺手的,有时候一个简单的效果要琢磨一会儿。作为初学者,以上内容难免会有错误或不足,欢迎批评指正。