Jetpack Compose

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ù)覽。


PreviewMessageCard.png

這段代碼會在內(nèi)容視圖中創(chuàng)建兩個(gè)文本元素。不過,由于您未提供有關(guān)如何排列這兩個(gè)文本元素的信息,因此它們會相互重疊,使文本無法閱讀。


composableMessageCard.png

使用 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)
    }
}

}


layout.png

第 3 課:Material Design
Compose 旨在支持 Material Design 原則。它的許多界面元素都原生支持 Material Design。在本課中,您將使用 Material Design widget 來設(shè)置應(yīng)用的樣式。


MaterialDesign.png

使用 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
       )
   }

}
}

color.png

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
       )
   }

}
}

typography.png

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!")
)
}
}
}


shape.png

啟用深色主題

您可以啟用深色主題(或夜間模式),以避免顯示屏過亮(尤其是在夜間),或者只是節(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!")
)
}
}
}


EnableDarkTheme.png

淺色和深色主題的顏色選項(xiàng)是在由 IDE 生成的 Theme.kt 文件中定義的。

目前為止,您已創(chuàng)建了一個(gè)消息界面元素,它會以不同樣式顯示一張圖片和兩項(xiàng)文本,并且在淺色和深色主題下都有良好的視覺效果!

theme.png

第 4 課:列表和動畫

列表和動畫在應(yīng)用內(nèi)隨處可見。在本課中,您將學(xué)習(xí)如何利用 Compose 輕松創(chuàng)建列表并添加有趣的動畫效果。


listAndAnimate.png

創(chuàng)建消息列表

只包含一條消息的聊天會略顯孤單,因此請更改對話,使其包含多條消息。您需要創(chuàng)建一個(gè)可顯示多條消息的 Conversation 函數(shù)。對于此用例,請使用 Compose 的 LazyColumnLazyRow。這些可組合項(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)
}
}

列表.png

在展開消息時(shí)顯示動畫效果

對話變得更加有趣了。是時(shí)候添加動畫效果了!您將添加展開消息以顯示更多內(nèi)容的功能,同時(shí)為內(nèi)容大小和背景顏色添加動畫效果。為了存儲此本地界面狀態(tài),您需要跟蹤消息是否已展開。為了跟蹤這種狀態(tài)變化,您必須使用 remembermutableStateOf 函數(shù)。

可組合函數(shù)可以使用 remember 將本地狀態(tài)存儲在內(nèi)存中,并跟蹤傳遞給 mutableStateOf 的值的變化。該值更新時(shí),系統(tǒng)會自動重新繪制使用此狀態(tài)的可組合項(xiàng)(及其子項(xiàng))。這稱為重組

通過使用 Compose 的狀態(tài) API(如 remembermutableStateOf),系統(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
            )
        }
    }
}

}

動畫.png

注意:您需要添加以下導(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 修飾符順暢地為消息容器大小添加動畫效果:


狀態(tài).png

后續(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í)顯示動畫效果
如果您想深入了解其中的一些步驟,請瀏覽以下資源。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容