Jetpack Compose基础组件之文字组件
作者:时光剑客
概述
文本是UI界面中最常见的元素之一,在Compose中,文字组件扮演着重要的角色,文字组件是遵循Material Design规范设计的上层组件,如果我们不想使用Material Design,我们也可以直接使用更底层的文本组件,如Text组件对应的更底层的文本组件是BasicText,文本组件不得不提输入框的使用,本文会主要介绍Text和输入框,并且实现一个现在App中都在用的好看的输入框
文字组件
1.Text 文本
因为Composable组件都是函数,所有的配置都来自于参数的传递,所以我们通过参数列表就可以了解组件的所有功能,Text组件的参数列表如下:
@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,// 文本最多的展示行数 onTextLayout: (TextLayoutResult) -> Unit = {},// 在文本发生变化之后,会回调一个TextLayoutResult,包含此文本的各种信息 style: TextStyle = LocalTextStyle.current // 文本的风格配置,如颜色,字体,行高等 )
Text组件的参数会按照其使用的频度排序,比如text和modifier排在靠前的位置,并尽量添加默认实现。
Text组件的基本功能是显示一段文字,可以为text参数传入要显示的文字内容,Compose也提供了stringResource方法,通过R 资源文件获取字符串,方便做多语言适配
// 指定字符串 Text(text = "Hello World") // 指定文字资源 Text(text = stringResource(id = R.string.app_name))
2.Text的style文字样式
style参数接受一个TextStyle类型,TextStyle中包含了一系列设置文字样式的字段,例如行高,间距,字体大小,字体粗细等,演示代码如下所示:
@Composable fun TextStyleDemo() { Column { Text( text = "Zhongxj", style = TextStyle( fontSize = 25.sp, fontWeight = FontWeight.Bold, background = Color.Cyan, lineHeight = 35.sp ) ) Text( text = stringResource(id = R.string.app_name), style = TextStyle( color = Color.Cyan, letterSpacing = 4.sp ) ) Text( text = "Hello world", style = TextStyle( textDecoration = TextDecoration.LineThrough ) ) Text( text = "Walt-zhong", style = MaterialTheme.typography.h6.copy(fontStyle = FontStyle.Italic) ) } }
运行结果如下所示:
代码中的TextDecoration可以为文字增加删除线或下划线,FontStyle可以用来设置文字是否是斜体
3.maxLines参数
maxLines参数可以帮助我们将文本限制在指定行数之间,当文本超过了参数设置的阈值时,文本会被截断,而overflow可以处理文字过多的场景,文字过多的时候会以“...”结尾,这个参数可以用来实现一个阅读文字时的展开和收起功能,当我们显示很多条数据时,每一条数据都有很多的文字,这时我们可以使用这个参数,默认只展示指定的行数,提供一个展开按钮,当用户点击展开的时候,就显示所有的文本。展示收起按钮,点击收起按钮又可以显示指定的行数。这个功能后面有时间我们会演示,这里我们先看下基本的演示代码:
@Composable fun MaxLinesDemo() { Column { Text( text = "在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。" + "Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的", style = MaterialTheme.typography.body1, color = Color.Red ) Spacer(modifier = Modifier.size(10.dp)) Text( text = "在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。" + "Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的", style = MaterialTheme.typography.body1, maxLines = 1, color = Color.Green ) Spacer(modifier = Modifier.size(10.dp)) Text( text = "在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。" + "Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的", style = MaterialTheme.typography.body1, maxLines = 1, color = Color.Blue, overflow = TextOverflow.Ellipsis ) } }
运行结果:
4.fontFamily字体风格
fontFamily参数用来设置文字字体,演示代码如下:
@Composable fun FontFamilyDemo() { Column { Text(text = "Hello World", color = Color.Red) Spacer(modifier = Modifier.size(10.dp)) Text(text = "Hello World", color = Color.Green, fontFamily = FontFamily.Monospace) Spacer(modifier = Modifier.size(10.dp)) Text(text = "Hello World", color = Color.Blue, fontFamily = FontFamily.Cursive) } }
运行结果
当使用系统中没有的字体时,可以右击res文件夹,选择New->Android Resource Directory->Resource type ->font创建font文件夹,然后将自己的字体拖入文件夹即可
5.AnnotatedString多样式文字
AnnotatedString 其实很像传统View中的SpannableString,在很多的场景中,我们需要在一段文字中突出某些内容,比如超链接和电话号码,我们点击超链接和电话号码能跳到链接指向的页面和拨打电话。在Compose中就需要使用AnnotatedString多样式文字,AnnotatedString是一个数据类,除了文本值,它还包含了一个SpanStyle和ParagraphStyle的Range列表,SpanStyle用于描述在文本中字串的文字样式,ParagraphStyle则用于描述文本字串中的段落样式,Range确定字串的范围,示例代码如下:
@Composable fun AnnotatedStringDemo() { Text(text = buildAnnotatedString { withStyle(style = SpanStyle(fontSize = 24.sp)) { append("I am Iron man,我是钢铁侠") } withStyle(style = SpanStyle(fontWeight = FontWeight.W900, fontSize = 24.sp)) { append("zhongxj") } append("\n") withStyle(style = ParagraphStyle(lineHeight = 25.sp)) { append( "在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。" + "Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的" ) } append("\n") append("Now,We are working hard") append(annotatedText) }) }
运行结果如下:
SpanStyle继承了TextStyle中关于文字样式相关的字段,而ParagraphyStyle继承了TextStyle中控制锻炼的样式,例如textAlign,lineHeight等。SpanStyle和ParagraphyStyle中的设置优先于整个TextStyle中同名属性的设置
看到上图中的绿色文字,是不是有一种想点击的冲动,可惜现在是无法点击的,若要实现点击,我们需要借助于ClickedText.演示代码如下:
@Composable fun ClickTextDemo() { ClickableText(text = annotatedText, onClick = { offset -> annotatedText.getStringAnnotations( tag = "URL", start = offset, end = offset ).firstOrNull()?.let { Log.d("zhongxj", "click me, content: $it") } }) } val annotatedText = buildAnnotatedString { withStyle(style = ParagraphStyle(lineHeight = 25.sp)) { append( "在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。" + "Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的" ) } append("\n") pushStringAnnotation(tag = "URL", annotation = "http://xxxxx") withStyle( style = SpanStyle( fontWeight = FontWeight.W900, textDecoration = TextDecoration.Underline, color = Color(0xFF59A869) ) ) { append("点击我,了解更多") } }
运行结果如下:
这样才可以点击,点击的时候我们打了一个Log,证明点击生效了
6.SelectionContainer可选中文字
Text自身默认是不能被长按选择的,否则在Button使用的时候,就会出现传统View的Button是可粘贴的Button的按钮,Compose提供了专门的SelectionContainer组件对包裹的Text进行选中,演示代码如下:
@Composable fun SelectionContainerDemo() { SelectionContainer { Text(text = "我是可以被复制的文字") } }
运行结果:
7.TextField输入框
TextField组件是我们最常用的文本输入框,遵循Material Design设计准则,它有一个低级别的底层组件,BasicTextField.TextField有两种风格,默认的(filled)和OutlinedTextField我们可以先看下输入框的参数
@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, // 占位符,当输入框处于焦点位置且输入框文本为空时显示 leadingIcon: @Composable (() -> Unit)? = null, // 输入框开头的前置图标 trailingIcon: @Composable (() -> Unit)? = null,// 输入框末尾的后置图标 isError: Boolean = false, // 指示输入框当前值是否有错,当值为true时,标签底部指示器和尾部图标将以错误的颜色显示 visualTransformation: VisualTransformation = VisualTransformation.None,// 输入框内的文字视觉 keyboardOptions: KeyboardOptions = KeyboardOptions.Default, // 软键盘选项,包含键盘类型和ImeAction等配置 keyboardActions: KeyboardActions = KeyboardActions(), // 当输入服务发出一个IME动作时,相应的回调被调用 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() // 输入框的颜色组 )
演示示例如下所示:
@Composable fun TextFieldDemo() { var text by remember { mutableStateOf("") } TextField(value = text, onValueChange = { text = it }, label = { Text(text = "用户名") }) }
运行结果
输入框附带一个label,label会更具输入框获得焦点而呈现出不同的效果,底部会有一个高亮的图标,代码中有关于State的使用,此处不做讲解,这里用于展示使用
我们还可以给输入框添加装饰,代码如下:
@Composable fun TextFieldWithDec() { 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, contentDescription = "用户名") }) TextField(value = password, onValueChange = { password = it }, label = { Text(text = "密码") }, trailingIcon = { IconButton(onClick = { /*TODO*/ }) { Icon( painter = painterResource(id = R.drawable.password), modifier = Modifier.size(16.dp), contentDescription = "password" ) } }) } }
运行结果
8.OutlinedTextField边框样式输入框
OutlinedTextField是按照Material Design规范设计的另一种风格的输入框,除了外观上带有一个边框,其他用法和TextField基本一致,演示代码如下:
@Composable fun OutlineTextFieldDemo() { var text by remember { mutableStateOf("") } OutlinedTextField(value = text, onValueChange = {text = it}, label = { Text(text = "用户名")}) }
运行结果:
9.BasicTextField
BasicTextField是一个更低级别的Compose组件,与TextField,OutlinedTextField不同的是,BasicTextField拥有更多的自定义效果,我们可以看下BasicTextField为我们提供的可选参数列表:
@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, 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的参数有很多共同的地方,我们自定义的关键在于最后一个参数decorationBox,decorationBox是一个Composable,它回调了一个innerTextField函数给我们,innerTextField是框架定义好给我们使用的,它就是文字输入的入口,所以需要创建一个完整的输入框界面,并且在合适的地方调用这个函数,我们实现一个仿当前大多是app都会使用的输入框结束今天的内容:代码如下
@Composable fun SearchBar() { var text by remember { mutableStateOf("") } Box( modifier = Modifier .fillMaxSize() .background(Color(0xFFD3D3D3)), contentAlignment = Alignment.Center ) { BasicTextField(value = text, onValueChange = { text = it }, decorationBox = { innerTextField -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 2.dp, horizontal = 8.dp) ) { Icon(imageVector = Icons.Filled.Search, contentDescription = null) Box( modifier = Modifier.padding(horizontal = 10.dp), contentAlignment = Alignment.CenterStart ) { if (text.isEmpty()) { Text( text = "请输入", style = TextStyle( color = Color(0, 0, 0, 128) ) ) } innerTextField() } Box( modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd ) { if (text.isNotEmpty()) { IconButton(onClick = { text = "" }, modifier = Modifier.size(16.dp)) { Icon( imageVector = Icons.Filled.Close, contentDescription = null ) } } } } }, modifier = Modifier .padding(horizontal = 10.dp) .background(Color.White, CircleShape) .height(30.dp) .fillMaxWidth() ) } }
运行结果
总结
至此,我们的文字组件就介绍完了,本文只是简单的介绍了文字组件的入门功能,如果读者想要实现更加复杂绚丽的功能,请查看官方文档和脚本之家其它相关文章!