Android Compose 基礎布局之 Box 和 Stack 源碼深度剖析(九)

Android Compose 基礎布局之 Box 和 Stack 源碼深度剖析

一、引言

1.1 Android 開發中布局的重要性

在 Android 應用開發里,布局是構建用戶界面(UI)的關鍵環節。良好的布局設計能夠提升用戶體驗,使應用界面更加美觀、易用且具有一致性。早期的 Android 開發使用 XML 進行布局,這種方式雖然直觀,但在處理復雜布局和動態變化時顯得繁瑣,代碼的可讀性和可維護性也較差。

1.2 Jetpack Compose 的出現及意義

Jetpack Compose 是 Google 推出的新一代聲明式 UI 框架,旨在簡化 Android UI 開發。它采用 Kotlin 語言編寫,以聲明式的方式描述 UI,使得代碼更加簡潔、易于理解和維護。Compose 的布局系統提供了一系列基礎布局組件,如?Box?和?StackStack?在較新版本中被?Box?替代),為開發者提供了強大而靈活的布局能力。

1.3 本文的目標和結構

本文將深入分析 Android Compose 框架中?Box?和?Stack?這兩個基礎布局組件。從源碼級別剖析它們的實現原理、工作機制以及使用場景。首先會介紹 Compose 布局系統的基礎知識,然后詳細分析?Box?和?Stack?的源碼,接著探討它們的高級用法、性能優化以及注意事項,最后進行總結和展望。

二、Compose 布局系統基礎

2.1 Compose 可組合函數(Composable Functions)

2.1.1 可組合函數的定義和特點

可組合函數是 Compose 的核心概念之一。在 Compose 中,UI 是通過可組合函數來描述的。可組合函數使用?@Composable?注解標記,它可以接收參數,并且可以調用其他可組合函數。與傳統的命令式 UI 編程不同,Compose 的可組合函數是聲明式的,即描述 UI 應該是什么樣子,而不是如何創建它。

以下是一個簡單的可組合函數示例:

kotlin

@Composable
fun Greeting(name: String) {Text(text = "Hello, $name!")
}

在這個示例中,Greeting?是一個可組合函數,它接收一個?name?參數,并顯示一個包含問候語的?Text?組件。

2.1.2 可組合函數的執行流程

當調用一個可組合函數時,Compose 會根據函數的描述來構建 UI。在構建過程中,Compose 會跟蹤函數的輸入參數和狀態變化。如果參數或狀態發生變化,Compose 會自動重新執行可組合函數,更新 UI 以反映這些變化。這種機制使得 UI 能夠自動響應數據的變化,實現了數據和 UI 的綁定。

2.1.3 可組合函數的嵌套和組合

可組合函數可以嵌套和組合,以構建復雜的 UI。例如,我們可以創建一個包含多個?Greeting?組件的可組合函數:

kotlin

@Composable
fun GreetingList(names: List<String>) {Column {for (name in names) {Greeting(name = name)}}
}

在這個示例中,GreetingList?函數使用?Column?布局組件將多個?Greeting?組件垂直排列。

2.2 Compose 布局系統的測量和布局階段

2.2.1 測量階段(Measure Phase)

在 Compose 布局系統中,測量階段是確定每個組件大小的過程。每個布局組件都會接收父布局傳遞的約束條件,這些約束條件規定了組件的最小和最大寬度、高度。組件會根據這些約束條件和自身的內容來計算出合適的大小。

例如,一個?Text?組件會根據文本內容的長度和字體大小來確定自身的寬度和高度。在測量過程中,組件可以選擇忽略部分約束條件,但通常會盡量滿足這些條件。

2.2.2 布局階段(Layout Phase)

布局階段是確定每個組件位置的過程。在測量階段完成后,每個組件都有了自己的大小。布局組件會根據這些大小和自身的布局規則,確定每個子組件的位置。

例如,Column?布局會將子組件垂直排列,每個子組件的位置取決于其前面子組件的大小和布局規則。布局組件會調用子組件的?place?方法,將子組件放置到指定的位置。

2.2.3 測量和布局階段的源碼分析

在 Compose 中,測量和布局階段主要由?Layout?可組合函數處理。以下是一個簡化的?Layout?可組合函數示例:

kotlin

