Jetpack Compose 是用于構(gòu)建原生 Android 界面的新工具包。它使用更少的代碼、強(qiáng)大的工具和直觀的 Kotlin API,可以幫助您簡化并加快 Android 界面開發(fā)。
在本教程中,您將使用聲明性的函數(shù)構(gòu)建一個(gè)簡單的界面組件。您無需修改任何 XML 布局,也不需要使用布局編輯器。相反,您只需調(diào)用可組合函數(shù)來定義所需的元素,Compose 編譯器即會完成后面的所有工作。
第 1 課:可組合函數(shù)
Jetpack Compose 是圍繞可組合函數(shù)構(gòu)建的。這些函數(shù)可讓您以程序化方式定義應(yīng)用的界面,只需描述應(yīng)用界面的外觀并提供數(shù)據(jù)依賴項(xiàng),而不必關(guān)注界面的構(gòu)建過程(初始化元素、將其附加到父項(xiàng)等)。如需創(chuàng)建可組合函數(shù),只需將 @Composable 注解添加到函數(shù)名稱中即可。
定義可組合函數(shù)
如需使函數(shù)成為可組合函數(shù),請?zhí)砑?@Composable 注解。如需嘗試此操作,請定義一個(gè) MessageCard 函數(shù)并向其傳遞一個(gè)名稱,然后該函數(shù)就會使用該名稱配置文本元素。
在 Android Studio 中預(yù)覽函數(shù)
借助 @Preview 注解,您可以在 Android Studio 中預(yù)覽可組合函數(shù),而無需構(gòu)建應(yīng)用并將其安裝到 Android 設(shè)備或模擬器中。該注解必須用于不接受參數(shù)的可組合函數(shù)。因此,您無法直接預(yù)覽 MessageCard 函數(shù),而是需要創(chuàng)建另一個(gè)名為 PreviewMessageCard 的函數(shù),由該函數(shù)使用適當(dāng)?shù)膮?shù)調(diào)用 MessageCard。請?jiān)?@Composable 上方添加 @Preview 注解。
重新構(gòu)建您的項(xiàng)目。由于新的 PreviewMessageCard 函數(shù)未在任何位置受到調(diào)用,因此應(yīng)用本身不會更改,但 Android Studio 會添加一個(gè)預(yù)覽窗口,您可以點(diǎn)擊拆分(設(shè)計(jì)/代碼)視圖以展開此窗口。此窗口會顯示由標(biāo)有 @Preview 注解的可組合函數(shù)創(chuàng)建的界面元素的預(yù)覽。任何時(shí)候,如需更新預(yù)覽,請點(diǎn)擊預(yù)覽窗口頂部的刷新按鈕。
第 2 課:布局
界面元素采用多層次結(jié)構(gòu),元素中又包含其他元素。在 Compose 中,您可以通過從可組合函數(shù)中調(diào)用其他可組合函數(shù)來構(gòu)建界面層次結(jié)構(gòu)。
添加多個(gè)文本
到目前為止,您已經(jīng)構(gòu)建了第一個(gè)可組合函數(shù)和預(yù)覽!為探索更多 Jetpack Compose 功能,您將構(gòu)建一個(gè)簡單的消息屏幕,屏幕上顯示可以展開且具有動畫效果的消息列表。
首先,通過顯示消息發(fā)送者和消息內(nèi)容,使消息可組合項(xiàng)更豐富。您需要先將可組合參數(shù)更改為接受 Message 對象(而非 String),然后在 MessageCard 可組合項(xiàng)中添加另一個(gè) Text 可組合項(xiàng)。請務(wù)必同時(shí)更新預(yù)覽。
這段代碼會在內(nèi)容視圖中創(chuàng)建兩個(gè)文本元素。不過,由于您未提供有關(guān)如何排列這兩個(gè)文本元素的信息,因此它們會相互重疊,使文本無法閱讀。
使用 Column
Column函數(shù)可讓您垂直排列元素。向MessageCard函數(shù)中添加一個(gè)Column。您可以使用Row水平排列各項(xiàng),并使用Box堆疊元素
添加圖片元素
我們添加消息發(fā)送者的個(gè)人資料照片,以豐富消息卡片。使用Resource Manager 從照片庫中導(dǎo)入圖片,或使用這張照片。添加一個(gè)Row可組合項(xiàng),以實(shí)現(xiàn)良好的設(shè)計(jì)結(jié)構(gòu),并向該可組合項(xiàng)中添加一個(gè)Image 可組合項(xiàng)
[圖片上傳失敗...(image-d4b0fa-1661744180884)]
load_images/19941755-3728e8d19107ea06.png?
配置布局
您的消息布局擁有良好的結(jié)構(gòu),但其元素的間距不合理,并且圖片過大!為了裝飾或配置可組合項(xiàng),Compose 使用了修飾符。通過修飾符,您可以更改可組合項(xiàng)的大小、布局、外觀,還可以添加高級互動,例如使元素可點(diǎn)擊。您可以將這些修飾符鏈接起來,以創(chuàng)建更豐富的可組合項(xiàng)。您將使用其中一些修飾符來改進(jìn)布局。
// ...
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@Composable
fun MessageCard(msg: Message) {
// Add padding around our message
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(R.drawable.profile_picture),
contentDescription = "Contact profile picture",
modifier = Modifier
// Set image size to 40 dp
.size(40.dp)
// Clip image to be shaped as a circle
.clip(CircleShape)
)
// Add a horizontal space between the image and the column
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(text = msg.author)
// Add a vertical space between the author and message texts
Spacer(modifier = Modifier.height(4.dp))
Text(text = msg.body)
}
}
}
第 3 課:Material Design
Compose 旨在支持 Material Design 原則。它的許多界面元素都原生支持 Material Design。在本課中,您將使用 Material Design widget 來設(shè)置應(yīng)用的樣式。
使用 Material Design
您的消息設(shè)計(jì)現(xiàn)在已有布局,但看上去還不是特別理想。
Jetpack Compose 原生提供 Material Design 及其界面元素的實(shí)現(xiàn)。您將使用 Material Design 樣式改進(jìn)MessageCard可組合項(xiàng)的外觀
首先使用在您的項(xiàng)目中創(chuàng)建的 Material 主題ComposeTutorialTheme和Surface來封裝MessageCard函數(shù)。在@Preview和setContent函數(shù)中都需要執(zhí)行此操作,這樣一來,可組合項(xiàng)即可沿用應(yīng)用主題中定義的樣式,從而在整個(gè)應(yīng)用中確保一致性。
Material Design 是圍繞Color、Typography、Shape這三大要素構(gòu)建的,您將逐一添加這些要素。
// ...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTutorialTheme {
Surface(modifier = Modifier.fillMaxSize()) {
MessageCard(Message("Android", "Jetpack Compose"))
}
}
}
}
}
@Preview
@Composable
fun PreviewMessageCard() {
ComposeTutorialTheme {
Surface {
MessageCard(
msg = Message("Colleague", "Take a look at Jetpack Compose, it's great!")
)
}
}
}
注意:Empty Compose Activity 模板會為您的項(xiàng)目生成默認(rèn)主題,使您能夠自定義 MaterialTheme
。 如果您為項(xiàng)目指定的名稱不是 ComposeTutorial,可以在 ui.theme
子軟件包的 Theme.kt
文件中找到您的自定義主題。
Color(顏色)
通過 MaterialTheme.colors,使用已封裝主題中的顏色來設(shè)置樣式。您可以在需要顏色的任意位置使用主題中的這些值。
設(shè)置標(biāo)題樣式,并為圖片添加邊框:
// ...
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(R.drawable.profile_picture),
contentDescription = null,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = msg.body,
style = MaterialTheme.typography.body2
)
}
}
}
Typography(排版)
MaterialTheme 中提供了 Material Typography 樣式,只需將其添加到 Text 可組合項(xiàng)中即可。
// ...
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(R.drawable.profile_picture),
contentDescription = null,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
Column {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = msg.body,
style = MaterialTheme.typography.body2
)
}
}
}
Shape(形狀)
通過 Shape
,我們可以添加最后的“點(diǎn)睛之筆”。首先,將消息正文封裝在 Surface
可組合項(xiàng)中。這樣即可自定義消息正文的形狀和高度。此外,還要為消息添加內(nèi)邊距,以改進(jìn)布局。
// ...
import android.content.res.Configuration
@Preview(name = "Light Mode")
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
ComposeTutorialTheme {
Surface {
MessageCard(
msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
)
}
}
}
啟用深色主題
您可以啟用深色主題(或夜間模式),以避免顯示屏過亮(尤其是在夜間),或者只是節(jié)省設(shè)備電量。由于支持 Material Design,Jetpack Compose 默認(rèn)能夠處理深色主題。使用 Material Design 顏色、文本和背景時(shí),系統(tǒng)會自動適應(yīng)深色背景。
您可以在文件中以單獨(dú)函數(shù)的形式創(chuàng)建多個(gè)預(yù)覽,也可以向同一個(gè)函數(shù)中添加多個(gè)注解。
添加新的預(yù)覽注解并啟用夜間模式
// ...
import android.content.res.Configuration
@Preview(name = "Light Mode")
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
name = "Dark Mode"
)
@Composable
fun PreviewMessageCard() {
ComposeTutorialTheme {
Surface {
MessageCard(
msg = Message("Colleague", "Hey, take a look at Jetpack Compose, it's great!")
)
}
}
}
淺色和深色主題的顏色選項(xiàng)是在由 IDE 生成的 Theme.kt 文件中定義的。
目前為止,您已創(chuàng)建了一個(gè)消息界面元素,它會以不同樣式顯示一張圖片和兩項(xiàng)文本,并且在淺色和深色主題下都有良好的視覺效果!
第 4 課:列表和動畫
列表和動畫在應(yīng)用內(nèi)隨處可見。在本課中,您將學(xué)習(xí)如何利用 Compose 輕松創(chuàng)建列表并添加有趣的動畫效果。
創(chuàng)建消息列表
只包含一條消息的聊天會略顯孤單,因此請更改對話,使其包含多條消息。您需要創(chuàng)建一個(gè)可顯示多條消息的 Conversation
函數(shù)。對于此用例,請使用 Compose 的 LazyColumn
和 LazyRow
。這些可組合項(xiàng)只會呈現(xiàn)屏幕上顯示的元素,因此,對于較長的列表,使用它們會非常高效。
在此代碼段中,您可以看到 LazyColumn
包含一個(gè) items
子項(xiàng)。它接受 List
作為參數(shù),并且其 lambda 會收到我們命名為 message
的參數(shù)(可以隨意為其命名),該參數(shù)是 Message
的實(shí)例。簡而言之,系統(tǒng)會針對提供的 List
的每個(gè)項(xiàng)調(diào)用此 lambda。將此示例數(shù)據(jù)集導(dǎo)入您的項(xiàng)目,以便快速引導(dǎo)對話。
// ...
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages) { message ->
MessageCard(message)
}
}
}
@Preview
@Composable
fun PreviewConversation() {
ComposeTutorialTheme {
Conversation(SampleData.conversationSample)
}
}
在展開消息時(shí)顯示動畫效果
對話變得更加有趣了。是時(shí)候添加動畫效果了!您將添加展開消息以顯示更多內(nèi)容的功能,同時(shí)為內(nèi)容大小和背景顏色添加動畫效果。為了存儲此本地界面狀態(tài),您需要跟蹤消息是否已展開。為了跟蹤這種狀態(tài)變化,您必須使用 remember
和 mutableStateOf
函數(shù)。
可組合函數(shù)可以使用 remember
將本地狀態(tài)存儲在內(nèi)存中,并跟蹤傳遞給 mutableStateOf
的值的變化。該值更新時(shí),系統(tǒng)會自動重新繪制使用此狀態(tài)的可組合項(xiàng)(及其子項(xiàng))。這稱為重組。
通過使用 Compose 的狀態(tài) API(如 remember
和 mutableStateOf
),系統(tǒng)會在狀態(tài)發(fā)生任何變化時(shí)自動更新界面。
// ...
import androidx.compose.foundation.clickable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTutorialTheme {
Conversation(SampleData.conversationSample)
}
}
}
}
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(R.drawable.profile_picture),
contentDescription = null,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colors.secondaryVariant, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
// We keep track if the message is expanded or not in this
// variable
var isExpanded by remember { mutableStateOf(false) }
// We toggle the isExpanded variable when we click on this Column
Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
Text(
text = msg.author,
color = MaterialTheme.colors.secondaryVariant,
style = MaterialTheme.typography.subtitle2
)
Spacer(modifier = Modifier.height(4.dp))
Surface(
shape = MaterialTheme.shapes.medium,
elevation = 1.dp,
) {
Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
// If the message is expanded, we display all its content
// otherwise we only display the first line
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.body2
)
}
}
}
}
注意:您需要添加以下導(dǎo)入內(nèi)容才能正確使用 by。按 Alt+Enter 鍵或 Option+Enter 鍵即可添加這些內(nèi)容。
import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue
現(xiàn)在,您可以根據(jù)點(diǎn)擊消息時(shí)消息的 isExpanded 狀態(tài),更改消息內(nèi)容的背景顏色。您將使用 clickable 修飾符來處理可組合項(xiàng)上的點(diǎn)擊事件。您會為背景顏色添加動畫效果,使其值逐步從 MaterialTheme.colors.surface 更改為 MaterialTheme.colors.primary(反之亦然),而不只是切換 Surface 的背景顏色。為此,您將使用 animateColorAsState 函數(shù)。最后,您將使用 animateContentSize 修飾符順暢地為消息容器大小添加動畫效果:
后續(xù)步驟
使用 Material Design 原則設(shè)計(jì),添加了深色主題,具有預(yù)覽功能,所有內(nèi)容只需不到 100 行代碼!
以下是您目前為止所學(xué)的內(nèi)容:
定義可組合函數(shù)
在可組合項(xiàng)中添加不同的元素
使用布局可組合項(xiàng)構(gòu)建界面組件
使用修飾符擴(kuò)展可組合項(xiàng)
創(chuàng)建高效列表
跟蹤狀態(tài)以及修改狀態(tài)
在可組合項(xiàng)上添加用戶互動
在展開消息時(shí)顯示動畫效果
如果您想深入了解其中的一些步驟,請瀏覽以下資源。