Compose中的基本布局
1、简介
Compose是一个界面工具包,可让您轻松实现应用的设计。您只需描述自己想要的界面外观,Compose会负责屏幕上进行绘制。
1.1、学习内容
您将学习:
- 如何借助修饰符扩充可组合项?
- 如何通过Column和LazyRow等标准布局组件定位可组合项?
- 如何通过对齐方式和排列方式更改可组合项在父项的位置?
- 如何借助Scaffold和Bottom Navigation等Material可组合项创建详细布局?
- 如何使用槽位API构建灵活的可组合项?
2、从制定计划着手
下面我们来详细了解一下设计:
在需要实现设计时,最好先清楚了解其结构。不要立即开始编码,而应分析设计本身。如果将此界面拆分为多个可重复利用的部分?
接下来,我们试着分析一下设计。在最高抽象级别,我们可以将此设计分为两部分:
- 屏幕上的内容
- 底部导航栏
展开细目后,您可以看到屏幕内容包含三个子部分:
- 搜索栏
- ”Align your body“板块
- ”Favorite collections“板块
在每个版块中,您还可以看到一些可重复利用的较低级别组件:
- ”align your body“元素,显示在可水平滚动的行中。
- ”favorite collection“卡片,显示在可水平滚动的网格中。
现在,您已经分析了设计,接下来可以开始为每个已确定的界面部分实现可组合项。先从最低级别的可组合项着手,然后继续讲它们组合成更复杂的可组合项。完成后,您的新应用将与提供的设计相似。
3、搜索栏 - 修饰符
第一个要转换为可组合项的元素是搜索栏。我们来看一下设计:
之通过上面的屏幕截图,很难在实现该设计时让元素完美呈现。通常情况下,设计人员应传达更多关于设计的信息。他们可以授权您访问他们的设计工具,或分享所谓的用红线标注的设计。在此示例中,我们的设计人员提交了用红线标注的设计,方便您读出任何尺寸值。该设计采用8dp网格叠加层的方式显示,以便您轻松查看各个元素之间和周围的空间大小。此外,还明确添加了一些间距,以阐明特定尺寸。
您可以看到,搜索栏的高度应为56密度无关像素。它还应填充其父项的全宽。
若要实现搜索栏,请使用名为文本字段的Materila组件。Compose Material库包含一个名为TextField的可组合项,后者是此Material组件的实现。
从基本的TextField实现着手。在您的代码库中,打开MainActivity.kt并搜索SearchBar可组合项。
在名为SearchBar的可组合项,编写基本的TextField实现:
import androidx.compose.material.TextField
@Composable
fun SearchBar(
modifier: Modifier = Modifier
) {
TextField(
value = "",
onValueChange = {},
modifier = modifier
)
}
需要注意以下几点:
- 您已对文本字段的值进行了硬编码,
onValueChange回调不会执行任何操作。 SearchBar可组合函数接受modifier形参,并将其传递给TextField。这是符合Compose准则的最佳实践。这样一来,方法调用方就可以修改可组合项的外观和风格,使其更加灵活且可重复利用。
我们来看一看此可组合项的预览。请注意,您可以使用Android Studio中的预览功能快速迭代各个可组合项。MainActivity.kt包含您要在此构建的所有可组合项的预览。在此示例中,SearchBarPreview方法会渲染我们的SearchBar可组合项,并提供一些鳖精和内边距,以便提供更多上下文。完成您刚才天津爱的实现后,预览应如下所示:
不过,还缺少一些内容。首先,我们使用修饰符修正可组合项的尺寸。
编写可组合项时,您可以使用修饰符执行以下操作:
- 更改可组合项的尺寸、布局、行为和外观。
- 添加信息,例如无障碍标签。
- 处理用户输入
- 添加高级互动,例如使元素可点击、可滚动、可拖动或可缩放。
您调用的每个可组合项都有一个modifier形参,您可以设置该形参以适应相应可组合项的外观、风格和行为。设置修饰符时,您可以将多个修饰符方法串联起来,以创建更复杂的调试。
在此示例中,搜索栏的高度应至少为56dp,并填充其父项的宽度。为了找到适合搜索栏的修饰符,您可以浏览修饰符列表,并查看尺寸部分。对于高度,您可以使用heightIn修饰符。这可确保该可组合项具有特定的最小高度。但是,如果用户放大系统字体大小,该高度会随之变高。对于宽度,您可以使用fillMaxWidth修饰符,此修饰符可确保搜索栏占用其父项的所有水平空间。
更新修饰符以匹配下面代码:
import androidx.compose.material.TextField
@Composable
fun SearchBar(
modifier: Modifier = Modifier
) {
value = "",
onValueChange = {},
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
}
在此示例中,由于一个修饰符会影响宽度,另一个修饰符会影响高度。因此这些修饰符的顺序无关紧要。
您还必须设置TextField的一些形参。您可以试着设置形参值,以使可组合项类似于设计。同样,以下设计提供您参考:
更新实现时应采取以下步骤:
- 添加搜索图标。
TextField包含一个可接受其他可组合项的leadingIcon形参。您可以在其中设置Icon,在此势力中应为Search图标。请务必使用正确的ComposeIcon导入。 - 将文字字段的背景颜色设为MaterialTheme的
surface颜色。您可以使用TextFieldDetaults.textFieldColors替换特定颜色。 - 添加占位符文本"Search"(以字符串资源R.string.placeholder_search的形式显示)。
完成后,您的可组合项应如下所示:
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.ui.res.stringResource
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.Icons
import androidx.compose.material.filled.Search
@Composable
fun SearchBar(
modifier: Modifier = Modifier
) {
TextField(
value = "",
onValueChange = {},
leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null
)
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = MaterialTheme.colors.surface
),
placeholder = {
Text(stringResource(R.string.placeholder_search))
},
modifier = modifier
.fillMaxWidth()
.heightIn(min = 56.dp)
)
}
请注意:
- 您添加了一个用于显示搜索图标的
leadingIcon。此图标不需要内容说明,因为文本字段的占位符已经说明了文本字段的含义。请注意,内容说明通常用于实现无障碍功能,以文本形式向应用用户呈现图片或图标。 - 如果调整文本字段的背景颜色,请设置
colors属性。该可组合项包含一个组合形参,而不是每种颜色的单独形参。在这种情况下,您可以传入TextFieldDefaults数据类的副本,您可以通过该类仅更新不同的颜色。在此示例中,即仅更新背景颜色。 - 您设置的是最小高度,而不是固定高度。建议您采用这种方法,这样依赖,如果用户在系统设置增加字体大小,文本字段的带下仍会增加。
在此步骤中,您了解了如何使用可组合项的形参和修饰符来更改可组合项的外观和风格。这适用于Compose和Material库提供的可组合项,以及您自己编写的可组合项。您应该始终考虑通过提供形参来自定义所编写的可组合项。您还应添加modifier属性,以便从外部调整可组合项的外观和风格。
4、Align your body - 对齐
接下来,您要实现的可组合项“Align your body”元素。我们来看看该元素的设计,包括它旁边的红线设计:
红线设计现在还包含面向基线的间距。以下是我们从中获得的信息:
- 图片高度应为88dp
- 文本基线与图片之间的间距应为24dp
- 基线与元素底部的间距应为8dp
- 文本的排版样式应为H3.
文本基线是指字母所在的那一行。设计人员认为根据基线(而不是顶部或底部)对齐文本元素是一种最佳时间。
如需实现此可组合项,您需要一个Image和一个Text可组合项。您还需要将它们添加到Column中,以便一个位于另一个下方。
在您的代码中找到AlignYourBodyElement可组合项,使用以下基本实现更新器内容:
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.res.painterResource
@Composable
fun AlignYourBodyElement(
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
) {
Image(
painter = painterResource(R.drawable.ab1_inversions),
contentDescription = null
)
Text(
text = stringResource(R.string.ab1_inversions)
)
}
}
请注意:
- 您将图片的
contentDescription设为null,因为这张图片是纯装饰性的。图片下方的文本充分描述了图片的含义,因此无需为图片添加额外的说明。 - 您使用的是经过硬编码的图片和文本。在下一步,您要为这些内容改用
AlightYourBodyElement可组合项中提供的形象,使其变为动态形式。
查看此可组合项的预览:
您需要进行一些改进。最值得注意的是,图片过大,且形状不是圆形。您可以使用size和clip修饰符以及contentScale形参调整Image可组合项。
size修饰符会调整可组合项以适应特定尺寸,这类似于您在上一步看到的fillMaxWidth和heightIn修饰符。clip修饰符的工作原理有所不同,用于调整可组合项的外观。您可以将该修饰符设置为任何Shape,然后它会按照相应形状对可组合项的内容进行裁剪。
图片也需要正确缩放。为此,我们可以使用Image的contentScale形参。具体选项有很多,最值得注意的是:
在此示例中,裁剪类型就是要使用的正确类型。应用修饰符和形参后,您的代码应如下所示:
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.CircleShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
@Composable
fun AlignYourBodyElement(
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
) {
Image(
painter = painterResource(R.drawable.ab1_inversions),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(88.dp)
.clip(CircleShape)
)
Text(
text = stringResource(R.string.ab1_inversions)
)
}
}
现在,您的设计应如下所示:
接下来,通过设置Column的对其方式来水平对其文本。
一般来说,若要对其父容器中的可组合项,您应设置该父容器的对齐方式。因此,您应告知父项如何对齐其子项,而不是告知子项将其自身放置在父项中。
对于Column,您可以决定其子项的水平对其方式。具体选项包括:
- Start
- CenterHorizontally
- End
对于Row,您可以设置垂直对齐。具体选项类似于Column的选项:
- Top
- CenterVertically
- Bottom
对于Box,您可以同时使用水平对齐和垂直对齐。具体选项包括:
- TopStart
- TopCenter
- TopEnd
- CenterStart
- Center
- CenterEnd
- BottomStart
- BottomCenter
- BottomEnd
容器的所有子项都将遵循这一相同的对其模式。您可以通过单个子项添加align修饰符来替换其行为。
对于此设计,文本应水平居中。为此,请将Column的horizontalAlignment设置为水平居中:
import androidx.compose.ui.Alignment
@Compoable
fun AlignYourBodyElement(
modifier: Modifier = Modifier
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
) {
Image(
//..
)
Image(
//..
)
}
}
实现这些部分,您只需进行一些细微更改,即可使可组合项与设计完全相同。如果遇到问题,不妨尝试自行实现这些部分,也可以引用最终代码。考虑采取以下步骤:
- 将图片和文本变为动态形式。将它们作为实参传递给可组合函数。别忘了更新相应的预览,并传入一些经过硬编码的数据。
- 更新文本以使用合适的排版样式。
- 更新文本元素的基线间距。
执行完这些步骤后,您的代码应如下所示:
import androidx.compose.foundation.layout.paddingFromBaseline
@Composable
fun AlignYourBodyElement(
@DrawableRes drawable: Int,
@StringRes text: Int,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(88.dp)
.clip(CircleShape)
)
Text(
text = stringResource(text),
style = MaterialTheme.typography.h3,
modifier = Modifier.paddingFromBaseline(
top = 24.dp, bottom - 8.dp
)
)
}
}
5、“Favorite collection”卡片 - Material Surface
下一个可组合项的事项方式与"Align your body"元素类似。
此示例提供了可组合项的完整尺寸。您可以看到,文本那同样还应为H3。
此容器有某种背景颜色,不同于整个屏幕的背景颜色。它还带有圆角。由于设计人员没有指定颜色,因此我们可以假定颜色将由主题定义。对于此类容器,我们使用Material的Surface可组合项。
Surface是Compose Material库中的一个组件。该组件遵循常规的Material Design模式,您可以通过更改应用主题来调整他。
您可以根据自己的需求调整Surface,只需设置其形参和修饰符即可。在此示例中,表面应有圆角。针对这种情况,您可以使用shape形参。对于上一步中的图片,您将形状设置为shape,但在这一步中,您将使用来自我们的Material主题的值。
我们来看一看效果如何:
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Surface
@Composable
fun FavoriteCollectionCard(
modifier: Modifier = Modifier
) {
Surface(
shape = MaterialTheme.shapes.small,
modifier = modifier
) {
Row {
Image(
painter = painterResource(R.drawable.fc2_nature_meditations),
contentDescription = null
)
Text(
text = stringRecource(R.string.fc2_nature_meditations)
)
}
}
}
我们来看一下此实现的预览:
接下来,运用在上一步中学到的经验。设置图片尺寸,然后在容器中对其进行裁剪。设置Row的宽度,并与其子项垂直对齐。先尝试自行实施这些更改,然后再查看解决方案代码!
现在,您的代码如下所示:
import androidx.compose.foundation.layout.width
@Composable
fun FavoriteCollectionCard(
modifier: Modifier = Modifier
) {
Surface(
shape = MaterialTheme.shapes.small,
modifier = modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.width(192.dp)
) {
Image(
painter = painterResource(R.drawable.fc2_nature__meditations),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.size(56.dp)
)
Text(
text = stringResource(R.string.fc2_nature_meditations)
)
}
}
}
预览现在应如下所示:
如需完成此可组合项,wing实施以下步骤:
- 将图片和文本变为动态形式。将它们作为实参传入可组合函数。
- 更新文本以使用合适的排版样式。
- 更新图片与文本之间的间距。
最终结果应如下所示:
@Composable
fun FavoriteCollectionCard(
@DrawableRes drawable: Int,
@StringRes text: Int,
modifier: Modifier = Modifier
) {
Surface(
shape = MaterialTheme.shapes.small,
modifier = modifier
) {
Image(
painter = painterResource(drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.size(56.dp)
)
Text(
text = stringResource(text),
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
// ..
@Previrew(showBackground = true, backgroundColor = 0xFFF0EAE2)
@Composable
fun FavoriteCollectionCardPreview() {
MySootheTheme {
FavoriteCollectionCard(
text = R.string.fc2_nature_meditations,
drawable = R.drawable.fc2_nature_meditations,
modifier = Modifier.padding(8.dp)
)
}
}
翻译原文:Compose 基础知识