@Composable
fun CustomLayout(modifier: Modifier = Modifier,content: @Composable () -> Unit
) {Layout(modifier = modifier,content = content) { measurables, constraints ->// 測量階段val placeables = measurables.map { measurable ->measurable.measure(constraints)}// 布局階段layout(constraints.maxWidth, constraints.maxHeight) {placeables.forEach { placeable ->placeable.place(0, 0)}}}
}

在這個示例中,Layout?可組合函數接收一個?content?參數,該參數是一個可組合函數,包含了要布局的子組件。在測量階段,使用?measurables.map?遍歷所有子組件,并調用?measurable.measure(constraints)?方法進行測量。在布局階段,使用?layout?方法確定布局的大小,并調用?placeable.place(0, 0)?方法將子組件放置到指定位置。

2.3 Compose 修飾符(Modifier)

2.3.1 修飾符的作用和特點

修飾符是 Compose 中用于修改可組合函數行為的機制。修飾符可以鏈式調用,每個修飾符都會對組件進行一些修改,如設置大小、邊距、背景顏色、點擊事件等。修飾符的使用使得代碼更加簡潔和靈活。

以下是一個使用修飾符的示例:

kotlin

@Composable
fun ModifiedText() {Text(text = "Modified Text",modifier = Modifier.padding(16.dp).background(Color.Gray).clickable {// 處理點擊事件})
}

在這個示例中,Text?組件使用了?paddingbackground?和?clickable?修飾符,分別設置了內邊距、背景顏色和點擊事件。

2.3.2 常見修飾符的源碼分析

不同的修飾符有不同的實現方式。以?padding?修飾符為例,其源碼簡化如下:

kotlin

fun Modifier.padding(all: Dp): Modifier = this.then(PaddingModifier(start = all,top = all,end = all,bottom = all)
)private class PaddingModifier(private val start: Dp,private val top: Dp,private val end: Dp,private val bottom: Dp
) : Modifier.Element {override fun MeasureScope.measure(measurable: Measurable,constraints: Constraints): MeasureResult {val innerConstraints = constraints.copy(minWidth = max(0, constraints.minWidth - (start.roundToPx() + end.roundToPx())),minHeight = max(0, constraints.minHeight - (top.roundToPx() + bottom.roundToPx())),maxWidth = max(0, constraints.maxWidth - (start.roundToPx() + end.roundToPx())),maxHeight = max(0, constraints.maxHeight - (top.roundToPx() + bottom.roundToPx())))val placeable = measurable.measure(innerConstraints)return layout(placeable.width + start.roundToPx() + end.roundToPx(),placeable.height + top.roundToPx() + bottom.roundToPx()) {placeable.place(start.roundToPx(), top.roundToPx())}}
}

在這個示例中,padding?修飾符創建了一個?PaddingModifier?實例。在?PaddingModifier?的?measure?方法中,首先根據內邊距調整約束條件,然后對子組件進行測量,最后根據測量結果和內邊距確定布局的大小和子組件的位置。

2.3.3 修飾符的鏈式調用原理

修飾符的鏈式調用是通過?Modifier?類的?then?方法實現的。then?方法會將當前修飾符和傳入的修飾符組合成一個新的修飾符。例如:

kotlin

val modifier = Modifier.padding(16.dp).background(Color.Gray)

在這個示例中,padding?修飾符和?background?修飾符通過?then?方法組合成一個新的修飾符。當應用這個修飾符時,會依次執行每個修飾符的操作。

三、Box 布局詳細分析

3.1 Box 布局的基本概念和用途

Box?是 Compose 中最基礎的布局組件之一,類似于傳統 Android 布局中的?FrameLayout。它可以將子元素堆疊在一起,子元素默認會從左上角開始布局,后添加的元素會覆蓋在先添加的元素之上。Box?常用于創建簡單的堆疊布局,如在圖片上添加文本標簽、創建徽章等。

3.2 Box 可組合函數的源碼解析

3.2.1 Box 可組合函數的定義和參數

Box?可組合函數的定義如下:

kotlin

@Composable
fun Box(modifier: Modifier = Modifier,contentAlignment: Alignment = Alignment.TopStart,propagateMinConstraints: Boolean = false,content: @Composable BoxScope.() -> Unit
) {// 函數體
}
  • modifier:用于修改?Box?的行為,如設置大小、邊距、背景顏色等。
  • contentAlignment:指定子元素的對齊方式,默認值為?Alignment.TopStart,表示左上角對齊。
  • propagateMinConstraints:是否將最小約束條件傳遞給子元素,默認值為?false
  • content:一個可組合函數,包含了要布局的子元素。
3.2.2 Box 可組合函數的實現細節

Box?可組合函數的實現主要依賴于?BoxWithConstraints?和?Layout?可組合函數。以下是簡化后的源碼:

kotlin

@Composable
fun Box(modifier: Modifier = Modifier,contentAlignment: Alignment = Alignment.TopStart,propagateMinConstraints: Boolean = false,content: @Composable BoxScope.() -> Unit
) {BoxWithConstraints(modifier = modifier,propagateMinConstraints = propagateMinConstraints) {Layout(content = {BoxScopeImpl(constraints.maxWidth,constraints.maxHeight,contentAlignment).content()}) { measurables, constraints ->// 測量階段val placeables = measurables.map { measurable ->measurable.measure(constraints)}// 計算布局的寬度和高度val width = if (constraints.hasFixedWidth) {constraints.maxWidth} else {placeables.maxOfOrNull { it.width } ?: 0}val height = if (constraints.hasFixedHeight) {constraints.maxHeight} else {placeables.maxOfOrNull { it.height } ?: 0}// 布局階段layout(width, height) {placeables.forEach { placeable ->val position = contentAlignment.align(IntSize(placeable.width, placeable.height),IntSize(width, height),layoutDirection)placeable.place(position.x, position.y)}}}}
}
  • BoxWithConstraints:用于處理約束條件,將父布局傳遞的約束條件提供給子布局。
  • Layout:用于進行測量和布局。在測量階段,遍歷所有子元素并進行測量;在布局階段,根據?contentAlignment?確定子元素的位置并放置。
3.2.3 測量階段的源碼分析

在測量階段,Box?會遍歷所有子元素,并調用?measurable.measure(constraints)?方法進行測量。constraints?是父布局傳遞的約束條件,子元素會根據這些約束條件確定自身的大小。以下是測量階段的關鍵代碼:

kotlin

val placeables = measurables.map { measurable ->measurable.measure(constraints)
}

這里使用?map?函數遍歷所有子元素,并調用?measure?方法進行測量,返回一個包含所有子元素?Placeable?對象的列表。

3.2.4 布局階段的源碼分析

在布局階段,Box?會根據?contentAlignment?確定子元素的位置。contentAlignment?是一個?Alignment?對象,提供了?align?方法用于計算子元素的位置。以下是布局階段的關鍵代碼:

kotlin

layout(width, height) {placeables.forEach { placeable ->val position = contentAlignment.align(IntSize(placeable.width, placeable.height),IntSize(width, height),layoutDirection)placeable.place(position.x, position.y)}
}

這里使用?forEach?函數遍歷所有子元素,調用?contentAlignment.align?方法計算子元素的位置,然后調用?placeable.place?方法將子元素放置到指定位置。

3.3 Box 的不同使用場景和示例

3.3.1 簡單堆疊布局

kotlin

@Composable
fun SimpleStackingExample() {Box(modifier = Modifier.size(200.dp).background(Color.LightGray)) {Text(text = "Text in Box",modifier = Modifier.padding(16.dp))Image(painter = painterResource(id = R.drawable.sample_image),contentDescription = "Sample Image",modifier = Modifier.size(50.dp).align(Alignment.BottomEnd))}
}

在這個示例中,Box?包含一個?Text?組件和一個?Image?組件。Image?組件使用?align?修飾符將其對齊到?Box?的右下角。

3.3.2 背景和前景布局

kotlin

@Composable
fun BackgroundForegroundExample() {Box(modifier = Modifier.size(200.dp).background(Color.LightGray)) {// 背景元素Box(modifier = Modifier.fillMaxSize().background(Color.Blue.copy(alpha = 0.5f)))// 前景元素Text(text = "Foreground Text",modifier = Modifier.align(Alignment.Center).padding(16.dp))}
}

在這個示例中,外層?Box?作為容器,內層第一個?Box?作為背景元素,設置了半透明的藍色背景。Text?組件作為前景元素,居中顯示。

3.3.3 徽章布局

kotlin

@Composable
fun BadgeExample() {Box(modifier = Modifier.size(100.dp).background(Color.Gray)) {Image(painter = painterResource(id = R.drawable.sample_image),contentDescription = "Sample Image",modifier = Modifier.fillMaxSize())Box(modifier = Modifier.size(20.dp).background(Color.Red).align(Alignment.TopEnd)) {Text(text = "3",modifier = Modifier.align(Alignment.Center),color = Color.White)}}
}

在這個示例中,Box?包含一個?Image?組件和一個用于顯示徽章的內層?Box。徽章?Box?位于?Image?的右上角,顯示數字 3。

3.4 Box 的對齊方式和修飾符使用

3.4.1 對齊方式的源碼分析

Alignment?是一個枚舉類,定義了多種對齊方式,如?TopStartCenterBottomEnd?等。Alignment?類提供了?align?方法,用于計算子元素的位置。以下是?Alignment?類的簡化源碼:

kotlin

enum class Alignment {TopStart,TopCenter,TopEnd,CenterStart,Center,CenterEnd,BottomStart,BottomCenter,BottomEnd;fun align(size: IntSize,parentSize: IntSize,layoutDirection: LayoutDirection): IntOffset {val horizontalOffset = when (this) {TopStart, CenterStart, BottomStart -> 0TopCenter, Center, BottomCenter -> (parentSize.width - size.width) / 2TopEnd, CenterEnd, BottomEnd -> parentSize.width - size.width}val verticalOffset = when (this) {TopStart, TopCenter, TopEnd -> 0CenterStart, Center, CenterEnd -> (parentSize.height - size.height) / 2BottomStart, BottomCenter, BottomEnd -> parentSize.height - size.height}return IntOffset(horizontalOffset, verticalOffset)}
}

在?align?方法中,根據不同的對齊方式計算子元素的水平和垂直偏移量,然后返回一個?IntOffset?對象表示子元素的位置。

3.4.2 修飾符在 Box 中的使用

Box?可以使用各種修飾符來修改其行為和外觀。例如,size?修飾符用于設置?Box?的大小,background?修飾符用于設置背景顏色,padding?修飾符用于設置內邊距等。以下是一個使用多種修飾符的示例:

kotlin

@Composable
fun BoxWithModifiersExample() {Box(modifier = Modifier.size(200.dp).background(Color.LightGray).padding(16.dp).clip(RoundedCornerShape(8.dp)).clickable {// 處理點擊事件}) {Text(text = "Box with Modifiers",modifier = Modifier.align(Alignment.Center))}
}

在這個示例中,Box?使用了?sizebackgroundpaddingclip?和?clickable?修飾符,分別設置了大小、背景顏色、內邊距、圓角和點擊事件。

四、Stack 布局分析(歷史版本)

4.1 Stack 布局的歷史背景和作用

在 Compose 早期版本中,Stack?是用于堆疊子元素的布局組件,類似于?Box。它為開發者提供了一種簡單的方式來創建堆疊布局。然而,隨著 Compose 的發展,為了簡化 API 設計,Stack?在較新版本中被棄用,推薦使用?Box?替代。

4.2 Stack 可組合函數的源碼解析

4.2.1 Stack 可組合函數的定義和參數

kotlin

@Composable
@Deprecated("Use Box instead", ReplaceWith("Box(modifier, contentAlignment, propagateMinConstraints, content)"))
fun Stack(modifier: Modifier = Modifier,contentAlignment: Alignment = Alignment.TopStart,propagateMinConstraints: Boolean = false,content: @Composable StackScope.() -> Unit
) {// 函數體
}
  • modifier:用于修改?Stack?的行為,如設置大小、邊距、背景顏色等。
  • contentAlignment:指定子元素的對齊方式,默認值為?Alignment.TopStart
  • propagateMinConstraints:是否將最小約束條件傳遞給子元素,默認值為?false
  • content:一個可組合函數,包含了要布局的子元素。
4.2.2 Stack 可組合函數的實現細節

Stack?可組合函數的實現與?Box?類似,主要依賴于?Layout?可組合函數。以下是簡化后的源碼:

kotlin

@Composable
@Deprecated("Use Box instead", ReplaceWith("Box(modifier, contentAlignment, propagateMinConstraints, content)"))
fun Stack(modifier: Modifier = Modifier,contentAlignment: Alignment = Alignment.TopStart,propagateMinConstraints: Boolean = false,content: @Composable StackScope.() -> Unit
) {Layout(modifier = modifier,content = {StackScopeImpl(contentAlignment).content()}) { measurables, constraints ->// 測量階段val placeables = measurables.map { measurable ->measurable.measure(constraints)}// 計算布局的寬度和高度val width = if (constraints.hasFixedWidth) {constraints.maxWidth} else {placeables.maxOfOrNull { it.width } ?: 0}val height = if (constraints.hasFixedHeight) {constraints.maxHeight} else {placeables.maxOfOrNull { it.height } ?: 0}// 布局階段layout(width, height) {placeables.forEach { placeable ->val position = contentAlignment.align(IntSize(placeable.width, placeable.height),IntSize(width, height),layoutDirection)placeable.place(position.x, position.y)}}}
}
  • Layout:用于進行測量和布局。在測量階段,遍歷所有子元素并進行測量;在布局階段,根據?contentAlignment?確定子元素的位置并放置。
4.2.3 測量和布局階段的對比分析

Stack?的測量和布局階段與?Box?基本相同。在測量階段,都使用?measurable.measure(constraints)?方法對子元素進行測量;在布局階段,都使用?contentAlignment.align?方法計算子元素的位置并調用?placeable.place?方法進行放置。主要區別在于?Stack?提供了?StackScope,而?Box?提供了?BoxScope,但功能上基本一致。

4.3 Stack 布局的使用示例和局限性

4.3.1 使用示例

kotlin

@Composable
fun StackExample() {Stack(modifier = Modifier.size(200.dp).background(Color.LightGray),contentAlignment = Alignment.Center) {Text(text = "Centered Text")Image(painter = painterResource(id = R.drawable.sample_image),contentDescription = "Sample Image",modifier = Modifier.size(50.dp))}
}

在這個示例中,Stack?包含一個?Text?組件和一個?Image?組件,子元素居中對齊。

4.3.2 局限性和棄用原因

Stack?的功能與?Box?基本相同,但為了簡化 Compose 的 API 設計,統一布局組件的使用方式,Stack?被棄用。使用?Box?可以使代碼更加簡潔和一致,同時也便于維護和擴展。

五、Box 和 Stack 的高級用法

5.1 嵌套使用 Box 和 Stack(歷史版本 Stack)

5.1.1 多層嵌套布局的實現和原理

Box?和?Stack(歷史版本)可以嵌套使用,以實現更復雜的布局效果。多層嵌套布局的原理是每個布局組件都會獨立進行測量和布局,子布局組件會根據父布局組件提供的約束條件進行計算。

以下是一個多層嵌套布局的示例:

kotlin

@Composable
fun NestedBoxExample() {Box(modifier = Modifier.size(300.dp).background(Color.LightGray),contentAlignment = Alignment.Center) {Box(modifier = Modifier.size(200.dp).background(Color.Gray),contentAlignment = Alignment.BottomEnd) {Text(text = "Nested Text")}}
}

在這個示例中,外層?Box?的大小為 300dp x 300dp,背景顏色為淺灰色,子元素居中對齊。內層?Box?的大小為 200dp x 200dp,背景顏色為灰色,子元素位于右下角。

5.1.2 嵌套布局的性能考慮

多層嵌套布局會增加布局的復雜度,可能會影響性能。在設計布局時,應盡量減少嵌套層級,合理使用其他布局組件。例如,如果只需要簡單的垂直或水平排列,可以使用?Column?或?Row?布局。

5.2 結合其他布局組件使用

5.2.1 與 Column 和 Row 布局的組合

Box?可以與?Column?和?Row?布局組合使用,以實現更復雜的布局。例如,在?Column?布局中使用?Box?來創建堆疊效果。

kotlin

@Composable
fun BoxWithColumnExample() {Column(modifier = Modifier.padding(16.dp).fillMaxWidth()) {Box(modifier = Modifier.size(200.dp).background(Color.LightGray),contentAlignment = Alignment.Center) {Text(text = "Box in Column")}Text(text = "Text below Box")}
}

在這個示例中,Column?布局包含一個?Box?組件和一個?Text?組件。Box?組件顯示一個居中的文本,Text?組件顯示在?Box?下方。

5.2.2 與 LazyColumn 和 LazyRow 布局的組合

Box?還可以與?LazyColumn?和?LazyRow?布局組合使用,用于創建可滾動的堆疊布局。

kotlin

@Composable
fun BoxWithLazyColumnExample() {LazyColumn(modifier = Modifier.fillMaxWidth().padding(16.dp)) {items(10) { index ->Box(modifier = Modifier.size(200.dp).background(Color.LightGray).padding(8.dp),contentAlignment = Alignment.Center) {Text(text = "Item $index")}}}
}

在這個示例中,LazyColumn?布局包含 10 個?Box?組件,每個?Box?組件顯示一個文本。

5.3 動態布局和狀態管理

5.3.1 根據狀態動態改變布局

Box?和?Stack?可以根據狀態變化進行動態布局。例如,根據一個布爾值狀態來顯示或隱藏一個子元素。

kotlin

@Composable
fun DynamicBoxExample() {var isVisible by remember { mutableStateOf(true) }Box(modifier = Modifier.size(200.dp).background(Color.LightGray)) {if (isVisible) {Text(text = "Visible Text",modifier = Modifier.align(Alignment.Center))}Button(onClick = { isVisible = !isVisible },modifier = Modifier.align(Alignment.BottomEnd)) {Text(text = if (isVisible) "Hide" else "Show")}}
}

在這個示例中,Box?包含一個?Text?組件和一個?Button?組件。點擊?Button?可以切換?isVisible?狀態,從而顯示或隱藏?Text?組件。

5.3.2 動畫效果的實現

Compose 提供了豐富的動畫 API,可以為?Box?和?Stack?布局添加動畫效果。例如,使用?animateContentSize?修飾符為?Box?添加內容大小變化的動畫。

kotlin

@Composable
fun AnimatedBoxExample() {var isExpanded by remember { mutableStateOf(false) }Box(modifier = Modifier.size(if (isExpanded) 300.dp else 200.dp).background(Color.LightGray).animateContentSize()) {Button(onClick = { isExpanded = !isExpanded },modifier = Modifier.align(Alignment.Center)) {Text(text = if (isExpanded) "Shrink" else "Expand")}}
}

在這個示例中,點擊?Button?可以切換?isExpanded?狀態,Box?的大小會根據狀態變化進行動畫過渡。

六、性能優化與注意事項

6.1 布局性能優化

6.1.1 減少不必要的嵌套

過度嵌套?Box?和?Stack?會增加布局的復雜度,降低性能。在設計布局時,應盡量減少嵌套層級,合理使用其他布局組件。例如,如果只需要簡單的垂直或水平排列,可以使用?Column?或?Row?布局。

6.1.2 合理使用約束條件

在使用?Box?和?Stack?時,應合理設置約束條件,避免不必要的測量和布局計算。例如,如果已知子元素的大小,可以使用固定大小的約束條件,減少計算量。

6.1.3 避免頻繁的重繪

頻繁的重繪會影響性能。在設計布局時,應盡量減少狀態變化導致的重繪。可以使用?remember?關鍵字緩存一些不變的數據,避免重復計算。

6.2 內存管理和資源使用

6.2.1 避免創建過多的臨時對象

在動態布局中,應注意內存管理,避免創建過多的臨時對象。例如,在狀態變化時,盡量復用已有的對象,減少垃圾回收的壓力。

6.2.2 及時釋放資源

如果?Box?或?Stack?中包含一些需要釋放資源的組件,如?Image?組件,應在組件銷毀時及時釋放資源,避免內存泄漏。

6.3 兼容性和版本問題

6.3.1 不同 Compose 版本的差異

Compose 框架在不斷發展和更新,不同版本可能存在一些差異。例如,Stack?在較新版本中被棄用,推薦使用?Box?替代。在開發過程中,應關注 Compose 版本的更新,及時調整代碼。

6.3.2 設備兼容性考慮

不同設備的屏幕分辨率和性能可能存在差異,在設計布局時,應考慮設備的兼容性。可以使用?Modifier?中的?fillMaxWidthfillMaxHeight?等修飾符來實現自適應布局。

七、總結與展望

7.1 對 Box 和 Stack 布局的總結

Box?和?Stack(歷史版本)是 Android Compose 中基礎的布局組件,它們為開發者提供了靈活的方式來創建堆疊布局。Box?作為?Stack?的替代方案,在 API 設計上更加統一和簡潔。通過深入分析它們的源碼,我們了解了其測量和布局的實現原理,以及如何使用修飾符和對齊方式來實現不同的布局效果。

7.2 Compose 布局系統的發展趨勢

隨著 Compose 框架的不斷發展,布局系統可能會進一步優化和擴展。例如,可能會提供更多的布局組件和修飾符,以滿足不同的布局需求;可能會優化布局性能,減少測量和布局的計算量;可能會加強對動畫和交互的支持,使布局更加生動和靈活。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74091.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74091.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74091.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

知識蒸餾:讓大模型“瘦身“而不失智慧的魔術

引言&#xff1a;當AI模型需要"減肥" 在人工智能領域&#xff0c;一個有趣的悖論正在上演&#xff1a;大模型的參數規模每年以10倍速度增長&#xff0c;而移動設備的算力卻始終受限。GPT-4的1750億參數需要價值500萬美元的GPU集群運行&#xff0c;但現實中的智能設備…

多路FM調頻廣播解調器:多路電臺FM廣播信號一體化解調處理方案

多路FM調頻廣播解調器&#xff1a;多路電臺FM廣播信號一體化解調處理方案 支持OEM型號開放式協議支持二次開發設計 北京海特偉業科技有限公司任洪卓發布于2025年3月21日 在信息傳播領域&#xff0c;FM調頻廣播媒體以其獨特的優勢持續發揮著重要作用。為了應對日益增長的多路…

如何在Spring Boot中設置HttpOnly Cookie以增強安全性

引言 在Web開發中,Cookie是用于在客戶端和服務器之間傳遞信息的重要機制。然而,Cookie的安全性一直是一個備受關注的問題。特別是當Cookie中存儲了敏感信息(如會話ID)時,如何防止這些信息被惡意腳本竊取就顯得尤為重要。HttpOnly屬性是增強Cookie安全性的一種有效手段。本…

LangManus:新一代開源智能體框架如何讓AI開發更簡單?

你是否想過&#xff0c;代碼生成、數據分析甚至系統調試&#xff0c;都能由一個“AI助手”自動完成&#xff1f;最近&#xff0c;一款名為LangManus的開源項目在開發者社區掀起熱議。它不只是一個工具庫&#xff0c;更是一個能自主思考、執行復雜任務的智能體框架。無論是企業內…

【STM32】SPI通信協議W25Q64Flash存儲器芯片(學習筆記)

通信接口部分有介紹SPI&#xff1a;【STM32】USART串口協議&串口外設-學習筆記-CSDN博客 SPI通信協議 SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff09;是由Motorola公司開發的一種通用數據總線四根通信線&#xff1a;SCK&#xff08;Serial Clock&…

批量合并 PPT 文件,支持合并成單個文件也支持按文件夾合并

合并多個 PPT 為一個 PPT 文檔是我們經常會碰到的需求&#xff0c;合并后不僅更容易管理&#xff0c;在某些場景&#xff08;比如批量打印&#xff09;下也非常的有用&#xff0c;那當我們需要批量合并多個 PPT 文檔地時候&#xff0c;我們有沒有比較高效的方法呢&#xff1f;今…

LDAP從入門到實戰:環境部署與配置指南(下)

#作者&#xff1a;朱雷 接上篇&#xff1a;《LDAP從入門到實戰&#xff1a;環境部署與配置指南&#xff08;上&#xff09;》 鏈接: link 文章目錄 2.5.添加賬號2.6.停止服務2.7.使用TLS證書2.7.1. TLS 證書2.7.2. TLS 配置2.7.3. 服務器配置 2.8.使用安全連接的反向代理 2.5…

發現一個好用的Vue.js內置組件

目錄 一、這個好用的內置組件是什么&#xff1f; 二、這個組件的主要功能 三、怎么使用&#xff1f; 四、使用注意事項 五、我的使用場景 一、這個好用的內置組件是什么&#xff1f; 今天在優化我的平臺應用時&#xff0c;發現一個好用的組件標簽--<keep-alive>。 …

dart學習記錄5(類、對象)

1.獲取運行時對象類型 使用Object 屬性的 runtimeType&#xff0c;它返回一個 Type 對象。 print(a 的類型是 ${a.runtimeType});??警告 在測試對象的類型時建議使用object is Type比測試 object.runtimeType Type 更穩定。 2.實例變量的聲明 class Point {double? x;…

啟明星辰春招面試題

《網安面試指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇網安資料庫https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

Live555+Windows+MSys2 編譯Androidso庫和運行使用

下載 wget http://www.live555.com/liveMedia/public/live555-latest.tar.gz tar -xzvf live555-latest.tar.gz加入版本控制 git init git add . git commit -a -m "first init" git log修改config.android-arm64 cd live vim config.android-arm64 ./genMakefile…

實用工具-Stirling-PDF

windows桌面版參考這個文檔 Getting Started | Stirling-PDF 安裝包推薦使用迅雷下載&#xff0c;先轉存到迅雷網盤在使用迅雷下載速度嘎嘎快。 github:https://github.com/Stirling-Tools/Stirling-PDF Stirling-PDF 是一個強大的、基于 Web 的開源 PDF 處理工具&#xff0c…

借助AI Agent實現數據分析

在當今數據驅動的世界中&#xff0c;數據分析已成為企業決策、科學研究和社會治理的核心工具。然而&#xff0c;隨著數據量的爆炸式增長和復雜性的提升&#xff0c;傳統的數據分析方法面臨著效率低下、成本高昂和人力不足等挑戰。AI技術的快速發展&#xff0c;尤其是AI Agent的…

JavaScript實現一個函數,將數組扁平化(flatten),即把多維數組轉為一維數組。

大白話實現一個函數&#xff0c;將數組扁平化&#xff08;flatten&#xff09;&#xff0c;即把多維數組轉為一維數組。 思路 實現數組扁平化的基本思路是遍歷數組中的每個元素&#xff0c;如果元素是數組&#xff0c;就遞歸地將其扁平化并添加到結果數組中&#xff1b;如果元…

麒麟操作系統安裝人大金倉數據庫

如果你想擁有你從未擁有過的東西&#xff0c;那么你必須去做你從未做過的事情 在當前數字化轉型和信息安全備受重視的背景下&#xff0c;眾多公司積極推進國產化改造進程。在操作系統領域&#xff0c;統信、open 歐拉、中標麒麟、銀河麒麟等國產操作系統嶄露頭角&#xff0c;逐…

開發SAPUI5 Fiori應用并部署到SAP系統

首先新建一個項目文件夾 在VScode中打開 打開SAP Fiori&#xff08;需要先下載安裝&#xff0c;參考上上一篇文章&#xff09; ,選擇已添加的SAP S4 ERP系統 ,點擊創建Firoi應用。 如果沒有添加系統的&#xff0c;點擊添加按鈕&#xff0c;添加即可&#xff0c;注意&#xff…

右鍵添加:新建HTML模板文件

使用注冊表給Windows右鍵添加:新建HTML文檔模板的功能_注冊表右鍵新建-CSDN博客 新建文件有了&#xff0c;但是沒有引用模板文件&#xff0c;是空文件。 默認改成 htmlfile 模板成功

[極客大挑戰 2019]Knife——3.20BUUCTF練習day4(1)

[極客大挑戰 2019]Knife——3.20BUUCTF練習day4(1) 很簡單 蟻劍連接 根目錄下有flag flag{f77e8444-dd87-48b3-8fe0-a735b5a5c708}

力扣22.括號生成

22. 括號生成 - 力扣&#xff08;LeetCode&#xff09; 代碼區&#xff1a; class Solution {vector<string> ans; public:vector<string> generateParenthesis(int n) {dfs(0,0,n,"");return ans;}void dfs(int left,int right,int n,string str){if(l…

第37周:文獻閱讀

目錄 摘要 Abstract 文獻閱讀 問題引入 研究背景 研究意義 研究目的 實驗方法 TimeGAN 數據增強 預測模型的獨立性 創新點 實驗研究 數據準備 合成數據分析 模型比較 總結 摘要 該文獻圍繞利用 TimeGAN 提高供熱變電站熱負荷預測精度展開。結構上&#x…