Android | Compose 初上手

語言: CN / TW / HK

theme: channing-cyan

簡介

Jetpack Compose 是用於構建原生 Andorid 介面的新工具包,Compose 使用了更少的程式碼,強大的工具和直觀的 Kotlin Api 簡化並且加快了 Android 上介面的開發。

在 Compose 中,在構建介面的時候,無需在像之前那麼構建 XML 佈局,只需要呼叫 Jetpack Compose 函式來宣告你想要的的元素,Compose 編譯器就會自動幫你完成後面的工作。

在開始使用 Compose 之前,你需要重新搭建環境,可參考官方文件

註解

  • @Compose

所有的組合函式都必須新增 @Compose 註解才可以。

@Compose 註解的方法只能被同類型的方法呼叫。

  • @Preview

使用該註解的方法可以不在執行 App 的情況下就可以檢視佈局。@Preview 中常用的引數如下:

  1. name: String: 為該Preview命名,該名字會在佈局預覽中顯示。

  2. showBackground: Boolean: 是否顯示背景,true為顯示。

  3. backgroundColor: Long: 設定背景的顏色。

  4. showDecoration: Boolean: 是否顯示Statusbar和Toolbar,true為顯示。

  5. group: String: 為該Preview設定group名字,可以在UI中以group為單位顯示。

  6. fontScale: Float: 可以在預覽中對字型放大,範圍是從0.01。

  7. widthDp: Int: 在Compose中渲染的最大寬度,單位為dp。

  8. heightDp: Int: 在Compose中渲染的最大高度,單位為dp。

Compose 程式設計思想

Jetpack COmpose 是一個適用於 android 的新式宣告性介面工具包。Compose 提供了宣告性 API ,可以在不以命令的方式改變前端檢視的情況下呈現應用介面,從而使得編寫和維護介面變得更加容易。

申明性程式設計正規化

長期以來,android 的檢視結構一直可以表示為介面微件數。由於應用的狀態會因使用者互動等因素而發生變化,因此介面層次結構需要進行更新以顯示當前的資料,最常見的就是 findviewById 等函式遍歷樹,並呼叫設定資料的方法等改變節點,這些方法會改變微件的內部狀態

再過去的幾年中,整個行業已經轉向宣告性介面模型,該模型大大的簡化了構建和更新介面管理的工程設計,改技術的工作原理是在改建上重頭生成整個螢幕,然後執行必要的更改。此方法可以避免手動更新有狀態檢視結構的複雜性。Compose 是一個宣告性的介面框架。

重新生成整個螢幕所面臨的一個難題是,在時間,計算力和電量方面可能成本高昂,為了減輕這一成本,Compose 會智慧的選擇在任何時間需要重新繪製介面的那些部分。這回對設計介面的元件有一定影響。

組合函式

Jetpack Compose 是圍繞可組合函式構建的,這些函式就是要顯示在介面上的元素,在函式中只需要描述應用介面形狀和資料依賴關係,而不用去關係介面的構建過程,

如果需要建立組合函式,只需要將 @Composeable 註解新增到對於的函式上即可,需要注意的是組合函式的名稱一般都是以大寫字母開頭的,如下:

```kotlin class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PrimaryTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Android") } } } } }

@Composable fun Greeting(name: String) { Text(text = "Hello $name!", fontSize = 18.sp, color = Color.Red) }

@Preview(showBackground = true) @Composable fun DefaultPreview() { PrimaryTheme { Greeting("Android") } } ```

setContent 塊 定義了 Activity 的佈局,我們不需要去定義 XML 的佈局內容,只需要在其中呼叫組合函式即可。

上面的 一個簡單的示例Greeting 微件,它接收 String 而發出的一個顯示問候訊息的 Text 微件。此函式不會返回任何內容,因為他們描述所需的螢幕狀態,而不是構造介面微件。

其中 Greeting 就是一個非常簡單的可組合函式,裡面定義了一個 Text,顧名思義,就是用來顯示一段文字

