购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 常用的基础组件

本节将对常用Composable组件的使用做一个简单介绍。由于本书容量有限,不可能覆盖所有组件以及所有参数。希望通过这些基础组件的学习,燃起大家使用Compose的热情,未来可以自发学习更多组件的深入用法。

2.2.1 文字组件

1.Text文本

文本是UI中最常见的元素之一。在Compose中,Text是遵循Material Design规范设计的上层文本组件,如果想脱离Material Design使用,也可以直接使用更底层的文本组件BasicText。

我们知道 Composable组件都是函数,所有的配置来自参数传递,通过参数列表就可以了解组件的所有功能 。Text组件的参数列表如下所示,接下来会介绍其中几个有代表性的参数的使用。

最佳实践:

Text组件的参数会按照其使用频度排序(比如text和modifier排在靠前的位置),并尽量添加默认实现,便于在单元测试或者预览中使用。我们自定义的Composable组件也应该遵循这样的参数设计原则。

Text的基本功能是显示一段文字,可以为text参数中传入要显示的文字内容。Compose也提供了stringResource方法通过R资源文件获取字符串。

补充提示:

除了stringResource, Compose也提供了获取其他类型资源的方法,例如colorResource、integerResource、painterResource(Drawable类型资源)等。

2.style文字样式

style参数接受一个TextStyle类型,TextStyle中包含一系列设置文字样式的字段,例如行高、间距、字体大小、字体粗细等。

上述代码的运行结果如图2-15所示。其中TextDecoration可以为文字增加删除线或下画线,FontStyle可以用来设置文字是否是斜体。

•图2-15 配置TextStyle

补充提示:

TextStyle虽然是一个普通data class,但是它提供了data class那样的copy方法,可以非常高效地构造一个新实例。

代码中出现的MaterialTheme.typography.h6是一个预置的TextStyle。Compose项目默认都有一个Type.kt文件,它和其他一些预置的TextStyle就定义在那里,这些都是Material Design规范中的文字样式,例如h1、h2、overline等。

表2-2就是Material Desgin 2的Typography规范,定义了各类文字样式,样式名称也体现了它们的使用场景,比如H1是一级标题,BUTTON是按钮文字等。

表2-2 Material Design字体

如果我们的项目采用了Material Design设计规范,那么可以为Text的style参数直接设置Typography中预置的TextStyle。当然也可以脱离Material Design来定义自己的文本样式。

补充提示:

TextStyle中的大部分字段也可以在Text参数中直接设置,例如fonteSize、fontWeight、fontStyle等。注意Text参数会覆盖对TextStyle同名属性的设置。

maxLines参数

代码执行结果如图2-16所示。

•图2-16 maxLines参数

3.fontFamily字体风格

fontFamily参数用来设置文字字体,代码及运行结果如图2-17所示。

•图2-17 fontFamily参数

补充提示:

当使用系统没有字体时,可以右击res文件夹,选择New-> Android Resource Directory > Resource type > font,创建font文件夹,然后将自己的字体文件拖入文件夹即可。

4.AnnotatedString多样式文字

在很多应用场景中,我们需要在一段文字中对局部内容应用特别格式以示突出,比如一个超链接或者一个电话号码等,此时需要用到AnnotatedString。AnnotatedString是一个数据类,除了文本值,它还包含了一个SpanStyle和ParagraphStyle的Range列表。SpanStyle用于描述在文本中子串的文字样式,ParagraphStyle则用于描述文本中子串的段落样式,Range确定子串的范围。

使用buildAnnotatedString{...},以DSL的方式构建一个AnnotatedString。其中append用来添加子串的文本,withStyle为append的子串指定文字或段落样式。代码如下所示:

执行结果如图2-18所示。

•图2-18 AnnotatedString效果

SpanStyle继承了TextStyle中关于文字样式相关的字段,而ParagraphStyle继承了TextStyle中控制段落的样式,例如textAlign、lineHeight等。某种意义上说 SpanStyle与ParagraphStyle分拆了TextStyle,可以对子串分别进行文字以及段落样式的设置。

