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.rhkb.cn/news/37722.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[网络安全] 滥用Azure内置Contributor角色横向移动至Azure VM

本文来源于团队的超辉老师&#xff0c;其系统分析了Azure RBAC角色模型及其在权限滥用场景下的攻击路径。通过利用AADInternals工具提升用户至Contributor角色&#xff0c;攻击者可在Azure VM中远程执行命令&#xff0c;创建后门账户&#xff0c;实现横向移动。文中详述了攻击步…

OO_Unit1

第一次作业 UML类图 代码复杂度分析 其中Expr中的toString方法认知复杂度比较高&#xff0c;主要源于多层条件嵌套和分散的字符串处理逻辑&#xff0c;重构时可重点关注这两部分的解耦。 代码量分析 1.”通用形式“ 我觉得我的设计的最大特点就是“通用形式”&#xff0c;具…

阿里云 AI 搜索产品荣获 Elastic Innovation Award 2024

阿里云AI搜索产品荣获Elastic Innovation Award 2024&#xff0c;该奖项于近日在新加坡ElasticON 2025的Elastic合作伙伴峰会上颁发&#xff0c;旨在表彰基于Elastic平台开发企业级生成式人工智能&#xff08;GenAI&#xff09;应用的顶尖合作伙伴&#xff0c;这些应用有效帮助…

网络原理之网络层、数据链路层

1. 网络层 1.1 IP协议 1.1.1 基本概念 主机: 配有IP地址,但是不进⾏路由控制的设备路由器: 即配有IP地址,⼜能进⾏路由控制节点: 主机和路由器的统称 1.1.2 协议头格式 说明&#xff1a; 4位版本号(version): 指定IP协议的版本,对于IPv4来说,就是4,对于IPv6来说,就是6 4位头…

炫酷的3D按钮效果实现 - CSS3高级特性应用

炫酷的3D按钮效果实现 - CSS3高级特性应用 这里写目录标题 炫酷的3D按钮效果实现 - CSS3高级特性应用项目介绍核心技术实现1. 基础结构设计2. 视觉效果实现2.1 背景渐变2.2 立体感营造 3. 交互动效设计3.1 悬停效果3.2 按压效果 技术要点分析1. 深度层次感2. 动画过渡3. 性能优…

Java定时任务的三重境界:从单机心跳到分布式协调

《Java定时任务的三重境界&#xff1a;从单机心跳到分布式协调》 本文将以生产级代码标准&#xff0c;揭秘Java定时任务从基础API到分布式调度的6种实现范式&#xff0c;深入剖析ScheduledThreadPoolExecutor与Quartz Scheduler的线程模型差异&#xff0c;并给出各方案的性能压…

鸿蒙Flutter开发故事:不,你不需要鸿蒙化

在华为牵头下&#xff0c;Flutter 鸿蒙化如火如荼进行&#xff0c;当第一次看到一份上百个插件的Excel 列表时&#xff0c;我也感到震惊&#xff0c;排名前 100 的插件赫然在列&#xff0c;这无疑是一次大规模的军团作战。 然后&#xff0c;参战团队鱼龙混杂&#xff0c;难免有…

PolyBench基准程序详解:编译器优化评测指标

PolyBench基准程序详解&#xff1a;编译器优化评测指标 PolyBench基本概念 PolyBench&#xff08;Polyhedral Benchmark&#xff09;是由UCLA&#xff08;加州大学洛杉矶分校&#xff09;的Louis-Nol Pouchet及其研究团队开发的基准测试套件&#xff0c;专门用于评估多面体编…

2025年渗透测试面试题总结-某四字大厂实习面试复盘 一面 二面 三面(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 一面 1. 数组和链表各自的优势和原因 2. 操作系统层面解析和进程 3. 线程和进程通信方式及数据安全问…

ruoyi-vue部署4

1.jdk-linux安装 2.tomcat-linux安装 3.ruoy后台部署 4.nginx-linux安装5.ruoyi前端部署​​​​​​​

查看visual studio的MSVC版本的方法

右键项目名称&#xff0c;下拉点击属性 然后点击库目录&#xff0c;下拉点击编辑 就可以看见msvc版本了

【Javascrip】Javascript练习01 REST API using Express.js.

针对该问题的项目路径 要求部分 what you need to doReview the tasks provided in the section below.Obtain the boilerplate code.Use your local development environment to implement a solution.Upload your solution for marking via Gradescope. There is no attempt…

【蓝桥杯速成】| 9.回溯升级

题目一&#xff1a;组合综合 问题描述 39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返…

apache-maven-3.9.9 详细安装配置教程(2025版)

apache-maven-3.9.9 详细安装配置教程 一、下载解压二、配置本地仓库镜像源三、配置环境变量四、配置 IDEA 一、下载解压 官网地址&#xff1a; https://maven.apache.org/download.cgi二、配置本地仓库镜像源 解压并新建文件夹&#xff0c;作为 maven 下载仓库。目的&#…

构建企业级数据的愿景、目标与规划历程

文章目录 1. 企业级数据的愿景2. 企业级数据的目标、实施标准和战略3. 企业级数据的蓝图3.1 业务数字化转型的蓝图3.2 大数据平台的架构蓝图 4. 企业级数据的规划历程4.1 第一阶段&#xff1a;数据生产与打通4.2 第二阶段&#xff1a;数据集成、联接、应用 伴随着数字科技、通信…

深入理解 JavaScript/TypeScript 中的假值(Falsy Values)与逻辑判断 ✨

&#x1f579;️ 深入理解 JavaScript/TypeScript 中的假值&#xff08;Falsy Values&#xff09;与逻辑判断 在 JavaScript/TypeScript 开发中&#xff0c;if (!value) 是最常见的条件判断之一。它看似简单&#xff0c;却隐藏着语言的核心设计逻辑&#xff0c;也是许多开发者…

74HC04(反相器)和74HC14(反相器、施密特触发器)的区别

74HC04和74HC14的具体区别详解 同样具有反相器功能&#xff0c;你知道74HC04和74HC14的具体区别吗&#xff1f; 74HC04 对于74HC04很好理解&#xff0c;输入低电平&#xff0c;输出高电平&#xff1b;输入高电平&#xff0c;输出低电平。 建议操作条件&#xff1a; 下图是TI的…

面向医药仓储场景下的药品分拣控制策略方法 研究(大纲)

面向医药仓储场景下的药品分拣控制策略方法研究 基于多机器人协同与智能调度的分拣系统设计 第一章 绪论 1.1 研究背景与意义 医药仓储自动化需求&#xff1a; 人工分拣效率低、出错率高&#xff08;如药品批次混淆、过期风险&#xff09;温控药品&#xff08;如疫苗、生物制…

AI大白话(三):深度学习——AI的‘大脑‘是如何构建的?

🌟引言: 专栏:《AI大白话》 AI大白话(一):5分钟了解AI到底是什么? AI大白话(二):机器学习——AI是怎么“学习“的? 大家好!继前两篇介绍AI基础和机器学习的文章后,今天我们来聊聊深度学习——这个让AI技术近年来突飞猛进的"神奇引擎"。别担心,我会用…

19681 01背包

19681 01背包 ⭐️难度&#xff1a;中等 &#x1f31f;考点&#xff1a;动态规划、01背包 &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 10001…