並且,我們可以在 Test 函式上新增 @PreView 註釋,這樣就可以非常方便的進行預覽。

宣告式正規化轉變

Compose 的宣告方法中,微件相對無狀態,並且不提供 get,set 方法。實際上,微件微件不會以物件的形式提供。你可以通過呼叫帶有不同引數的統一可組合函式來更新介面。這使得架構模式,如 ViewModel 變得很容易。

引用邏輯為頂級可組合函式提供資料。該函式通過呼叫其他可組合函式來使用這些資料來描述介面。將適當的資料傳遞給這些可組合函式,並沿層次結構向下傳遞資料。

image-20220525170811867

當用戶與介面互動時,介面發起 onClick事件。這些事件會通知應用邏輯,應用邏輯可以改變應用狀態。當狀態發生變化時,系統就會重新呼叫可組合函式。這回導致重新繪製介面描述,此過程稱為重組

image-20220525170848197

動態內容

由於可組合函是 kotlin 編寫的,因此他們可以像任何 kotlin 程式碼一樣動態,例如,假設你想要的構建一個介面,如下:

kotlin @Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }

此函式接受一個列表,每位每個列表元素生成一個 Text。可組合函式可能性非常複雜,你可以使用 if 語句來確定是否需要顯示特定的介面元素。例如迴圈,輔助函式等。你擁有地城語言的靈活性,這種強大的功能和靈活性是 JetpackCompose 的主要優勢之一。

重組

Compose 中,你可以用新資料再次呼叫某個可組合函式,這回導致組合函式重新進行重組。系統會根據需要使用新資料重新繪製發出的微件。Compose 框架可以只能的重組已經更改的元件。

例如,下面這個可組合函式,用於顯示一個按鈕:

kotlin @Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }

每次點選按鈕,就會更新 clicks 的值,Compose 會再次呼叫 lambda 與 Text 函式以顯示新值,此過程稱為 重組。不依賴該值的其他元素不會重組。

重組是指在輸入更改的時候再次呼叫可組合函式的過程。當函式更改時,會發生這種情況。當 Compose 根據新輸入重組時,它僅呼叫可能已經更改的函式或 lambad,而跳過其餘函式或 lambda。通過跳過豈會為更改引數的函式或者 lambda ,Compose 可以高效的重組。

切勿依賴於執行可組合函式所產生的附帶效應,因為可能會跳過函式的重組,如果這樣做,使用者可能在應用中遇到奇怪且不可預測的行為。例如:

  • 寫入共享物件的屬性
  • 更新 viewmodel 中的可觀察項
  • 更新共享偏好設定

可組合函式可能會每一幀一樣的頻繁執行,例如呈現動畫的時候。所以可組合函式需要快速執行,所以避免在組合函式中出現卡頓,如果你需要執行高昂的操作,請在狗太協程中執行,並將結果作為引數傳遞給可組合函式。

例如下面程式碼,應該將 sp 讀取的操作放在 viewmode 中,然後在回撥中觸發更新:

kotlin @Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }

可組合函式可以按照任何順序執行

如果你看到了可組合函式的程式碼,可能會認為他們按照順序執行。但實際上未必是這樣。如果某個可組合函式包含對其他組合程式碼的呼叫,這些函式可以按照順序執行。

Compose 可以選擇識別出某些介面元素的優先順序高於其他介面元素,因此首先繪製這些元素。

假設你有如下程式碼:

kotlin @Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }

對於這三個的呼叫可以按照任何順序進行。這意味著你不能讓某個函式設定一個全域性變數(附帶效應),並讓別的函式利用這個全域性變數而發生更改。所以每個函式都應該獨立。

可組合函式可以並行執行

Compose 可以通過並行執行可組合函式來優化重組。這樣依賴,Compose 就可以利用多個核心,並按照較低的優先順序執行可組合函式(不在螢幕上)