补充提示:

SpanStyle或ParagraphStyle中的设置优先于整个TextStyle中的同名属性设置。

Compose提供了一种可点击文本组件ClickedText,可以响应我们对文字的点击,并返回点击位置。可以让AnnotatdString子串在相应的ClickedText中点击后,做出不同的动作。例如点击一个超链接样式子串可以打开浏览器、点击数字格式子串来拨打电话等。在AnnotatedString中可以为子串添加一个tag标签,在处理onClick事件时,可以根据tag实现不同的逻辑。代码如下:

5.SelectionContainer选中文字

Text自身默认是不能被长按选择的,否则在Button中使用时,又会出现第1章那种“可粘贴的Button”的例子。

Compose提供了专门的SelectionContainer组件,对包裹的Text进行选中。可见Compose在组件设计上,将关注点分离的原则发挥到了极致。代码执行结果如图2-19所示。

•图2-19 使用SelectionContainer

6.TextField输入框

TextField组件是我们最常使用的文本输入框,它也遵循着Material Design设计准则。它也有一个低级别的底层组件,叫作BasicTextField,我们会在之后的章节介绍到它。

TextField有两种风格,一种是默认的,也就是filled,另一种是OutlinedTextField。

下面的代码是一个输入框的例子。输入框同时附带一个label, label会根据输入框获得焦点而呈现出不同的效果。输入框获得焦点时,底部还会有一个颜色高亮的提示,如图2-20所示。

•图2-20 使用TextField

代码中提前出现了关于State的使用:var text by remember{mutableStateOf("")}。关于状态我们会在第4章重点介绍。这里只需要简单知道这个text是一个可以变化的文本,用来显示TextField输入框中当前输入的文本内容。在onValueChange回调中可以获取来自软键盘的最新输入,我们利用这个信息来更新可变状态text,驱动界面刷新显示最新的输入文本。

补充提示:

来自软键盘的输入内容不会直接更新TextField, TextField需要通过观察额外的状态更新自身,这也体现了声明式UI中“状态驱动UI”的基本理念。

7.为输入框添加装饰

在很多情况下,我们的输入框都可能带有前后图标或者按钮,例如在注册界面的表单中,密码一栏可能就含有可以隐藏或者显示密码的小按钮。这些都可以通过TextField的参数进行设置:

代码执行结果如图2-21所示。

•图2-21 装饰TextField

username和password是我们要在两个输入框中显示的带状态的文字。代码中首次出现了Column,这是一个布局组件,在接下来的2.3节会详细介绍。可以简单地理解为它能将里面的元素从上到下垂直排列。

在第一个输入框中,通过leadingIcon参数为输入框添加了前置小图标。小图标由专门的Icon组件来展示,稍后会在2.2.2节详细介绍其使用。同理trailingIcon可以添加后置图标。

leadingIcon与trailingIcon都是@Composable(()-> Unit)类型,理论上可以摆放任意Composable组件。例如第二个输入框,在输入框尾部放置了一个IconButton,它除了显示Icon以外,还可以响应用户点击。

8.OutlinedTextField边框样式输入框

OutlinedTextField是按照Material Design规范设计的另一种风格的输入框,除了外观上它带有一个边框(如图2-22和图2-23所示),其他用法和TextField基本一致。

•图2-22 OutlinedTextField

•图2-23 OutlinedTextField(无Label)

9.BasicTextField基本演示

BasicTextField是一个更低级别的Composable组件,与TextField、OutlinedTextField不同的是,BasicTextField拥有更多的自定义效果。由于TextField和OutlinedTextField是根据Material Design准则设计的,我们无法直接修改输入框的高度,如果尝试修改高度,会看到输入区域被截断,影响正常输入。

代码执行结果如图2-24所示。

•图2-24 TextField输入框被截断

这是由于TextField没有暴露足够的参数供我们设置,而BasicTextField可以支持大多数的定制需求。首先来看看BasicTextField为我们提供了哪些可选参数。

