原文链接
MAD 技术第 1 集: Compose 布局 和 修饰符
欢迎来到关于Jetpack Compose 布局 和 修饰符 的 MAD 技术系列!在第一篇文章中,咱们将经过解说 布局 和 修饰符 的根底知识 来开端咱们的旅程。咱们将介绍他们是怎么协同作业的,Compose 供给了什么开箱即用的API,以及怎么漂亮地规划您的UI——一切这些内容,都将在为一款名为 Android Aliens 的迷你像素化游戏构建画面的一起介绍给你!
你们好啊,人类
假如你还没有检查之前的内容关于 Compose 根底知识的 MAD 技术系列,咱们主张你在持续之前看下这些内容,由于它将为你供给一个杰出的构建根底。
你也能够将这篇文章作为 MAD 技术视频 观看
布局——由于 Compose 中的简直一切内容都是布局
布局是Compose UI的中心组件,使您能够运用供给的各种现成API制造令人惊叹的app,并构建自己的自界说解决方案。在Compose中,您运用可组合函数来宣布UI部分,但布局指定了这些元素的 准确摆放和对齐。
因而,无论您运用 Compose 预置的布局还是构建您自己的自界说布局,布局都是 Compose国际的重要组成部分。 您能够将它们视为 Compose 和谐器 — 指示嵌套在其间的其他可组合项的结构。 事实上,咱们将在本系列的后边看到,简直 Compose UI 中的一切都是布局! 但咱们稍后会谈到这一点。
修饰符——把它们链接在一起!
现在,假如您以前运用过 Compose(假如您用过的话,对您来说是 ⭐(盲猜是技术已get的意思)!),您一定现已注意到修饰符对这个框架的重要性和关键性。 它们答应您装修和扩大可组合项,依照您需求的方法塑造它们,并使它们能够履行您需求它们履行的操作。 经过链接你想要的恣意数量的修饰符,您能够:
- 更改可组合项的巨细,布局,行为 和外观
- 增加附加信息,如辅佐功能标签
- 处理用户输入
- 使其可点击,可滚动,可拖动,或 可缩放
现在咱们现已介绍了布局和修饰符的关键,让咱们看看它们的实际使用!
开端游戏
让咱们卷起袖子,构建一个风趣的游戏画面,以了解布局和修饰符怎么协同作业。 咱们将从运用这两艘 Android Alien 飞船开端,逐渐构建游戏的完好屏幕截图:
所以咱们需求两艘飞船!可重复运用的 AndroidAlien
可组合项如下所示:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAlien(
color: Color,
modifier: Modifier = Modifier,
) {
Image(
modifier = modifier,
painter = painterResource(R.drawable.android_alien),
colorFilter = ColorFilter.tint(color = color),
contentDescription = null
)
}
然后,调用此可组合项两次,咱们将一个绿色和一个赤色外星人增加到父级 AndroidAliens
中:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliens() {
AndroidAlien(
color = Color.Red,
modifier = Modifier
.size(70.dp)
.padding(4.dp)
)
AndroidAlien(
color = Color.Green,
modifier = Modifier
.size(70.dp)
.padding(4.dp)
)
}
在构建像这样的简略可组合项时,这里有两件关于修饰符用法的事儿要解说:
- 在第一个代码段中,咱们依据 Compose API 最佳实践,设置了一个带有默许参数的
modifier
参数 - 在第二个片段中,咱们传递了一个由两个十分常见的修饰符组成的修饰符链来设置图画的巨细 和内边距
仅阅读最后一个代码示例,咱们或许期望(并期望)Compose 以特定次序放置这些子可组合项。 可是,当咱们运转它时,咱们只会在屏幕上看到一个元素:
一个落单的,孤单的 Android Alien
那艘赤色飞船似乎不见了。为了调试这个问题,让咱们增加赤色飞船的巨细:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliens() {
AndroidAlien(
color = Color.Red,
modifier = Modifier.size(100.dp)
)
// …
}
红飞船似乎有点害臊,躲在绿飞船后边。 这意味着,这两个可组合项实际上是相互堆叠的,由于它们缺少关于怎么布局的具体说明。 Compose 能够做许多事情,但它无法读懂你的主意! 这告诉咱们,咱们的飞船需求一些结构和编队,而这正是 Compose 布局所做的事情。
笔直和水平布局元素
咱们不期望咱们的外星飞船互相完全堆叠,而是期望它们被安置成一个挨着一个——这是一般 Android apps UI 元素的一个十分常见的用例。 咱们期望咱们的飞船只能够并排放置,以及一个在另一个之上:
为此,Compose 供给了 Column
和 Row
布局可组合项,分别用于笔直和水平布局元素。
让咱们首要将咱们的两个 AlienShips
移动到一个 Row
中,以水平摆放它们:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row {
AndroidAlien(…)
AndroidAlien(…)
}
}
相反,要笔直摆放 items,请将您的飞船包裹在一个Column
可组合项中:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensColumn() {
Column {
AlienShip(...)
AlienShip(...)
}
}
现在,这看起来不错,但咱们想进一步调整它们的定位。咱们期望它们在咱们的屏幕上对齐,如下所示:
咱们期望宇宙飞船“粘”在屏幕的特定角落,例如底部。 这意味着咱们需求相应地对齐和摆放这些船只,一起仍将它们坚持在Column
和Row
编队中。
要在您的Column
和Row
父级中指定元素的准确定位,您能够运用Arrangement
和Alignment
。 这些特点指示布局怎么定位其子项。 意思是,假如您期望嵌套的飞船“粘附”到 Column
父级的底端,请设置以下内容:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Top,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensColumn() {
Column(
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
可是,当咱们运转 app 时,咱们仍然会看到咱们之前的外星飞船:
咱们不能将它们粘到角落,由于像Column
和Row
这样的布局环绕着它们的子项的巨细并且不会超出那个范围,所以咱们看不到设置Arrangements
和Alignments
的效果。 为了让 Column
扩展到整个可用巨细,咱们将运用 .fillMaxSize()
修饰符,它的称号不言自明:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Top,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensColumn() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
AndroidAlien(…)
AndroidAlien(…)
}
}
这便是诀窍! 请注意Column
怎么运用笔直摆放和水平对齐,而Row
承受水平摆放和笔直对齐。 这是为什么呢?! 让咱们更具体地解说两者之间的差异。
Arrangement
指的是子布局在主轴上的布局方法 – Column
笔直,Row
水平。 Alignment对齐方法同理,可是是在穿插轴上 – Column
水平,Row
笔直:
有许多特点可供挑选和调整您的可组合项。 你乃至能够用各种方法将你的外星人离隔,这样他们就不会互相相处得太融洽。 检查文档 并探究或许合适您的规划要求的一切选项。
打破规矩
以这种方法在布局中对齐和摆放组件可保证将相同的规矩使用于一切子项。 可是,当咱们期望第三艘流氓外星飞船脱离强加的规矩时会产生什么? 让咱们分化一下确切的定位——咱们期望绿色和蓝色外星人遵循父级规矩指令,但咱们期望赤色外星人抵挡并“逃跑”:
叛军的呐喊
为完成这一点,Compose 供给了 .align()
修饰符 用于您期望单独定位的特定子可组合项,违反了由父级强制履行的规矩:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row(…) {
AndroidAlien(…)
AndroidAlien(…)
AndroidAlien(…)
AndroidAlien(
color = Color.Red,
modifier = Modifier.align(Arrangement.CenterVertically)
) // 流氓叛乱飞船
}
}
让咱们持续咱们的游戏构建,看看当一艘大型外星母船进入竞技场时会产生什么!
望文生义,咱们期望母舰比其他外星主体大。 在这个外星人行编队中,惯例船只应该占有他们需求正确烘托的最小宽度,而赤色母舰应该占有该行的剩下宽度:
这是一个常见的用例,您只期望 您的一些子 UI 元素具有比其他元素更大或更小的尺寸。 您能够仅手动更改一个元素的巨细,但这或许很麻烦,并且您或许期望巨细相对于其他元素是相对的,而不是静态的固定值。 为此,Column
和 Row
可组合项供给 .weight()
修饰符:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row(…) {
AndroidAlien(modifier = Modifier.size(70.dp)) // 正好占用 70 DP
AndroidAlien(modifier = Modifier.weight(1F)) // 占用剩下的(空间)
AndroidAlien(modifier = Modifier.size(70.dp)) // 正好占用 70 DP
}
}
母舰现已完全霸占了游戏空间!不为其他元素分配权重意味着它们将只占用所需的宽度(在 Row
中)或高度(Column
中),而剩下的宽度/高度将平均分配给已分配权重的元素。
可是,假如咱们期望其他惯例飞船刚好占有宽度的 1/4 而母舰占有 2/4 的话该怎么办?那么,咱们能够经过以下方法分配权重:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensRow() {
Row(…) {
AndroidAlien(modifier = Modifier.weight(1F))
AndroidAlien(modifier = Modifier.weight(2F))
AndroidAlien(modifier = Modifier.weight(1F))
}
}
您或许会注意到惯例的外星飞船现在也更大了。分配权重的默许行为是一起调整 item 的巨细,以习惯新分配的宽度/高度。 可是,假如您期望坚持元素的原始巨细,您能够将 false 传递给此修饰符的fill
参数:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
AndroidAlien(
modifier = Modifier
.size(70.dp)
.weight(1F, fill = false)
)
现在咱们现已介绍了怎么运用 Compose 简略但强壮的列
和行
以及一些便利的修饰符来笔直地和水平地布局元素,让您的 UI 超级整洁,让咱们持续!
堆叠元素
咱们游戏的一个重要功能是在游戏完毕 时通知您。让咱们看看您需求在外星人编队的顶部显示此叠加层的规划:
你能够看到这需求在外星人上面堆叠文字,让你十分清楚地知道游戏 现已完毕咧。 在 Compose 中,您能够运用 Box
布局作为将元素放在互相之上或堆叠它们的快速方法,并遵循可组合项的履行次序:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensGameOverBox() {
Box {
AndroidAliensRow(…)
Text(
text = “GAME OVER”
// …
)
}
}
与咱们之前的布局类似,Box
可组合项还供给了一种方法来指示它怎么安置其嵌套的子项。 这里的差异在于水平和笔直之间没有差异——相反,它们合并为一个 contentAlignment
:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensGameOverBox() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
AndroidAliensRow(…)
Text(
text = “GAME OVER”
// …
)
}
}
在前面的代码片段中,咱们运用 contentAlignment = Alignment.Center
将子元素居中对齐,可是您也能够运用任何现有的 2D 对齐方法 。这使得 Box
布局在您期望放置嵌套的子项时十分有用,好吧,简直在您想要的任何位置!
假如游戏的确完毕了,您的主游戏屏幕或许看起来更漂亮,带有叠加的透明背景,更突显您无法再玩了:
要是有一种方法能够在“GAME OVER”Text
后边设置一个灰色透明背景,然后将其展开以覆盖可用巨细就好了 … 你别说还真有!
Box
布局供给了一个便利的 .matchParentSize()
修饰符,它答应子元素匹配包括的 Box
的巨细:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensGameOverBox() {
Box(…) {
AndroidAliensRow(…)
Spacer(
modifier = Modifier
.matchParentSize()
.background(color = Color.Gray.copy(alpha = .7f))
)
Text(…)
}
}
可是,请记住,此子元素不参与界说Box
父级的最终巨细。相反,在首要测量了一切其他子项(不运用matchParentSize()
修饰符)以取得完好的Box的
巨细后,它会匹配Box
的巨细。相比之下,.fillMaxSize()
修饰符将参与界说Box
的巨细,它使元素占有一切可用空间。
您或许会注意到 matchParentSize
仅在 Box
可组合项中可用。 为什么呢,还有这是怎么做到的? 这让咱们想到了 Compose 中一个十分重要的修饰符概念:scope safety.
修饰符效果域安全
在 Compose 中,有些修饰符只能在使用于某些可组合项的子项时才能运用。 Compose 经过自界说效果域强制履行此操作。 这便是为什么 matchParentSize
仅在 BoxScope
中可用,而 weight
仅在 ColumnScope
和 RowScope
中可用。这能够避免您增加在其他当地底子不起效果的修饰符,并节省您反复实验的时刻。
插槽组件
当咱们玩的时分,咱们的小游戏肯定会从一些关于游戏进度的信息中获益——比如一个带有当时分数的标题和一个位于底部的按钮来开端或暂停游戏,这些信息将其他的游戏内容包括在两者之间:
这或许会让您想起像顶部和底部栏这样的东西,咱们的标题和按钮将放在其间。为了完成这一点,Compose 供给了一组十分便利的开箱即用的可组合项。 所以咱们不得不提一下 Material components!
Jetpack Compose 供给 Material Design 的完成,这是一个用于创建数字界面的综合规划系统。 按钮、卡片、开关 等资料组件以及Scaffold
等布局可用作可组合函数。 它们一起代表了用于创建用户界面的交互式构建块。 有许多可供挑选,因而请务必检查 Compose Material API 参考 了解具体信息。
在咱们增加一个标题作为顶部和一个按钮作为底部栏的比如中,Compose 和 Material 供给了Scaffold
布局,其间包括用于将各种组件安置成常见屏幕模式的插槽 . 因而,让咱们将开端按钮和得分标题增加为顶部和底部栏:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensWithInfo() {
Scaffold(
topBar = {
InfoHeader(…)
},
bottomBar = {
Button(…) {
Text(
text = “PRESS START”
)
}
}
) {
AndroidAliens(…)
}
}
顶部和底部栏的其他常见用例是常绿工具栏和底部导航栏。可是由于 Scaffold
可组合参数承受通用可组合 lambdas,您能够传入您想要的任何类型的可组合项:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun Scaffold(
// …
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
// …
)
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensWithInfo() {
Scaffold(
topBar = {
ComposeShipsRow(…)
},
bottomBar = {
AndroidAliensRow(…)
}
) {
AndroidAliens(…)
}
}
这个开放式插槽概念称为 Slot API 并且被许多运用在 Compose 中。Scaffold
还承受 floating action button,snackbar,和许多其他自界说选项。
现在,您或许会注意到咱们运用了一个 Button
可组合项作为底部栏。 这只是 Material 组件供给的许多开箱即用的解决方案中的一种。 它支撑快速向屏幕增加按钮并在其间供给任何类型的内容——文本、图画和其他内容,因而也运用插槽 API 概念。 要了解有关整个 Material 组合的更多信息,请检查 Material 组件文档。
棒极了! 咱们的游戏进展十分顺利。 但与咱们的游戏相同,咱们的 Compose 教程也会增加每个等级的难度。 那么让咱们进入下一个应战。
按需增加内容
到目前为止,咱们在屏幕上以简略的 SVG 形式展现了数量十分有限的外星飞船。 可是,假如咱们有数百或数千呢? 假如它们是动画的呢?!
外星飞船 StackOverflow
在那种情况下,这个小小的入侵或许会导致一些严重的卡顿。 从后端一次加载您的一切内容,尤其是当它包括大数据集, 许多图画或视频时,或许会影响您使用的功能。相反,假如您能够按需加载内容,在滚动时一点一点地加载会怎么样?我个人最喜欢的场景,Compose 中的 Lazy 组件。
当 items 可见时,Lazy 列表会在屏幕上烘托一个可滚动的 items 列表,而不是一次性悉数显示出来。为了构建一个令人印象深刻的绿色 Android Aliens 快速网格,咱们将运用LazyVerticalGrid
可组合项,然后在其上设置固定数量的列并向其间增加 50 个 AndroidAlien
items:
/* Copyright 2023 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AndroidAliensGrid() {
// 准确地指定网格应该有 5 列
LazyVerticalGrid(columns = GridCells.Fixed(5)) {
// 增加 50 个绿色 Android 外星人 items
items(50) {
AndroidAlien(…)
}
}
}
还有许多其他 Lazy 组件可供挑选:LazyHorizontalGrid
、LazyColumn
、LazyRow
以及最近的交织网格 — LazyVerticalStaggeredGrid
和 LazyHorizontalStaggeredGrid
。
Lazy 布局是一个巨大的 Compose 领域,很容易单独成文,因而要取得关于此 API 的最佳和最翔实的信息,请检查Compose 视频中的 Lazy 布局,咱们在其间解说了有关 Lazy 布局的一切知识。
游戏关卡
咱们今日涵盖了许多内容!从 Compose 布局和修饰符及其相互协作的最根底的知识,到供给哪些开箱即用的 API、Material 组件和 Slot API,以及怎么按需增加内容——一切这些都是在构建咱们自己的游戏屏幕时会触及的内容。
请持续关注下一篇文章,咱们将在其间介绍 Compose 的各个阶段。 当然,别忘了订阅定期更新!
额定资源:
Compose 中的基本布局 codelab:
developer.android.com/codelabs/je…
Compose 中的基本布局 codealong:
www.youtube.com/watch?v=kyH…
这篇博文是系列博文的一部分:
第 1 集:Compose 布局和修饰符的根底知识
第 2 集:Compose 的各个阶段
第 3 集:约束和修饰符次序
第 4 集:高级布局概念
Mad 技术
Jetpack Compose
UI 规划
Codelab
教程