目录
- 前言
- 可组合函数
- 显示简单文本
- 将样式应用于文本
- 使用 TextField 进行输入
- 在 Android Studio 中预览
- 预览参数
- Column
- Scrollable Column
- Lazy Column
- Box
- Button
- Card
- Clickable
- Image
- Alert Dialog
- Material AppBar
- Material BottomNavigation
- Material Checkbox
- Material ProgressBar
- Material Slider
- Material Snackbar
- Custom View
- Crossfade动画
前言
在本章节中,我们将学习 Jetpack Compose,这是一个用于构建原生 UI 的现代工具包。
通过这个完整的教程,我们将学习如何使用 Text、TextField、Preview、Column、Row、Button、Card、AlertDialog、MaterialDesign 元素等。因此,事不宜迟,让我们开始创建一个 Jetpack Compose 项目。因此,本章节是关于通过示例学习适用于 Android 的 Jetpack Compose。
注意:要使用 Jetpack Compose,您需要拥有最新的 Canary 版本的 Android Studio 4.2。因此,您可以转到Android Studio 预览页面并下载最新的 Canary 版本,然后创建一个 Empty Compose Activity。
可组合函数
在 Jetpack Compose 中,可组合函数用于以编程方式定义应用程序的所有 UI。因此,您无需为应用程序的布局使用任何 XML 文件。制作可组合函数所需要做的就是使用@Composable函数名称的注释。可组合函数的基本语法是:
fun AnyUiComponent() { | |
// Code for UI element | |
} |
现在,您知道了可组合函数是什么以及如何使用@Composable注解创建可组合函数。让我们继续看Text的例子。
显示简单文本
在本章的这一部分中,我们将学习如何使用 compose 显示简单的文本。
要显示文本,我们使用 Text Composable 并在其中传递要显示的字符串。例如,
fun SimpleText(displayText: String) { | |
Text(text = displayText) | |
} |
在这里,SimpleText是一个可组合函数,在该函数内部,我们正在使用Text并传递displayText给它。
现在,您可以从活动方法的块中调用此SimpleText函数。setContentonCreate
class SimpleTextActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContent { | |
Column( | |
modifier = Modifier.fillMaxSize(), | |
verticalArrangement = Arrangement.Center, | |
horizontalAlignment = Alignment.CenterHorizontally, | |
) { | |
SimpleText(getString("I am learning Compose")) | |
} | |
} | |
} | |
} |
在这里,我们使用一个Column用于垂直显示某些内容的 Column,我们正在调用SimpleTextComposable 函数。
将样式应用于文本
我们可以将各种样式应用于文本,例如增加字体大小、更改颜色等。
因此,让我们创建一个名为 的函数SetTextStyling:
fun SetTextStyling(displayText: String, style: TextStyle? = null, maxLines: Int? = null) { | |
Text( | |
text = displayText, | |
modifier = Modifier.padding(.dp), | |
style = style ?: TextStyle.Default, | |
overflow = TextOverflow.Ellipsis, | |
maxLines = maxLines ?: Int.MAX_VALUE | |
) | |
} |
在上面的函数中,参数是displayText即要显示的文本,style即要放在Text 上的样式,maxLines即文本允许的最大行数。如果文本超过最大行,则将显示省略号(...)。
我们将通过传递这些参数来调用这个函数。让我们看看其中的一些:
- 设置字体大小:
style = TextStyle( | |
fontSize =.sp | |
) |
- 要设置字体粗细,请将 text-style 传递为:
fontWeight = FontWeight.Bold
同样,您可以更改字体大小、文本颜色、字体系列、下划线文本等。您可以从我们的开源项目中看到所有这些。
使用 TextField 进行输入
就像 EditText 一样,在 Compose 中我们可以使用TextField和BaseTextField。BaseTextField仍处于试验阶段,可以随时删除或永久添加。因此,要使用BaseTextField,您需要添加@ExperimentalFoundationApi注释。
下面是一个简单的例子BaseTextField:
fun SimpleTextFieldComponent() { | |
Surface(color = Color.LightGray, modifier = Modifier.padding(.dp)) { | |
var text by remember { mutableStateOf(TextFieldValue("Enter text here")) } | |
BaseTextField( | |
value = text, | |
modifier = Modifier.padding(.dp).fillMaxWidth(), | |
onValueChange = { | |
text = it | |
} | |
) | |
} | |
} |
在上面的函数中,我们有一个BaseTextFieldinside a Surface。我们有一个名为 的回调onValueChange。当 的输入发生一些变化时调用此回调,BaseTextField并且更新的文本将作为回调的参数出现。
这是一个例子BaseTextField。Material Design 还为 EditText 提供了一个 Composable,即TextField. 一个简单的实现TextField如下:
fun SimpleMaterialTextFieldComponent() { | |
var text by savedInstanceState { "" } | |
TextField( | |
value = text, | |
modifier = Modifier.padding(.dp).fillMaxWidth(), | |
onValueChange = { text = it }, | |
label = { Text("Label") } | |
) | |
} |
TextField的行为类似于BaseTextField。在这里TextField,你还有一件事,即label。标签是在TextField中没有文本时显示在 中的文本TextField。
我们可以通过向它传递各种参数来自定义这个 BaseTextField 和 TextField。例如,
- 仅显示数字键盘:
var text by remember { mutableStateOf(TextFieldValue("")) } | |
BaseTextField(value = text, | |
keyboardType = KeyboardType.Number, | |
onValueChange = { | |
text = it | |
} | |
) |
- 将密码作为输入:
keyboardType = KeyboardType.Password, | |
visualTransformation = PasswordVisualTransformation() |
- 在 TextField 中添加占位符(当 TextField 为空且有焦点时显示)
placeholder = { Text("MindOrks") }
同样,我们可以添加图标,在 TextFiled 中显示错误消息,设置 errorColor、backgroundColor、intractionState、activeColor、inactiveColor 等。您可以在我们的开源项目中找到这些。
您可以尝试这些并在 Android Studio 本身中查看输出。是的,你没听错。您可以在 Android Studion 本身中预览任何 UI 元素。让我们看看如何。
在 Android Studio 中预览
Android Studio 提供了一个很棒的功能,可以在工作室本身中预览您的 UI 组件,而且非常动态。因此,每当您想测试一些 UI 组件时,您都可以通过制作 Composable 函数并使用@Preview注解在 Android Studio 中简单地预览它。
以下是相同的示例:
// This is a Composable function to display a Text | |
fun SimpleText(displayText: String) { | |
Text(text = displayText) | |
} | |
fun SimpleTextPreview() { | |
SimpleText("Hi I am learning Compose") | |
} |
现在,在预览选项卡(Studio 的右侧)中,您可以看到上述 Composable 函数的预览。
您可以使用不同宽度和高度的任意数量的预览。如果您单击预览中的任何 UI 元素,Android Studio 会将您带到创建该 UI 的行。
此外,您可以使用参数将一些名称放入预览中name。要命名预览,您需要添加以下代码:
@Preview(name = "Named Preview")
默认情况下,预览的名称是函数的名称。
注意:您不能将任何参数传递给 Preview Composable 函数。
预览参数
在上一节中,我们学习了如何使用 Android Studio 的预览功能。现在,当我们制作任何 Android 应用程序时,在大多数情况下,数据都来自服务器,我们将这些数据填充到我们的应用程序中。因此,下一个问题是如何预览数据来自服务器的 UI,即,其数据现在不可用。对于这些情况,我们可以使用@PreviewParameter注解。
主要思想是制作一个虚拟数据并将该虚拟数据传递给预览可组合函数。由于您不能将任何参数传递给 Preview Composable Function,因此为了实现这一点,您需要使用@PreviewParamter注解来传递参数。
所以,让我们首先创建一个虚拟数据类。
创建一个名为的数据类Blog.kt并将以下代码添加到其中:
data class Blog( | |
val name: String, | |
val author: String | |
) |
现在,创建一个名为的类DummyBlogProvider,它实现了一个名为 的接口PreviewParameterProvider:
class DummyBlogProvider : PreviewParameterProvider<Blog> { | |
override val values = | |
sequenceOf(Blog("Learning Compose", "MindOrks"), Blog("Learning Android", "MindOrks")) | |
override val count: Int = values.count() | |
} |
现在,我们完成了虚拟数据,我们可以在预览中使用这个虚拟数据。以下是相同的示例:
@Preview | |
@Composable | |
fun BlogInfo(@PreviewParameter(DummyBlogProvider::class) blog: Blog) { | |
SimpleTextComponent("${blog.name} by ${blog.author}") | |
} |
您可以使用虚拟数据查看 UI 的预览。
Column
AColumn是一种可组合的布局,用于将其所有子级一个接一个地垂直放置。它类似于具有垂直方向的 LinearLayout。
以下是列的示例:
fun SimpleColumnComponent() { | |
Column(modifier = Modifier.padding(.dp)) { | |
Text(text = "Hello! I am Text", color = Color.Black) | |
Text(text = "Hello! I am Text", color = Color.Blue) | |
} | |
} |
Scrollable Column
当您使用 simpleColumn时,您只能使用手机屏幕的高度。但是,如果您的内容超出了屏幕范围,那么您可以使用ScrollableColumn. ScrollableColumn 就像 ScrollView。
以下是相同的示例:
fun ScrollableColumnComponent(blogList: List<Blog>) { | |
ScrollableColumn { | |
val context = ContextAmbient.current | |
Column { | |
for (blog in blogList) { | |
Card( | |
shape = RoundedCornerShape(.dp), | |
modifier = Modifier.fillMaxWidth().padding(.dp).clickable(onClick = { | |
Toast.makeText(context, "Author: ${blog.author}", Toast.LENGTH_SHORT).show() | |
}), | |
backgroundColor = Color(xFFFFA867.toInt()) | |
) { | |
Text( | |
blog.name, style = TextStyle( | |
fontSize =.sp, | |
textAlign = TextAlign.Center | |
), modifier = Modifier.padding(.dp) | |
) | |
} | |
} | |
} | |
} | |
} |
Lazy Column
ScrollableColumn在开始时加载其所有元素。例如,如果您有 50 个元素,并且在任何时间点屏幕只能显示 10 个元素,并且您需要滚动才能看到另一个元素,那么如果您使用的是 a ScrollableColumn,那么最初将加载所有元素。
但是,如果您使用的是LazyColumnFor,那么它将仅加载当前在移动屏幕上可见的那些元素。它的行为有点类似于RecyclerView.
以下是相同的示例:
fun LazyColumnScrollableComponent(blogList: List<Blog>) { | |
LazyColumnFor(items = blogList, modifier = Modifier.fillMaxHeight()) { blog -> | |
val context = ContextAmbient.current | |
Card( | |
shape = RoundedCornerShape(.dp), | |
modifier = Modifier.fillParentMaxWidth().padding(.dp).clickable(onClick = { | |
Toast.makeText(context, "Author: ${blog.author}", Toast.LENGTH_SHORT).show() | |
}), | |
backgroundColor = Color(xFFFFA867.toInt()) | |
) { | |
Text( | |
blog.name, style = TextStyle( | |
fontSize =.sp, | |
textAlign = TextAlign.Center | |
), modifier = Modifier.padding(.dp) | |
) | |
} | |
} | |
} |
现在,如果要水平显示内容,则可以使用 Row、ScrollableRow 或 Lazy Row。所有这些的工作方式分别类似于 Column、ScrollableColumn 和 Lazy Column。因此,为了使此博客提供更多信息,我们不包括该部分。不过,您可以从我们的开源项目中找到这些代码。
Box
Box 是一种可组合的布局,用于相对于其边缘放置子级。最初,Stack 被用来代替 Box。但是现在,不推荐使用 Stack 并引入了 Box。
顾名思义,孩子被放置在父母内部。框内的子元素按指定的顺序绘制,如果子元素小于父元素,则默认根据对齐方式将它们放置在框内。
以下是 Box 的示例:
fun SimpleBoxComponent() { | |
Box(modifier = Modifier.fillMaxSize().padding(.dp)) { | |
Image(imageResource(R.drawable.mindorks_cover)) | |
Text( | |
modifier = Modifier.padding(start =.dp, top = 16.dp), | |
text = "I am a text over Image", | |
fontSize =.sp, | |
color = Color.Red | |
) | |
} | |
} |
Button
Button用于在用户单击时执行某些操作。
以下是一个简单Button的示例:
fun SimpleButtonComponent() { | |
val context = ContextAmbient.current | |
Button( | |
onClick = { | |
Toast.makeText(context, "Thanks for clicking!", Toast.LENGTH_LONG).show() | |
}, | |
modifier = Modifier.padding(.dp).fillMaxWidth() | |
) { | |
Text("Click Me") | |
} | |
} |
这里,Text用于在按钮上放置一些文本,onClick回调用于监听按钮的点击事件。
通过将各种参数传递给 Button,您可以在很大程度上对其进行自定义。其中一些是:
- 制作圆角Button:
shape = RoundedCornerShape(.dp)
- 制作一个带边框的Button:
border = BorderStroke(width =.dp, brush = SolidColor(Color.Green))
类似地,您可以为按钮添加一些图标、为按钮应用颜色、禁用按钮、制作轮廓按钮、制作 IconButton、制作 FAB 等。您可以查看我们的开源项目以获取更多示例。
Card
Card 是一种可组合的布局,用于制作 CardView。
以下是相同的示例:
fun SimpleCardComponent() { | |
Card( | |
backgroundColor = Color(xFFFFA867.toInt()), | |
modifier = Modifier.padding(.dp).fillMaxWidth() | |
) { | |
Text( | |
text = "Simple Card", | |
textAlign = TextAlign.Center, | |
style = TextStyle( | |
fontSize =.sp | |
), | |
modifier = Modifier.padding(.dp) | |
) | |
} | |
} |
Clickable
您可以使用 Clickable 对用户做出可组合的反应。Clickable 支持单次点击、双击和长按。
以下是相同的示例:
fun SimpleTextComponent() { | |
val context = ContextAmbient.current | |
Text( | |
text = "Click Me", | |
textAlign = TextAlign.Center, | |
color = Color.Black, | |
modifier = Modifier.padding(.dp).fillMaxWidth().clickable(onClick = { | |
Toast.makeText(context, "Thanks for clicking! I am Text", Toast.LENGTH_SHORT).show() | |
}, onLongClick = { | |
Toast.makeText(context, "Thanks for LONG click! I am Text", Toast.LENGTH_SHORT).show() | |
}, onDoubleClick = { | |
Toast.makeText(context, "Thanks for DOUBLE click! I am Text", Toast.LENGTH_SHORT).show() | |
}) | |
) | |
} |
同样,您可以使卡片可点击。
Image
要显示图像,我们可以使用Image可组合的。
fun SimpleImageComponent() { | |
// Image is a composable that is used to display some image. | |
val image = imageResource(R.drawable.mindorks_cover) | |
Column( | |
modifier = Modifier.padding(.dp) | |
) { | |
Image(image) | |
} | |
} |
您还可以使用以下代码制作圆角图像:
Image( | |
image, | |
modifier = Modifier.fillMaxWidth().clip(shape = RoundedCornerShape(.dp)), | |
contentScale = ContentScale.Fit | |
) | |
Alert Dialog
顾名思义,AlertDialog 用于以对话框的形式向用户显示一些重要的消息(可能有一些操作)。
我们在 AlertDialog 中有标题、文本、确认按钮和关闭按钮。
fun AlertDialogComponent() { | |
val openDialog = remember { mutableStateOf(true) } | |
if (openDialog.value) { | |
AlertDialog( | |
onDismissRequest = { openDialog.value = false }, | |
title = { Text(text = "Alert Dialog") }, | |
text = { Text("Hello! I am an Alert Dialog") }, | |
confirmButton = { | |
TextButton( | |
onClick = { | |
openDialog.value = false | |
/* Do some other action */ | |
} | |
) { | |
Text("Confirm") | |
} | |
}, | |
dismissButton = { | |
TextButton( | |
onClick = { | |
openDialog.value = false | |
/* Do some other action */ | |
} | |
) { | |
Text("Dismiss") | |
} | |
}, | |
backgroundColor = Color.Black, | |
contentColor = Color.White | |
) | |
} | |
} |
Material AppBar
要在 Android 应用中显示 AppBar,您可以在应用中使用TopAppBar或BottomAppBar可组合。在这里,您可以拥有 AppBar 上的标题(通常是应用程序名称)、一些导航图标和一些操作。
fun TopAppBarComponent() { | |
TopAppBar( | |
modifier = Modifier.padding(.dp).fillMaxWidth(), | |
title = { Text("App Name") }, | |
navigationIcon = { | |
IconButton(onClick = { /* doSomething() */ }) { | |
Icon(Icons.Filled.Menu) | |
} | |
}, | |
actions = { | |
IconButton(onClick = { /* doSomething() */ }) { | |
Icon(Icons.Filled.Favorite) | |
} | |
IconButton(onClick = { /* doSomething() */ }) { | |
Icon(Icons.Filled.Favorite) | |
} | |
} | |
) | |
} |
同样,我们可以使用BottomAppBar也。
Material BottomNavigation
BottomNavigation 用于在屏幕底部显示应用程序的一些重要操作,以便用户轻松访问。要将项目添加到 a BottomNavigation,我们需要使用BottomNavigationItem可组合项。
fun BottomNavigationWithLabelComponent() { | |
var selectedItem by remember { mutableStateOf() } | |
val items = listOf("Home", "Blogs", "Profile") | |
BottomNavigation( | |
modifier = Modifier.padding(.dp).fillMaxWidth(), | |
backgroundColor = Color.Black, | |
contentColor = Color.Yellow | |
) { | |
items.forEachIndexed { index, item -> | |
BottomNavigationItem( | |
label = { Text(text = item) }, | |
icon = { Icon(Icons.Filled.Favorite) }, | |
selected = selectedItem == index, | |
onClick = { selectedItem = index } | |
) | |
} | |
} | |
} |
要使用不带标签的底部导航,您可以alwaysShowLabels = false在BottomNavigationItem.
Material Checkbox
当我们有 2 个选项并且用户可以选择或取消选择选项时,使用复选框。
fun SimpleCheckboxComponent() { | |
val checkedState = remember { mutableStateOf(true) } | |
Row { | |
Checkbox( | |
checked = checkedState.value, | |
modifier = Modifier.padding(.dp), | |
onCheckedChange = { checkedState.value = it }, | |
) | |
Text(text = "Checkbox Example", modifier = Modifier.padding(.dp)) | |
} | |
} |
onCheckedChangecallback 用于标识 Checkbox 中的变化。
Material ProgressBar
ProgressBar 用于显示应用程序中正在发生的一些进度。例如,从服务器下载进度或加载数据。
我们可以制作一个圆形进度条或线性进度条。
以下是循环进度条的示例:
fun SimpleCircularProgressComponent() { | |
CircularProgressIndicator( | |
modifier = Modifier.padding(.dp) | |
) | |
} |
您还可以使用 设置进度进度progress = 0.4f。
同样,您可以使用LinearProgressIndicator也。
Material Slider
Slider 用于从一系列值中选择一些值。例如,您可以通过音量滑块增加/减少音量,通过亮度滑块等来增加/减少亮度。
滑块可以是线性的,也可以有一些离散值,即您可以滑动以仅选择允许的值,例如仅选择整数值。
fun SimpleSliderComponent() { | |
var sliderValue by remember { mutableStateOf(.4f) } | |
Slider( | |
value = sliderValue, | |
modifier = Modifier.padding(.dp), | |
onValueChange = { newValue -> | |
sliderValue = newValue | |
} | |
) | |
Text( | |
text = "Slider value: $sliderValue", | |
modifier = Modifier.padding(.dp) | |
) | |
} |
同样,您可以通过将 steps 参数传递给 Slider 来制作阶梯滑块。
Material Snackbar
Snackbar 用于在屏幕底部显示一些信息,并放置在所有 UI 元素上。
fun SimpleSnackbarComponent() { | |
Snackbar( | |
modifier = Modifier.padding(.dp), | |
text = { | |
Text(text = "I'm a Simple Snackbar") | |
} | |
) | |
} |
您还可以使用以下方法向 Snackbar 添加一些操作:
action = { | |
Text(text = "OK", style = TextStyle(color = Color.Green)) | |
} |
Custom View
在 Compose 中,我们也可以制作一个 Canvas,在画布上,我们可以绘制各种形状,例如圆形、矩形、弧形等。
fun CustomViewComponent() { | |
Canvas(modifier = Modifier.fillMaxSize().padding(.dp)) { | |
drawRect( | |
color = Color.Red, | |
// topLeft is the coordinate of top-left point | |
topLeft = Offset(f, 0f), | |
size = Size(f, 400f) | |
) | |
drawArc( | |
Color.Gray, | |
startAngle =f, | |
sweepAngle =f, | |
useCenter = true, | |
size = Size(f, 600f), | |
topLeft = Offset(f, 300f) | |
) | |
} | |
} |
Crossfade动画
我们也可以在 Compose 中使用动画。例如,我们可以通过使用 Crossfade Composable 来使用 Crossfade 动画。
fun CrossFadeAnimation() { | |
val colors = listOf(Color.Red, Color.Green, Color.Blue, Color.Gray) | |
var current by remember { mutableStateOf(colors[]) } | |
Column(modifier = Modifier.fillMaxSize()) { | |
Crossfade(current = current) { color -> | |
Box(Modifier.fillMaxSize().clickable( | |
onClick = { | |
current = colors.random() | |
} | |
).background(color)) | |
Text( | |
modifier = Modifier.fillMaxSize(), | |
textAlign = TextAlign.Center, | |
text = "Click To See" | |
) | |
} | |
} | |
} |
在这里,Box 的颜色将随着单击 Box 时的 Crossfade 动画而改变。
这就是本教程的内容。您可以尝试使用 Jetpack Compose 的许多其他示例。