可以看到,BasicTextField的参数和TextField有很多共同的地方,而我们自定义的关键在于最后一个参数decorationBox。decorationBox是一个Composable,它回调了一个innerTextField函数给我们。innerTextField是框架定义好给我们使用的东西,它就是文字输入的入口,所以需要创建好一个完整的输入框界面,并在合适的地方调用这个函数。接下来看一段代码并尝试理解它。代码结果如图2-25所示。

通过以上一个简单的代码,我们创建了一个自定义的输入框。在decorationBox中,传入了一个自己组合的Composable组件。这个Composable组件最外层由Column这个布局组件控制。Column能够将里面的元素从上到下垂直排列,第二个组件叫作Divider,可以使用这个组件创建一条分割线。通过使用合理的布局组件,将innerTextField组件和分割线组合起来,形成了一个输入框。这也就是BasicTextField的基本使用方法,如图2-25所示。

•图2-25 使用BasicTextField

10.实战:B站风格搜索框

decorationBox的存在使得我们可以发挥自己的想象力来创建一个输入框,接下来尝试制作出类似于哔哩哔哩App中的搜索输入框(如图2-26所示)。

•图2-26 搜索输入框

首先我们分析一下,在这个输入框中,含有前置的搜索图标,在未输入文字之前,有个提示的文字(Placeholder)在输入框中,在输入框有文字之后,输入框尾部含有一个可单击的关闭按钮,用来清空输入框当前所有的文字。输入框的外观为:白色的背景+圆角。分析完输入框的特点后,可以开始着手制作了,首先来声明出输入框的外观。

为了凸显输入框的形状,将当前页面的背景颜色设置为灰色,并将输入框放置于页面中央,如图2-27所示。

•图2-27 圆角输入框

可以看到,我们已经创建出来了一个带有圆角,背景颜色为白色的输入框。但是输入框的输入位置处于左上角,不符合预期,因为我们还没有合理地布置innerTextField。

按照需求,输入框内的样式应该由三部分组成,从左往右水平排列,分别是搜索图标、文字内容、取消按钮,如图2-28所示。

•图2-28 使用Row布局的SearchBar

像这种子项以水平排列的布局,一般使用线性布局组件Row来实现(将在2.3节详细介绍)。接下来在decorationBox中增加Row, verticalAlignment参数表示Row的子项垂直居中。

代码效果如图2-29所示。

•图2-29 使用Row布局innerTextField

接下来增加Placeholder,当文本框中没有输入内容时,显示Placeholder文字,它相比于正常的文字透明度更低,我们通过设置文字颜色透明度来达到这种效果。但是如何让innerTextField和这个Placeholder显示在同一个位置呢,这里使用帧布局组件Box进行实现,具体会在2.3节详细介绍。

代码效果如图2-30所示。

•图2-30 添加Placeholder

最后添加取消按钮,仅在输入框有输入内容时才显示。使用IconButton组件显示Icon,并在onClick中将输入框的内容清空。同时与Placeholder一样,根据text的状态控制是否显示:

代码效果如图2-31所示。

•图2-31 添加Clear按钮

至此我们使用Composable组件完成了一个B站样式的搜索框,是不是非常简单呢。

2.2.2 图片组件

1.Icon图标

Icon组件用于显示一系列小图标。Icon组件支持三种不同类型的图片设置:

我们除了直接传入具体类型的实例,也可以通过res/下的图片资源来设置图标:

如上所示,ImageVector和ImageBitmap都提供了对应的加载Drawable资源的方法,vectorResource用来加载一个矢量XML, imageResource用来加载jpg或者png图片。painterResource对以上两种类型的Drawable都支持,内部会根据资源创建对应的画笔进行图标的绘制。

接下来使用Icon组件显示一个具体的图标,如图2-32所示。

•图2-32 原黑色(左)、tint红色(右)

上述代码中我们直接使用了Material包预置的Favorite矢量图标,它是一个心形图标。

contentDescription参数服务于系统的无障碍功能,其中的文字会转换为语言供视障人士听取内容时使用,这个参数没有默认值,必须手动设置,也是官方有意为之,提醒开发者重视对于残障人士的关怀。但是contentDescription允许设置为null。

