Android | Compose 初上手
theme: channing-cyan
簡介
Jetpack Compose
是用於構建原生 Andorid 介面的新工具包,Compose
使用了更少的程式碼,強大的工具和直觀的 Kotlin Api 簡化並且加快了 Android 上介面的開發。
在 Compose 中,在構建介面的時候,無需在像之前那麼構建 XML 佈局,只需要呼叫 Jetpack Compose 函式來宣告你想要的的元素,Compose 編譯器就會自動幫你完成後面的工作。
在開始使用 Compose 之前,你需要重新搭建環境,可參考官方文件
註解
- @Compose
所有的組合函式都必須新增 @Compose
註解才可以。
被 @Compose
註解的方法只能被同類型的方法呼叫。
- @Preview
使用該註解的方法可以不在執行 App 的情況下就可以檢視佈局。@Preview
中常用的引數如下:
-
name: String
: 為該Preview命名,該名字會在佈局預覽中顯示。 -
showBackground: Boolean
: 是否顯示背景,true為顯示。 -
backgroundColor: Long
: 設定背景的顏色。 -
showDecoration: Boolean
: 是否顯示Statusbar和Toolbar,true為顯示。 -
group: String
: 為該Preview設定group名字,可以在UI中以group為單位顯示。 -
fontScale: Float
: 可以在預覽中對字型放大,範圍是從0.01。 -
widthDp: Int
: 在Compose中渲染的最大寬度,單位為dp。 -
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
變得很容易。
引用邏輯為頂級可組合函式提供資料。該函式通過呼叫其他可組合函式來使用這些資料來描述介面。將適當的資料傳遞給這些可組合函式,並沿層次結構向下傳遞資料。
當用戶與介面互動時,介面發起 onClick
事件。這些事件會通知應用邏輯,應用邏輯可以改變應用狀態。當狀態發生變化時,系統就會重新呼叫可組合函式。這回導致重新繪製介面描述,此過程稱為重組。
動態內容
由於可組合函是 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
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
// 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
類似於 LinearLayout
,Column
是橫向的,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")
}
}
}
}
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()
。可選的有:
其中可以設定按鈕的背景色,未啟用的顏色等。
栗子:
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
一致,效果如下
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) ) }
```
像一些圓圖或者邊框啥的就可以在 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)
)
}
載入網路圖片
載入網路圖片需要藉助第三方庫 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
,SmallTopAppBar
,MediumTopAppBar
等。 - 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
@Composable
private fun BottomBar(mainBottomState: MutableState
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
以及網上的一些文章
如果本文對你有幫助,請點贊支援,謝謝!如果有任何問題,可直接在下方評論,謝謝!