這種優化方方式意味著可組合函式可能會在後臺的執行緒池中執行,如果某個可組合函式對 viewModel 呼叫一個函式,則 Compose 可能會同時從多個執行緒調動該函式。

為了確保應用可以正常執行,所有的組合都不應該有附帶效應,而應該通過始終在介面執行緒上執行的 onClick 等回撥觸發附帶效應。

呼叫某個可組合函式時,呼叫可能發生在與呼叫方不同的執行緒上。這意味著,應避免修改可組合函式 lambda 中的變數程式碼,基因為此類程式碼並非執行緒安全程式碼,又因為他是可組合 lambda 不允許的附帶效應。

下面展示了一個可組合函式,他顯示了一個列表已經數量。

kotlin @Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }

此函式沒有附帶效應,他會將輸出列表轉為介面。才程式碼非常適合展示小列表。不過此函式寫入區域性變數,則這並不是非執行緒安全或者正確的程式碼:

```kotlin @Composable @Deprecated("Example with bug") fun ListWithBug(myList: List) { var items = 0

Row(horizontalArrangement = Arrangement.SpaceBetween) {
    Column {
        for (item in myList) {
            Text("Item: $item")
            items++ // Avoid! Side-effect of the column recomposing.
        }
    }
    Text("Count: $items")
}

} ```

在上面例子中,每次重組都會修改 items。這可以在動畫的第一幀,或者在列表更新的時候。但不管怎麼樣,介面都會顯示出錯誤的數量。因此 Compose 不支援這樣的寫入操作。通過靜止此類操作,我們允許框架更改執行緒以執行可組合 lambda。

重組跳過儘可能多的內容

如果介面某些部分無需,Compose 會盡力只重組需要更新的部分。這意味著,他可以跳過某些內容以重新執行單個按鈕的可組合項,而不執行樹中其上面或下面的任何可組合項。

每個可組合函式和 lambda 都可以自行重組。以下演示了在呈現列表時重組如何跳過某些元素:

```kotlin /* * Display a list of names the user can click with a header / @Composable fun NamePicker( header: String, names: List, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.h5) Divider()

    // LazyColumn is the Compose version of a RecyclerView.
    // The lambda passed to items() is similar to a RecyclerView.ViewHolder.
    LazyColumn {
        items(names) { name ->
            // When an item's [name] updates, the adapter for that item
            // will recompose. This will not recompose when [header] changes
            NamePickerItem(name, onNameClicked)
        }
    }
}

}

/* * Display a single name the user can click. / @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) } ```

這些作用域中的每一個都可能是在重組期間執行唯一一個作用域。當 header 發生更改時,Compose 可能會跳至 Column lambda 。二部執行他的任何父項。此外,執行 Colum 時,如果 names 未更改,Compose 可能會旋轉跳過 LazyColum 的項。

同樣,執行所有組合函式或者 lambda 都應該沒有附帶效應。當需要執行附帶效應時,應該通過回撥觸發。

重組是樂觀操作

只要 Compose 任務某個可組合函式可能已經更改,就會開始重組。重組是樂觀操作,也就是說 Compose 預計會在引數再次更改之前完成重組。如果某個引數在重組完成之間發生改變,Compose 可能會取消重組,並使用新的引數重新開始。

取消重組後,Compose 會從重組中捨棄介面樹。如有附帶效應依賴於顯示的介面,即使取消了組成操作,也會應用該附帶效應。這可能導致應用狀態不一致。

確保每個可組合函式和 lambda 都冪等,且沒有附帶效應,以處理樂觀的重組

可組合函式可能會非常頻繁的執行

在某些情況下,可能針對介面每一幀執行一個可組合函式,如果該函式成本高昂,可能會導致介面卡頓。

例如,你的微件重試讀取裝置配置,或者讀取 sp,他可能會在一秒鐘內讀取這些資料上百次,這回對效能造成災難性的影響。

如果您的可組合函式需要資料,它應為相應的資料定義引數。然後,您可以阿靜成本高昂的工作移到其他執行緒,並使用 mutableStateOf 或者 LiveData 將相應的資料傳遞給 Compose。