例子中的Favorite是一个Filled风格的图标,Material包每个图标都提供了五种风格可供使用,除了Filled,还包括Outlined、Rounded、Sharp、Two tone等,都可以通过Icons.xxx.xxx的方式调用。这五种风格在一些设计上的侧重点不同,如表2-3所示。

表2-3 Icon的五种类型

(续)

补充提示:

Icon组件不仅可以加载Material包里自带的图标,也能加载网络下载第三方图标。谷歌也提供了专门的图标库。我们可以从https://fonts.google.com/icons下载更多图标使用。此外,Material自带的包仅有一些常用的图标,如需使用其他所有的Material图标,可以添加依赖implementation "androidx.compose.material:material-icons-extended:$compose_version"。

2.Image图片

Image组件用来显示一张图片。它和Icon一样也支持三种类型的图片设置,这里以Painter类型的组件为例,展示一下它的参数列表:

跟Icon组件一样,可以使用painterResource来加载一个本地图片资源传入painter参数。项目中也经常需要加载一个远程的图片资源,这部分会在之后的章节介绍。

contentScale参数用来指定图片在Image组件中的伸缩样式,类似传统视图ImageView的scaleType属性,它有以下几种类型,如表2-4所示。

表2-4 ContentScale类型

colorFilter参数用来设置一个ColorFilter,它可以通过对绘制的图片的每个像素颜色进行修改,以实现不同的图片效果。ColorFilter有三种修改方式:tint、colorMatrix、lighting。

2.2.3 按钮组件

1.Button按钮

Button也是最常用的组件之一,它也是按照Material Design风格来实现的。本节让我们看看Button的基本使用,照例先看一下Button的参数列表,了解一下它的整体功能。

Button的第一个参数onClick是必填项,这是其最重要的功能,通过回调响应用户点击事件。最后一个参数content也是一个必填项,也是其最重要的功能之一。Compose的Button默认没有任何UI。仅仅是一个响应onClick的容器,它的UI需要在content中通过其他组件来实现。

最佳实践:

同样是Button的重要功能,为什么onClick和content的位置如此厚此薄彼呢?这是因为content是一个带有作用域的函数类型,将函数类型的参数放到列表的最后,在调用时可以省略圆括号,写出更符合DSL风格的代码。

创建一个显示文字的Button,代码效果如图2-33所示。

•图2-33 使用Button

content提供了RowScope的作用域,所以当我们想在文字前面水平摆放一个Icon时,只需要在content中顺序书写即可,代码效果如图2-34所示。

•图2-34 添加图标

可以根据需求,在content中实现各种复杂的Button样式。

Button有一个参数interactionSource,在前面的组件中也出现过。它是一个可以监听组件状态的事件源,通过它我们可以根据组件状态设置不同的样式,比如按钮按下时什么效果,正常时什么效果,类似传统视图中的Selector。interactionSource通过以下方法获取当前组件状态:

• interactionSource.collectIsPressedAsState()判断是否按下状态。

• interactionSource.collectIsFocusedAsState()判断是否获取焦点的状态。

• interactionSource.collectIsDraggedAsState()判断是否拖动。

来看下面的例子,通常状态下按钮边框为白色,当处于选中状态时,框线将变为绿色。

Button并非唯一可点击组件,理论上 任何Composable组件都可以通过Modifier.clickable修饰符化身为可点击组件 。而当Button被点击后,需要额外进行一些事件响应处理,比如显示Material Desgin风格的水波纹等,这些都是其内部通过拦截Modifier.clickable事件实现的处理,由于Modifier.clikable已经被内部实现所占用,Button需要提供单独的onClick参数供开发者使用。

注意:

Button的onClick在底层是通过覆盖Modifier.clickable实现的,所以不要为Button设置Modifier.clickable,即使设置了,也会因为被onClick覆盖而没有任何效果。

2.IconButton图标按钮

IconButton组件实际上只是Button组件的简单封装(一个可点击的图标),它一般用于应用栏中的导航或者其他行为。一般来说,我们需要在IconButton组件里提供一个图标组件,这个图标的默认尺寸一般为24×24dp。

