一、准备工作
JetPack Compose是一款新型工具包,旨在协助简化界面开发。该工具包将呼应式编程模型与简练易用的Kotlin编程语言相结合,并选用彻底声明式的代码编写办法,让您能够经过调用一些列函数来描绘界面,这些函数会将数据转换为界面层次结构。当底层数据产生改变时,框架会主动从头履行这些函数,为您更新界面结构。
Compose运用由可组合函数构成。可组合函数即带有@Composable
符号的惯例函数,这些函数能够调用其他可组合函数。运用一个函数就能够创立一个新的界面组件。该注解会奉告Compose为函数增加特别支撑,以便后续更新和维护界面。凭借Compose,您能够将代码规划成多个小代码块。可组合函数通常简称为“可组合项”。
经过创立可重用的小型可组合项,您能够轻松构建运用中的所用界面元素的库。每个可组合项对应屏幕的一部分,能够独自修正。
经过这篇博客你将学习:
- 什么是Compose
- 怎么运用Compose构建界面
- 怎么在可组合函数中管理状况
- 怎么创立高效列表
- 怎么增加动画
- 怎么为运用设置样式和主题 您将构建一个包括初始装备屏幕和一系列动画翻开项的运用:
2、发动新的Compose项目
如需发动新的Compose项目,请翻开Android Studio,然后挑选Start a new Android Studio project,如图所示: 假如体系未显现上述界面,请依次进入File>New>New Project
创立新项目时,请从可用模板中挑选Empty Compose Activity(Material3)
点击Next,然后照常装备项目,并将其命名为Basics Codelab。 请确保您挑选minmumSdkVersion至少API级别21,这是Compose支撑的最低API级别。
挑选Empty Compose Activity(Material3) 模版后,体系会在项目中为您生成以下代码:
- 该项目已装备为运用Compose
- 已创立
AndroidManifest.xml
文件 -
build.gradle
和app/build.gradle
文件包括Compose所需的选项和依赖项。
同步项目后,翻开MainActivity.kt
并检查代码。
class MainActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstamceState)
setContent {
BasicsCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
Greeting("Android")
}
}
3、Compose运用入门
了解Android Studio为您生成的与Compose相关的各种类和办法。
3.1、可组合函数
可组合函数是带有@Composable
注解的惯例函数。这类函数本身能够调用其他的@Composable
函数。咱们会展示怎么为Greeting
函数增加@Composable
符号。此函数会生成一段显现给定输入String
的界面层次结构。Text
是由库供给的可组合函数。
@Composable
private fun Greeting(name: String) {
Text(text = "Hello $name!")
}
留意:可组合函数是带有@Composable
注解的Kotlin函数,如上述代码所示。
3.2、Android运用中的Compose
运用Compose时,Activity
仍然是Android运用的进口点。在咱们的项目中,用户翻开运用时会发动MainActivity
(如AndroidManifest.xml
文件中所指定)。您能够运用setContent
来界说布局,但不同于在传统View体系中运用XML文件,您将在该函数中调用可组合函数。
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicsCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
}
}
BasicsCodelabTheme
是为可组合函数设置样式的一种办法。如需检查文本在屏幕上的作用,您能够在模拟器或设备上运转运用,或运用Android Studio预览进行检查。
若要运用Android Studio预览,您只需运用@Preview
注解符号一切五参数可组合函数或选用默许形参的函数,然后构建那您的项目即可。现在MainActivity.kt
文件中现已包括了一个Preview Composable
函数。您能够在同一个文件中包括多个预览,并为它们指定名称:
@Preview(showBackground = true, name = "Text preview")
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
Greeting(name = "Android")
}
}
4、微调界面
首要,为Greeting
设置不同的色彩背景。为此,您能够用Surface
包围Text
可组合项。Surface
会选用一种色彩,因而请运用MaterialTheme.colorScheme.primary
。
留意:`Surface`和`MaterialTheme`相关的概念。Material Design是Google供给的一个规划体系,旨在协助您构建界面和体会。
@Composable
private fun Greeting(name: String) {
Surface(color = MaterialTheme.colorScheme.primary) {
Text (text = "Hello $name!")
}
}
将上述代码增加到项目后,您会在Android Studio的右上角看到Build & Refresh按钮。点按该按钮或构建项目即可在预览中检查新更改
您能够在预览中检查新更改:
您或许忽略了一个重要的细节:为现在是白色的。 咱们是怎么对此进行界说的?
咱们并没有对此进行界说!Material组件(例如 androidx.compose.material3.Surface
)旨在供给运用中或许需求的额常见功用(例如为文本挑选恰当的色彩),让您获得更好的体会。咱们之所以说Material很有用,是由于它供给在大多数运用中都会用到在运用默许值和模式。Compose中的Material组件是在其他基础组件(坐落androidx.compose.foundation
中)的基础上构建的。假如您需求更高的灵活性,也能够从您的运用组件中拜访这些组件。
在这种情况下,Surface
会了解,当该背景设为primary
色彩后,其上的任何文本都应运用onPrimary
色彩,此色彩也在主题中进行了界说。
4.1、修饰符
大多数Compose界面元素(例如Surface
和Text
都承受可选的modifier
参数。修饰会指示界面元素怎么在其父布局中放置、显现或体现)。
例如,padding
修饰符会在其修饰的元素周围运用必定的空间。您能够运用Modifier.padding()
创立内边距修饰符。
现在,为屏幕上的Text
增加内边框。
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
...
@Composable
private fun Greeting(name: String) {
Surface(color: MaterialTheme.colorScheme.primary) {
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
}
}
点击Build&Refresh即可检查新更改
5、重复运用可组合项
您增加到界面的组件越多,创立的嵌套层级就越多。假如函数变得十分大,或许会影响可读性。经过创立可重用的小型组件,能够轻松构建运用中所用界面元素的库。每个组件对应于屏幕的一个部分,能够独自修正。
最佳实践是,您的函数应包括一个修饰符参数,体系默许为该参数分配空修饰符。将此修饰符转发到您在函数内联调用的第一个可组合项。这样,调用点就能够在组合函数之外调整布局指令和行为了。
创立一个名为MyApp
的可组合项,该组合项中包括问候语.
@Composable
private fun MyApp(modifier: Modifier = Modifier) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
这样一来,由于现在能够重复运用MyApp
可组合项,您就能够省去onCreate
回调和预览,然后防止重复编写代码。您的MainActivity.kt
文件应如下所示:
package com.example.basicscodelab
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.io.unit.dp
import com.example.basocscodelab.ui.theme.BasicsCodelabTheme
class MainActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BasicsCodelabTheme {
MyApp(modifier = Modifier.fillMaxSize())
}
}
}
}
@Composable
private fun MyApp(modifier: Modifier = Modifier) {
Surface(
modifier = modifier,
color = MaterialTheme.colorScheme.backgrounf
) {
Greeting("Android")
}
}
@Composable
private fun Greeting(name: String) {
Sueface(color = MaterialTheme.colorScheme.primary
) {
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
}
}
@Previer(showBackground = true)
@Composable
private fun DefaultPreview() {
BasicsCodelabTheme {
MyApp()
}
}
6、创立列和行
Compose中的三个基本规范布局元素是Column
、Row
和Box
可组合项。
它们是承受可组合内容的可组合函数,因而您能够在其中放置项目。例如,Column
中的每个子级都将垂直放置。
// Don't copy over
Column {
Text("First row")
Text("Second row")
}
现在尝试更改Greeting
,使其显现包括两个文本元素的列,如以下示例中所示:
请留意,您或许需求移动周围的内边距。
将您的结果与此解决方案进行比较:
import androidx.compose.foundation.layout.Column
...
@Composable
private dun Greeting(name: String) {
Surface(color = MaterialTheme.colorScheme.primary) {
Column(modifier = Modifier.padding(24.dp)) {
Text(text = "Hello,")
Text(text = name)
}
}
}
6.1、Compose和Kotlin
可组合函数能够像Kotlin中的其他函数相同运用。这会使界面构建变得十分有效,由于您能够增加语句来影响界面的显现办法。
例如,您能够运用for
循环向Column
中增加元素:
@Composable
fun MyApp(
modifier: Modifier = Modifier,
name: List<String> = listOf("World", "Compose")
) {
Column(modifier) {
for (name in names) {
Greeting(name = name)
}
}
}
您尚未设置可组合项的尺寸,也未对可组合项的巨细增加任何约束,因而每一行仅占用或许得最小空间,预览时的作用也是如此。让咱们更改预览作用,以模拟器小屏幕手机的常见宽度320dp。按如下所示向@Preview
注解增加widthDp
参数:
@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
BasicsCodelabTheme {
MyApp()
}
}
修饰符在Compose中运用十分广泛,现在咱们来练习更高档的用法:尝试运用fillMaxWidth
和padding
修饰符仿制以下布局。
现在,将您的代码与解决方案进行比较:
@Composable
fun MyApp(
modifier: Modifier = Modifier,
names: List<String> = listOf("World", "Compose")
) {
Column(modifier = modifier.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
@Composable
private fun Greeting(name: String) {
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
Text(text = "Hello,")
Text(text = name)
}
}
}
请留意:
- 修饰符能够包括重载,因而具有相应的优势,例如您能够指定不同的办法来创立内边距。
- 若要向一个元素增加多个修饰符,您只需求将它们链接起来即可。
有多种办法能够完成此结果,因而,假如您的代码榆次代码不同,并不表明您的代码便是错的。
6.2、增加按钮
接下来,您将增加一个用于翻开Greeting
的可点击元素,因而需求先增加对应的按钮。您的方针是要创立以下布局:
Button
是material3软件包供给的一种可组合项,它选用可组合项作为最后一个参数。由于跟随lambda能够移到括号之外,因而您能够向按钮增加任何内容作为子级,例如Text
:
Button(
onClick = { }
) {
Text("Show less")
}
留意:Compose依据Material Design按钮规范供给了不同类型的Button
:Button
、ElevatedButton
、FilledTonalButton
、OutlinedButton
和TextButton
。
为了完成这一点,您需求学习怎么在尾行放置可组合项。由于没有alignEnd
修饰符,因而您需求在开始时为该组合项赋予必定的weight
。weight
修饰符会让元素填满一切可用空间,使其“具有弹性”也便是会推开起他们有权重的元素。该修饰符还会使fillMaxWidth
修饰符变得多余。
下面列出了对应的解决方案代码:
import androidx.compose.foundation.layout.Row
import andtoidx.compose.material3.ElevatedButton
...
@Composable
private fun Greeting(name: String) {
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier.weight(1f)) {
Text(text = "Hello,")
Text(text = name)
}
ElevatedButton(
onClick = { /* TODO */}
) {
Text("Show more")
}
}
}
}
7、Compose中的状况
在本部分中,您将向屏幕中增加一些互动。到目前为止,您现已创立了一些静态布局,但现在要让她们相运用户更改,以到达下面的作用。
在开始了解怎么运用按钮可点击以及怎么调整内容巨细之前,您需求再某个方位存储某个值,用于指示每项内容是否翻开(即内容的状况)。由于咱们需求为每条问候语设定这两个值之一,因而其逻辑方位坐落Greeting
可组合项中。咱们来看看此expanded
布尔值及其代码中的运用办法:
@Composable
private fun Greeting(name: String) {
var expanded = false // Don't do this!
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier.weight(1f)) {
Text(text = "Hello,")
Text(text = name)
}
ElevatedButton(
onClick = { expanded = !expanded}
) {
Text(if (expanded) "Show less" else "Show more")
}
}
}
}
请留意,咱们还增加了onClick
操作和动态按钮文本。
可是,此设置无法依照预期发挥作用。为expanded
变量设置不同的值不会使Compose将其检测为状况更改,因而不会产生任何作用。
Compose运用经过调用可组合函数将数据转换为界面。假如您的数据产生改变,Compose会运用新数据从头履行这些函数,然后创立更新后的界面,此进程称为重组。Compose还会检查各个可组合项需求哪些数据,以便只需重组数据产生了改变的组件,而防止重组未影响的组件。
可组合函数能够按任意次序频繁履行,因而您不能以代码的履行次序或该函数的重组次数为判别依据。
更改此变量不会触发足够的原因是Compose并未盯梢此更改。此外,每次调用Greeting
时,都会将该变量重置为false。
如需向可组合项增加内部状况,能够运用mutableStateOf
函数,该函数可让Compose重组读取该State
的函数。
State和MutableState是两个接口,它们具有特定的值,当该值产生改变时,它们就会触发界面更新(重组)。
import androidx.compose.runtime.mutableStateOf
...
@Composable
fun Greeting() {
val expanded = mutableStateOf(false) // Don't do this!
}
可是,不能只是将mutableStateOf
分配给可组合项中的某个变量。如前所述,重组或许会随时产生,这会再次调用可组合项,然后将状况重置为值为false
的新可变状况。
如需在重组后保留状况,请运用remember
记住可变状况。
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
...
@Composable
fun Greeting() {
val expanded = remember { mutableStateOF(false) }
...
}
remember
能够起到维护作用,防止状况在重组时被重置。
请留意,假如从屏幕的不同部分调用同一可组合项,则会创立不同的界面元素,且每个元素都会拥有自己的状况版别。您能够将内部状况视为类中的私有变量。
可组合函数会主动“订阅”状况。假如状况产生改变,读取这些字段的可组合项将会重组以显现更新。
7.1、更改状况和呼应状况更改
您或许现已留意到,为了更改状况,Button
具有一个名为onClick
的形参,但它不承受值,而承受函数。
您或许不熟悉以这种办法运用的函数,这其实便是一种Compose中广泛运用的十分强壮的Kotlin功用。函数是Kotlin中的首要元素,您能够将它们分配给某个变量,传递给其他函数,甚至能够从它们本身返回函数。
您能够经过为”onClick”指定lambda表达式,界说点击时将履行的操作。例如,切换翻开状况的值,并依据该值显现不同的文本。
ElevatedButton(
onClick = { expanded.value = !expanded.value },
) {
Text(if (expanded.value) "Show less" else "Show more")
}
假如在模拟器中运转运用,您会看到点击该按钮时,expanded
会切换,然后触发重组该按钮的文本。每个Greeting
都具有自己的翻开状况,由于它们属于不同的界面元素。
到目前为止的代码:
@Composable
private fun Greeting(name: String) {
val expanded = remember { mutableStateOf(false) }
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4/dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier.weight(1f)) {
Text(text = "Hello,")
Text(text = name)
}
ElevatedButton(
onClick = { expanded.value = !expanded.value }
) {
Text(if (expanded.value) "Show less" else "Show more")
}
}
}
}
7.2、翻开内容
现在,咱们来依据请求实践翻开内容。增加一个依赖于状况的额定变量:
@Composable
private fun Greeting(name: String) {
val expanded = remember { mutableStateOf(false) }
val extraPadding = if (expanded.value) 48.dp else 0.dp
}
您无需在重组后记住extraPadding
,由于它仅履行简略的核算。
现在咱们能够将新的内边距修饰符运用于Column:
@Composable
private fun Greeting(name: String) {
val expanded = remember { mutableStateOf(false) }
val extraPadding = if (expanded.value) 48.dp else 0.dp
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(
modifier = Modifier.weight(1f)
.padding(bottom = extraPadding)
) {
Text(text = "Hello, ")
Text(text = name)
}
ElevatedButton(
onClick = { expanded.value = !expanded.value }
) {
Text(if (expanded.value) "Show less" else "Show more")
}
}
}
}
假如在模拟器上运转,您应该会看到每项内容均可独自翻开。
8、状况提高
可这函数中,被多个函数读取或修正的状况坐落一起先人实体中,此进程称为状况提高。“提高”意味“提高”或“升级”。
使状况可提高,能够防止仿制状况和引进bug,有助于重复运用可组合项,并大大降低可组合项的测验难度。相反,不需求有可组合项的父级操控的状况则不应该被提高。可信来源属于该状况的创立者和操控者。
例如,让咱们来为运用创立一个初始装备屏幕
将以下代码增加到MainActivity.kt
:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.material3.Button
import androidx.compose.runtime.getValue
import androidx.compose.runtimw.setValue
import androidx.compose.ui.Alignment
...
@Composable
fun OnboardingScreen(modifier: Modifier = Modifier) {
// TODO: This state should be hoisted
var shouldShowOnboarding by remember { mutableStateOf(true) }
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = { shouldShowOnboarding = false }
) {
Text("Continue")
}
}
}
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
BasicsCodelabTheme {
onboardingScreen()
}
}
此代码包括多个新功用:
- 您现已增加了一个名为
OnboardingScreen
的新可组合项以及一个新的预览。构建项目时,您会发现您能够一起拥有多个预览。咱们还增加了一个固定高度,以验证内容是否正确对齐。 - 能够装备
Column
,使其在屏幕中心显现其内容。 -
shouldShowOnboarding
运用的是by
关键字,而不是=
。这是一个特点托付,可让您无需每次都输入.value
- 点击该按钮时,会将
shouldShowOnboarding
设为false
,虽然您并未从任何方位读取该状况。
现在,咱们即可将这个新的初始装备屏幕增加到运用。咱们希望该屏幕在运用发动时显现,然后在用户按“持续”时躲藏。
在Compose中,您不会躲藏界面元素,由于不会将它们增加到组合中,因而它们也不会增加到Compose生成的界面数中。您只需求运用简略的Kotlin条件逻辑就能够做到这一点。例如,如需显现初始装备屏幕或问候语列表,您需求履行以下操作:
@Composable
fun MyApp(modifier: Modifier = Modifier) {
Surface(modifier) {
if (shouldShowOnboarding) {// Where does this come from?
OnboardingScreen()
} else {
Greetings()
}
}
}
可是,咱们无法拜访shouldShowOnboarding
。很明显,咱们需求与MyApp
可组合项共享在OnboardingScreen
中创立的状况。
咱们不会以某种办法与状况的父级共享状况值,而是会提高该状况,也便是将状况转移到需求拜访它的一起先人实体中。
首要,将MyApp
的内容移到名为Greetings
的新可组合项中。此外,调整预览以改为调用Greetings
办法:
@Composable
fun MyApp(modifier: Modifier = Modifier) {
Greetings()
}
@Composable
private fun Greeting(
modifier: Modifier = Modifier,
name: List<String> = listOf("World", "Compose")
) {
Column(modifier = modifer.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
private fun GreetingsPreview() {
BasicsCodelabTheme {
Greetings()
}
}
为新的尖端MyApp可组合项增加预览,以便测验其行为:
@Preview
@Composable
fun MyAppPreview() {
BasicsCodelabTheme {
MyApp(Modifier.fillMaxSize())
}
}
现在,增加相应的逻辑来显现MyApp
中的不同屏幕,并提高状况。
@Composable
fun MyApp(modifier: Modifier = Modifier) {
var shouldShowOnboarding by remember { mutableStateOf(true) }
Surface(modifier) {
if (shouldShowOnboarding) {
OnboardingScreen(/* TODO */)
} else {
Greetings()
}
}
}
咱们还需求与初始化装备屏幕共享shouldShowOnboarding
,但咱们不会直接传递它。与其让OnboardingScreen
更改状况,不如让它在用户点击”Continue”按钮时通知咱们。
怎么向上传递时刻?经过向上传递回调来传递。回调是这样一类函数,它们以实参的形式传递给其他函数,并在事情产生时履行。
尝试向初始装备屏幕增加界说为onContinueClicked: () -> Unit
的函数参数,以便您能够从MyApp
更改状况。
解决方案:
@Composable
fun MyApp(modifier: Modifier = Modifier) {
var shouldShowOnboarding by remember { mutableStateOf(true) }
Surface(modifier) {
if (shouldShowOnboarding) {
OnboardingScreen(onContinueClicked = { shouldShowOnboarding = flase })
} else {
Greetings()
}
}
}
@Composable
fun OnboardingScreen(
onContinueClicked: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = onContinueClicked
) {
Text("Continue")
}
}
}
经过向OnboardingScreen
传递函数而不是状况,能够提高该组合项的可重用性,并防止状况被其他可组合项更改。一般来说,这能够让事情变得简略。一个很好的比如便是,现在需求怎么修正初始装备屏幕预览来调用OnboardingScreen
:
@Preview(shouldBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
BasicsCodelabTheme {
OnboardingScreen(onContinueClicked = {}) // Do nothing on click.
}
}
将onContinueClicked
分配给空lambda表达式就等于“什么也不做”,这十分适合于预览。
看起来现已越来越像一个真正的运用了。
到目前为止的完整代码:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.codelab.basics.ui.theme.BasicsCodelabTheme
class MainActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposePractiseTheme {
MyApp(modifier = Modifier.fillMaxSize())
}
}
}
}
@Composable
fun MyApp(modifier: Modifier = Modifier) {
var shouldShowOnboarding by remember { mutableStateOf(true) }
Surface(modifier) {
if (shouldShowOnboarding) {
OnboardingScreen(onContinueClicked = { shouldShowOnboarding = false })
} else {
Greetings()
}
}
}
@Composable
fun OnboardingScreen(
onContinueClicked: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Welcome to the Basics Codelab!")
Button(
modifier = Modifier.padding(vertical = 24.dp),
onClick = onContinueClicked
) {
Text("Coutinue")
}
}
}
@Composable
private fun Greetings(
modifier: Modifier = Modifier,
names: List<String> = listOf("World", "Compose")
) {
Column(modifier = modifier.padding(vertical = 4.dp)) {
for (name in names) {
Greeting(name = name)
}
}
}
@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
ComposePractiseTheme {
OnboardingScreen(onContinueClicked = {})
}
}
@Composable
private fun Greeting(name: String) {
val expanded = remember { mutableStateOf(false) }
val extraPadding = if (expanded.value) 48.dp else 0.dp
Surface(
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
) {
Row(modifier = Modifier.padding(24.dp)) {
Column(
modifier = Modifier.weight(1f).padding(bottom = extraPadding)
) {
Text(text = "Hello,")
Text(text = name)
}
ElevatedButton(
onClick = { expanded.value = !expanded.value }
) {
Text(if (expanded.value) "Show less" else "Show more")
}
}
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
ComposePractiseTheme {
Greetings()
}
}
@Preview
@Composable
fun MyAppPreview() {
ComposePractiseTheme {
MyApp(Modifier.fillMaxSize())
}
}
翻译原文:Compose 基础知识