主題

```kotlin //深色 val DarkColorScheme = darkColors( primary = Purple80, onPrimary = Color(0xFFFFFFFF), secondary = PurpleGrey80, )

//亮色 val LightColorScheme = lightColors( primary = Purple40, onPrimary = Color(0xFF333333), secondary = PurpleGrey40, )

@Composable fun PrimaryTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit ) {

MaterialTheme(
    colors = LightColorScheme,
    typography = Typography,
    content = content
)

} ```

預設的主題定義如上所示,最終會呼叫 MaterialTheme

Material 主題主要包含三個屬性,分別是 顏色,排版,和內容,Api 如下:

kotlin @Composable fun MaterialTheme(    colors: Colors = MaterialTheme.colors, // 顏色集合    typography: Typography = MaterialTheme.typography, // 排版集合    shapes: Shapes = MaterialTheme.shapes, // 形狀集合    content: @Composable () -> Unit // 要展示的內容 )

顏色

kotlin class Colors(    primary: Color, // 主顏色,螢幕和元素都用這個顏色    primaryVariant: Color, // 用於區分主顏色,比如app bar和system bar    secondary: Color, // 強調色,懸浮按鈕,單選/複選按鈕,高亮選中的文字,連結和標題    secondaryVariant: Color, // 用於區分強調色    background: Color, // 背景色,在可滾動項下面展示    surface: Color, // 表層色,展示在元件表層,比如卡片,清單和選單(CardView,SheetLayout,Menu)等    error: Color, // 錯誤色,展示錯誤資訊,比如TextField的提示資訊    onPrimary: Color, // 在主顏色primary之上的文字和圖示的顏色    onSecondary: Color, // 在強調色secondary之上的文字和圖示的顏色    onBackground: Color, // 在背景色background之上的文字和圖示的顏色    onSurface: Color, // 在表層色surface之上的文字和圖示的顏色    onError: Color, // 在錯誤色error之上的文字和圖示的顏色    isLight: Boolean // 是否是淺色模式 )

更多的可以檢視 lightColorScheme 函式。

排版

kotlin @Immutable class Typography internal constructor( val h1: TextStyle, val h2: TextStyle, val h3: TextStyle, val h4: TextStyle, val h5: TextStyle, val h6: TextStyle, val subtitle1: TextStyle, val subtitle2: TextStyle, val body1: TextStyle, val body2: TextStyle, val button: TextStyle, val caption: TextStyle, val overline: TextStyle )

形狀

```kotlin class Shapes( // 小元件使用的形狀,比如: Button,SnackBar,懸浮按鈕等    val small: CornerBasedShape = RoundedCornerShape(4.dp),

// 中元件使用的形狀,比如Card(就是CardView),AlertDialog等

val medium: CornerBasedShape = RoundedCornerShape(4.dp),

// 大元件使用的形狀,比如ModalDrawer或者ModalBottomSheetLayout(就是抽屜佈局和清單佈局)

val large: CornerBasedShape = RoundedCornerShape(0.dp), ) ```

使用

kotlin setContent { PrimaryTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.color.background ) { Greeting("Android") } } }

kotlin @Composable fun PrimaryTheme( themeType: ThemeType = themeTypeState.value, content: @Composable () -> Unit ) { val shapes = Shapes( small = RoundedCornerShape(4.dp), medium = RoundedCornerShape(8.dp), large = RoundedCornerShape(12.dp), ) MaterialTheme( colors = getThemeForTheme(themeType), typography = Typography, shapes = shapes, content = content ) }

另外,我寫了一個可動態切換的主題,有興趣的可以看一下

UI

SetContent