效果如图2-35所示。

2.FloatingActionButton悬浮按钮

FloatingActionButton悬浮按钮(FAB)一般代表当前页面的主要行为。FAB组件也是需要我们提供一个Icon组件,效果如图2-36所示。

•图2-35 IconButton点击前(左)与点击后(右)

•图2-36 使用FAB

除了普通的FAB之外,Compose也提供了带有文字扩展的FAB,即ExtendedFloatingActionButton组件。代码及效果如图2-37所示。

•图2-37 使用拓展版FAB

2.2.4 选择器

1.Checkbox复选框

CheckBox允许用户从一个集合中选择一个或多个项目。复选框可以将一个选项打开或关闭。代码及效果如图2-38所示。

•图2-38 使用CheckBox

2.TriStateCheckbox三态选择框

很多时候,我们的复选框会有很多个,并且希望能够统一选择或者取消,这个时候就可以用TriStateCheckBox组件。代码与运行效果(图2-39)如下:

•图2-39 TriStateCheckbox

在子复选框全选中时,TriCheckBox显示已完成的状态,而如果只有部分复选框选中时,TriCheckBox则显示不确定的状态,当我们在这个时候单击它,则会将剩余没有选中的复选框设置为选中状态。

3.Switch单选开关

Switch组件可以控制单个项目的开启或关闭状态。代码及效果(图2-40)如下所示。

•图2-40 使用Switch

4.Slider滑竿组件

Slider类似于传统视图的Seekbar,可用来做音量、亮度之类的数值调整或者进度条。

其中colors参数用来设置滑竿各部位的颜色。滑竿组件中可设置颜色的区域很多,例如滑竿小圆球的颜色、滑竿进度颜色、滑竿底色等。step参数将进度条平分成(steps+1)段。比如当分成2段时,进度条在第一段之间拉动,超过第一段的一半就自动到第一段,没超过就退回到开始位置。Slider使用的代码如下,效果如图2-41所示。

•图2-41 使用Slider

2.2.5 对话框

1.Dialog对话框

Dialog组件的参数如下:

其中content允许我们通过传入自己的Composable组件来描述Dialog页面。例如下面这样Dialog,是我们的Dialog宽度不受限制,达到全屏的效果。

properties参数定制一些对话框特有的行为:

Compose的对话框不像传统视图的对话框那样通过show()、dismiss()等命令式的方式显隐,它像不同的Composable组件一样,显示与否要看是否在重组中被执行, 所以它的显示与否要依赖状态控制。Dialog和普通Composable组件的不同在于其底层需要依赖独立的Window进行显示。

下面的例子,展示了如何用状态控制Dialog的显隐:

在Dialog组件显示过程中,当我们点击对话框以外区域时,onDismissRequest会触发执行,修改openDialog状态为false,触发DialogSample重组,此时判断openDialog为false, Dialog无法被执行,对话框消失。有关状态和重组的更多内容会在第4章中详细介绍。

2.AlertDialog警告对话框

AlertDialog组件是Dialog组件的更高级别的封装,同时遵守着Material Design设计标准。它已经帮助我们定位好了标题、内容文本、按钮组的位置。我们只需要提供相应的组件即可。

代码运行效果如图2-42所示。

•图2-42 使用AlertDialog

2.进度条

Compose自带了两种Material Design的进度条,分别是圆形和直线的进度条,它们都有两种状态,一种是无限加载的,另一种是根据值来动态显示的,下面我们来看看一个圆形的进度条如何使用吧。代码与效果(图2-43)如下所示。

•图2-43 CircularProgressIndicator

代码中每次单击按钮,进度就会增加10%。当不设置progress时,就是无限加载的进度条。另外还有直线进度条(LinearProgressIndicator)可供选择,使用方法完全一致。 lX9C3RMdbrce/V19dfLEgLbupXjJ2MZ2ruVvGkQVpPKYtsM/SY5MZLix1mST6FVI

点击中间区域
呼出菜单
上一章
目录
下一章
×