kotlin setContent { PrimaryTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Android") } } }

同 Android 中的 SetContentView。

Theme

建立專案之後,就會生成一個 專案名稱+Theme@Compose 方法,我們可以通過更改其中的顏色來完成對主題的修改。具體如上面的主題所示.

Modifier

Modifier 本質是一個介面,可以用來修飾各種佈局,例如 寬高,padding 等,常見的如下:

  • padding:有四個過載方法
  • plus:將其他的 Modifer 加入到當前的 Modifer 中。
  • fillMaxHeight,fillMaxWidth,fillmaxSize:類似於 match_parent,填充整個父 Layout
  • with,height,size :設定寬高度
  • rtl,ltr:開始佈局的方向
  • widthIn,heightIn,sizeIn 設定佈局的寬度和高度的最大值和最小值
  • gravity:元素的位置,
  • 等等

需要注意的是 Modifier 系列的方法都支援鏈式呼叫

Column,Row

類似於 LinearLayoutColumn 是橫向的,Row 是豎向的。有四個引數:

  • Modifer: 具體值如上述所示

  • verticalArrangement:子元素豎向的排列規則

常見的就是,上下左右中,比較特殊的就是

SpaceEvenly 均勻分配,

SpaceBetween 第一個元素前和最後一個元素後沒有空隙,其他的按比例放入。

SpaceAround 把整體中的一半空隙憑據放入第一個和最後一個的開始和結束,剩餘的一半等比放入各個元素。

  • horizontalAlignment:和上面一個,只不過方向不同

  • content:要顯示的內容

栗子:@Composable () -> Unit

dart setContent { PrimaryTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Column { Row { Button( onClick = { themeTypeState.value = ThemeType.RED_THEME }, modifier = Modifier.width(100.dp), colors = ButtonDefaults.buttonColors(containerColor = Color.Yellow) ) { Greeting(name = "按鈕1") } Button( onClick = { themeTypeState.value = ThemeType.GREEN_THEME }, modifier = Modifier.width(100.dp), colors = ButtonDefaults.elevatedButtonColors() ) { Greeting(name = "按鈕2") } } Greeting(name = "Hello Android") Greeting(name = "Hello 345") } } } }

螢幕截圖 2022-05-17 135439

Text

kotlin fun Text( text: String, //顯示內容 modifier: Modifier = Modifier, //修飾,可修改透明度,邊框,背景等 color: Color = Color.Unspecified, //文字顏色 fontSize: TextUnit = TextUnit.Unspecified,// size fontStyle: FontStyle? = null, //文字樣式,粗體,斜體等 fontWeight: FontWeight? = null,//文字厚度 fontFamily: FontFamily? = null,//字型 letterSpacing: TextUnit = TextUnit.Unspecified, //用於與文字相關的維度值的單位。該元件還在測試中 textDecoration: TextDecoration? = null,//文字裝飾,中劃線,下劃線 textAlign: TextAlign? = null,對齊方式 lineHeight: TextUnit = TextUnit.Unspecified,//行高 overflow: TextOverflow = TextOverflow.Clip,//如何處理溢位,預設裁切 softWrap: Boolean = true,//是否軟換行 maxLines: Int = Int.MAX_VALUE,//最大行數 onTextLayout: (TextLayoutResult) -> Unit = {},//計算佈局時回撥 style: TextStyle = LocalTextStyle.current //文字的樣式配置,如顏色、字型、行高等。 )

  • modifier:在此處用來修飾 Text,Modifer 提供了很多擴充套件,如透明度,背景,邊框等

示例:

kotlin @Composable fun Greeting(name: String) { Text( text = name, fontSize = 18.sp, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.primary, modifier = Modifier.height(30.dp) ) }

Button

kotlin fun Button( onClick: () -> Unit,//點選時呼叫 modifier: Modifier = Modifier,//同上 enabled: Boolean = true,//是否啟用 elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),// z軸上的高度 shape: Shape = FilledButtonTokens.ContainerShape.toShape(), border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit )

  • shape

調整 button 的樣式,例如 RoundedCornerShape 是圓角矩形的樣式,CircleShape 是圓形的樣式,CutCornerShape 是切角樣式

  • border

外邊框,預設是 null,Border 有兩種使用方式,1 Border(size: Dp, color: Color),2 Border(size: Dp, brush: Brush)

第二種需要自己建立一個筆刷,去繪製外邊框,例如要實現漸變的外邊框。

  • colors

按鈕的顏色,預設是 ButtonDefaults.buttonColors() 。可選的有:

image-20220517151926468

​ 其中可以設定按鈕的背景色,未啟用的顏色等。

栗子:

kotlin Button( onClick = { themeTypeState.value = ThemeType.GREEN_THEME }, modifier = Modifier.width(100.dp), colors = ButtonDefaults.buttonColors( containerColor = Color.Yellow, contentColor = Color.Red, disabledContainerColor = Color.Black, disabledContentColor = Color.Green ) ) { Greeting(name = "按鈕2") }

OutLinedButton

具有外邊框的按鈕,內部使用的也是 Button。預設會有一個邊框,其引數和 Button 一致,效果如下

image-20220517163336439

TextButton

預設的 button 在有主題的時候,預設背景是主題顏色,而 textButton 背景預設是透明的。TextButton 預設使用的顏色是 ButtonDefaults.textButtonColors()

Image

kotlin @Composable fun Image( painter: Painter, bitmap: ImageBitmap, // contentDescription: String?, modifier: Modifier = Modifier, alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null )

  • painter:圖片資源,使用 PainterResource 來完成。
  • contentDescription:無障礙提示文字資訊
  • contentScale :類似於 ImageView 中的 scaleType 屬性。
  • colorFilter:將某種顏色應用到圖片上
  • alpha:不透明度
示例

```dart @Composable @Preview fun Image() { Image( painter = painterResource(id = R.drawable.one), contentDescription = "無障礙提示", contentScale = ContentScale.Crop, modifier = Modifier .width(100.dp) .height(100.dp) ) }

```

image-20220517172753037

像一些圓圖或者邊框啥的就可以在 modifer 中直接設定了,如下:

kotlin @Composable @Preview fun Image() { Image( painter = painterResource(id = R.drawable.one), contentDescription = "無障礙提示", contentScale = ContentScale.Crop, modifier = Modifier .width(100.dp) .height(100.dp) .clip(shape = CircleShape) .border(2.dp, color = Color.Red, shape = CircleShape) ) }

image-20220517173235028

載入網路圖片

載入網路圖片需要藉助第三方庫 coil,使用方式如下:

tex //圖片載入庫 implementation("io.coil-kt:coil:2.0.0") implementation("io.coil-kt:coil-compose:2.0.0")

kotlin @Composable @Preview fun Image() { AsyncImage( model = "http://img0.baidu.com/it/u=3147375221,1813079756&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=836", contentDescription = "無障礙提示", contentScale = ContentScale.Crop, modifier = Modifier .width(100.dp) .height(100.dp) .clip(shape = CircleShape) .border(2.dp, color = Color.Red, shape = CircleShape) ) }

Spacer

和原生的一樣,需要空白區域時可以使用 Spacer ,使用方式如下:

kotlin Spacer(modifier = Modifier.height(100.dp))

Surface

對內容進行裝飾,例如設定背景,shape 等

kotlin fun Surface( modifier: Modifier = Modifier, shape: Shape = Shapes.None, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, content: @Composable () -> Unit )

  • color :設定 Surface 的背景色,預設是主題中的 surface 顏色。
  • contentColor:此 Surface 為其子級提供的首選內容顏色。預設為 [color] 的匹配內容顏色,或者如果 [color] 不是來自主題的顏色,這將保持在此 Surface 上方設定的相同值。
  • tonalElevation:當 [color] 為 [ColorScheme.surface] 時,高程越高,淺色主題顏色越深,深色主題顏色越淺。
  • shadowElevation:陰影大小

Scaffold

腳手架的意思,和 Flutter 中的 Scaffold 是一樣的,通過 Scaffold 我看可以快速的對頁面進行佈局,例如設定導航欄,側滑欄,底部導航等等。

kotlin fun Scaffold( modifier: Modifier = Modifier, topBar: @Composable () -> Unit = {}, bottomBar: @Composable () -> Unit = {}, snackbarHost: @Composable () -> Unit = {}, floatingActionButton: @Composable () -> Unit = {}, floatingActionButtonPosition: FabPosition = FabPosition.End, containerColor: Color = MaterialTheme.colorScheme.background, contentColor: Color = contentColorFor(containerColor), content: @Composable (PaddingValues) -> Unit )

  • topBar:Toolbar,常用的有 CenterAlignedTopAppBar,SmallTopAppBarMediumTopAppBar 等。
  • bootomBar:底部導航欄
  • snackbarHost:
  • floatingActionButton:按鈕
  • floatingActionButtonPosition:按鈕位置
  • containerColor:背景顏色
  • contentColor:內容首選顏色
看一個栗子:

kotlin Scaffold( topBar = { //..... }, bottomBar = bottomBar, ) { Box( modifier = Modifier .fillMaxSize() .padding(top = it.calculateTopPadding(), bottom = it.calculateBottomPadding()) ) { content.invoke(it) } }

需要注意的是,如果使用了 toolbar 或者 bootomBar,就會把 content 中的內容擋住,這個時候就需要使用 PaddingValue 設定內邊距了。

還有一點須要注意,如果要使用沉浸式狀態列,就需要自定義 topBar 了,要不然狀態列會被 topBar 覆蓋。下面程式碼是設定沉浸式狀態列的。

kotlin ///系統 UI 控制器 implementation "com.google.accompanist:accompanist-systemuicontroller:0.24.8-beta" //正確獲取狀態列高度 api "com.google.accompanist:accompanist-insets-ui:0.24.8-beta"

```dart override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { SetImmersion() PrimaryTheme { SetContent() } } }

@Composable private fun SetImmersion() { if (isImmersion()) { val systemUiController = rememberSystemUiController() SideEffect { systemUiController.run { setSystemBarsColor(color = Color.Transparent, darkIcons = isDark()) setNavigationBarColor(color = Color.Black) } } } } ```

底部導航欄

```kotlin @Composable fun MainCompose(navController: NavHostController, mainBottomState: MutableState) { SetScaffold( bottomBar = { BottomBar(mainBottomState) } ) { when (mainBottomState.value) { 0 -> HomeCompos(navController) 1 -> ProjectCompos() 2 -> FLCompos() else -> UserCompos() } } }

@Composable private fun BottomBar(mainBottomState: MutableState) { BottomNavigation( backgroundColor = MaterialTheme.colors.background, ) {

    navigationItems.forEachIndexed { index, navigationItem ->
        BottomNavigationItem(
            selected = mainBottomState.value == index,
            onClick = {
                mainBottomState.value = index
            },
            icon = {
                Icon(
                    imageVector = navigationItem.icon,
                    contentDescription = navigationItem.name
                )
            },
            label = {
                BottomText(
                    isSelect = mainBottomState.value == index,
                    name = navigationItem.name
                )
            },
            selectedContentColor = Color.White,
            unselectedContentColor = Color.Black
        )
    }
}

}

@Composable fun BottomText(isSelect: Boolean, name: String) { if (isSelect) { Text( text = name, color = MaterialTheme.colors.primary, fontSize = 12.sp ) } else { Text( text = name, color = Color.Black, fontSize = 12.sp ) } } ```

如果看的不是特別清楚,可以直接點這裡看

最後

到這裡,這篇文章也完了。這篇文章主要講了一下 Compose 中最基本的一些 核心思想以及 UI 函式以及主題啥的。這也是我最開始接觸到 Compose 學到的東西,所以這也算是我的學習筆記吧。

參考資料

http://developer.android.google.cn/jetpack/compose/documentation

以及網上的一些文章

如果本文對你有幫助,請點贊支援,謝謝!如果有任何問題,可直接在下方評論,謝謝!