当前位置: 首页 > news >正文

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 框架的不断发展,布局系统可能会进一步优化和扩展。例如,可能会提供更多的布局组件和修饰符,以满足不同的布局需求;可能会优化布局性能,减少测量和布局的计算量;可能会加强对动画和交互的支持,使布局更加生动和灵活。

相关文章:

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

Android Compose 基础布局之 Box 和 Stack 源码深度剖析 一、引言 1.1 Android 开发中布局的重要性 在 Android 应用开发里&#xff0c;布局是构建用户界面&#xff08;UI&#xff09;的关键环节。良好的布局设计能够提升用户体验&#xff0c;使应用界面更加美观、易用且具有…...

【强化学习】Reward Model(奖励模型)详细介绍

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…...

UE5材质法线强度控制节点FlattenNormal

连法 FlattenNormal内部是这样的 FlattenNormal的作用是用来调整法线强度 连上FlattenNormal后 拉高数值...

<项目> 主从Reactor模型的高并发服务器

目录 Reactor 概念 分类 单Reactor单线程 单Reactor多线程 多Reactor多线程 项目介绍 项目规划 模块关系 实现 TimerWheel -- 时间轮定时器 定时器系统调用 时间轮设计 通用类型Any Buffer Socket Channel Poller EventLoop&#xff08;核心&#xff09; eventfd 设计思路 …...

python爬虫解析器bs4,xpath,pquery

0x00 bs4 解析器的作用就是可以直接解析html页面&#xff0c;可以直接从网页中提取标签中的内容&#xff0c;而不用在使用正则表达式进行提起数据 import requests from bs4 import BeautifulSoup html_content <li id123><a hrefdfsdf>123</a>789</l…...

分析K8S中Node状态为`NotReady`问题

在Kubernetes&#xff08;k8s&#xff09;集群中&#xff0c;Node状态为NotReady通常意味着节点上存在某些问题&#xff0c;下面为你分析正常情况下节点应运行的容器以及解决NotReady状态的方法。 正常情况下Node节点应运行的容器 1. kubelet kubelet是节点上的核心组件&…...

【最后203篇系列】021 Q201再计划

忙了一周&#xff0c;终于到周末有时间再细细想这个问题了。这周还是不经意的弥补了kv硬盘存储库这个小空白的&#xff0c;这样也有助于构建更好的Q201。 计划是到6.1再发版&#xff0c;之所以留那么长时间&#xff0c;一方面是因为平时的确忙&#xff0c;另一方面则是可以有更…...

CA 机构如何防止中间人攻击

在现代互联网中&#xff0c;中间人攻击&#xff08;Man-in-the-Middle Attack&#xff0c;简称 MITM&#xff09;是一种常见的网络攻击方式&#xff0c;攻击者通过拦截和篡改通信双方的信息&#xff0c;进而窃取敏感数据或执行恶意操作。为了防止中间人攻击&#xff0c;证书颁发…...

CUL-CHMLFRP启动器 windows图形化客户端

CUL-CHMLFRP启动器 windows图形化客户端 基于v2 api开发的chmlfrp ui版本的第三方客户端 CUL原名CHMLFRP_UI CUL顾名思义为CHMLFRP-UI-Launcher 下载地址&#xff1a;https://cul.lanzoul.com/b00pzv3oyj 密码:ff50 下载解压运行即可&#xff08;仅支持win7以上版本&#xf…...

C语言基础08

内容提要 数组 排序算法&#xff1a;冒泡排序 二维数组 字符数组 数组 冒泡排序 排序思想&#xff08;向前冒泡&#xff09; 一次只排好一个数&#xff0c;针对n个数&#xff0c;最差情况需要n-1次就可以排好 每次排序假定第一个元素是最大或者最小&#xff0c;用第一个…...

基于javaweb的SpringBoot儿童爱心管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...

深度学习:从零开始的DeepSeek-R1-Distill有监督微调训练实战(SFT)

原文链接&#xff1a;从零开始的DeepSeek微调训练实战&#xff08;SFT&#xff09; 微调参考示例&#xff1a;由unsloth官方提供https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen2.5_(7B)-Alpaca.ipynbhttps://colab.research.google.com/git…...

JavaScript |(五)DOM简介 | 尚硅谷JavaScript基础实战

学习来源&#xff1a;尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 笔记来源&#xff1a;在这位大佬的基础上添加了一些东西&#xff0c;欢迎大家支持原创&#xff0c;大佬太棒了&#xff1a;JavaScript |&#xff08;五&#xff09;DOM简介 | 尚硅谷JavaScript基础…...

模型整合-cherry studio+mysql_mcp_server服务配置

一、什么是MCP MCP&#xff08;Model Context Protocol&#xff09;是模型上下文协议&#xff0c;它允许大型语言模型&#xff08;LLM&#xff09;通过协议与外部工具或服务交互&#xff0c;动态获取实时数据或执行操作。简单来说&#xff0c;它让模型不再局限于静态知识库&…...

【QA】装饰模式在Qt中有哪些运用?

在Qt框架中&#xff0c;装饰模式&#xff08;Decorator Pattern&#xff09;主要通过继承或组合的方式实现&#xff0c;常见于IO设备扩展和图形渲染增强场景。以下是Qt原生实现的装饰模式典型案例&#xff1a; 一、QIODevice装饰体系&#xff08;继承方式&#xff09; 场景 …...

window 设置自动开启/关闭程序(分享)

打开计算机管理 winr 输入 compmgmt.msc 找到任务计划程序创建任务 设置开启任务 常规&#xff1a;添加名称与描述 触发器&#xff1a;新建触发时间与次数 操作&#xff1a;新建执行程序 添加任务对应的位置 以便修改 设置关闭任务 添加批处理文件&#xff0c;写完后吧 .…...

QT布局笔记

在 Qt 中&#xff0c;如果你希望将一个 QGroupBox 放置在水平布局&#xff08;QHBoxLayout&#xff09;的上方&#xff0c;可以通过将它们添加到一个垂直布局&#xff08;QVBoxLayout&#xff09;中来实现。垂直布局会将子布局或子控件按垂直顺序排列&#xff0c;因此 QGroupBo…...

【LLM大模型】LangChain学习

大模型对话 from langchain.chat_models import ChatOpenAI # 内置对话模型 from langchain.schema import HumanMessage, SystemMessage, AIMessage # 用户提示词&#xff0c;系统提示词&#xff0c; AI响应chat ChatOpenAI(temperature0.7, openai_api_keyxxxxxxxxxxxx) #…...

SpringBoot实战(三十二)集成 ofdrw,实现 PDF 和 OFD 的转换、SM2 签署OFD

目录 一、OFD 简介 1.1 什么是 OFD&#xff1f;1.2 什么是 版式文档&#xff1f;1.3 为什么要用 OFD 而不是PDF&#xff1f; 二、ofdrw 简介 2.1 定义2.2 Maven 依赖2.3 ofdrw 的 13 个模块 三、PDF/文本/图片 转 OFD&#xff08;ofdrw-conterver&#xff09; 3.1 介绍&#xf…...

SolidWorks使用显卡教程

操作步骤&#xff1a; 打开注册表编辑器 按下键盘上的 Win R 组合键&#xff0c;输入 regedit 并按回车键&#xff0c;打开注册表编辑器。 导航到显卡信息路径 在注册表中依次展开以下路径&#xff1a; plaintext HKEY_CURRENT_USER\Software\SolidWorks\SOLIDWORKS 2021\Per…...

mysql 查询进程查看并释放

在MySQL中&#xff0c;查看和管理进程&#xff08;例如查询、连接等&#xff09;是数据库维护和性能调优的重要部分。以下是一些常用的方法来查看MySQL进程并释放它们。 1. 查看进程 你可以使用SHOW PROCESSLIST命令来查看当前MySQL服务器上的所有进程。这个命令会显示正在执…...

C++代码2-多目标算法求解车辆路径规划

为了解决车辆路径规划问题,我们需要在同一模型中同时考虑多个目标,其中一个目标是降低运营总成本,而另一个目标是降低总的碳排放量。使用组合算法,包括人工蜂群算法(Artificial Bee Colony, ABC)、模拟退火算法(Simulated Annealing, SA)、以及多目标优化算法MODAD(Mu…...

JAVA学习*接口

接口 在生活中我们常听说USB接口&#xff0c;那接口是什么呢&#xff1f; 在Java中&#xff0c;接口相当于多个类的一种公共规范&#xff0c;是一种引用数据类型。 定义接口 public interface IUSB {public static final String SIZE "small";public abstract vo…...

Matplotlib

一、Matplotlib快速入门 学习目标 了解什么是matplotlib 为什么要学习matplotlib matplotlib简单图形的绘制 1、什么是Matplotlib 是专门用于开发2D图表(包括3D图表) 以渐进、交互式方式实现数据可视化 2、为什么要学习Matplotlib 可视化是在整个数据挖掘的关键辅助工…...

新版frp-0.61.0 实现泛解析域名穿透 以及 https启用

需要在公网服务器的域名解析平台 泛域名 *.aa.com 解析到frp 公网服务器的ip x.x.x.x 对于frpc.toml 文件的 serverAddr 绑定的ip 需要公网服务器放行 bindPort 对于的端口 frpc.toml serverPort 对于的的是 frps.toml bindPort 端口 frps.toml bindPort 7000 vhostHTTPP…...

HTTPS 加密过程详解

HTTPS 详解及其加密过程流程框架 HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是一种基于 HTTP 协议的安全通信协议&#xff0c;通过 SSL/TLS 协议对传输数据进行加密和身份验证&#xff0c;解决了 HTTP 明文传输的安全隐患。以下是其核心原理和加密流程的…...

lua垃圾回收

lua垃圾回收 lua 垃圾回收 lua 垃圾回收 collectgarbage(“count”)获取当前lua脚本占用内存字节数(单位为KB)。 collectgarbage(“collect”)执行一次垃圾回收。 xxxnil 将变量置为空&#xff0c;会释放内存。 lua中的机制和c#中回收机制很类似 解除羁绊(置为空)。 --垃圾回…...

springboot继承使用mybatis-plus举例相关配置,包括分页插件以及封装分页类

以下是使用 MyBatis-Plus 分页插件的完整配置和封装步骤&#xff0c;包括日志输出、驼峰转下划线、逻辑删除以及分页属性类的封装。 1. 引入依赖 确保在 pom.xml 中已经引入 MyBatis-Plus 的依赖&#xff1a; <XML> <dependency><groupId>com.baomidou<…...

智能汽车以太网系统测试:聚焦ETH链路高负载性能表现

引言 在智能汽车高速发展的今天&#xff0c;车载以太网作为车辆信息交互的“神经网络”&#xff0c;承担着传输海量数据的关键使命。它不仅能够满足车辆对高带宽、低延迟和高可靠性的通信需求&#xff0c;还为自动驾驶、智能座舱等复杂功能提供了强大的技术支持。然而&#xf…...

学习笔记:黑马程序员JavaWeb开发教程(2025.3.21)

10.7 案例-员工管理-分页查询-分析 形参的默认值可以使用注解来设置&#xff1a;RequestParam(default “1”) 10.8 案例-员工管理-分页查询-PageHelper插件 分页插件PageHelper帮助完成有关分页的所有操作&#xff0c;我们只要正常使用查询语句就可以了。插件会自动…...

计算机网络精讲day1——计算机网络的性能指标(上)

性能指标1&#xff1a;速率 概念1&#xff1a;比特 英文全称是binary digit&#xff0c;意思是一个二进制数字&#xff0c;因此一个比特就是二进制数字中的1或0&#xff0c;比特也是信息论中使用的信息量单位。 概念2&#xff1a;速率 网络中的速率指的是数据的传送速率&#…...

gin-路由handler封装思路

约束handler入参和返回为func(ctx, req) (resp, error)。通过反射&#xff0c;封装handler&#xff0c;在调用前后写入入参和返回的处理。 package testingimport ("context""fmt""reflect""strings""testing" )type ReqPa…...

【实战案例】用STAR+3W模型拆解电商支付系统设计文档

各位开发者朋友&#xff0c;上次分享了结构化写作的黄金公式后&#xff0c;很多同学反馈需要更具象的落地方法。今天通过真实电商支付系统案例&#xff0c;手把手教你用STAR3W模型写出可执行的设计文档&#xff01; 结构化写作的「黄金公式」 STAR原则 3W模型 Situation&…...

计算机组成原理和计算机网络常见单位分类及换算

计算机组成原理&#xff08;主要用于存储、内存、缓存等&#xff09; 计算机网络&#xff08;主要用于传输速率&#xff09; 直观对比...

线性筛法求素数

时间复杂度 o&#xff08;n&#xff09; int cnt, primes[N];//cnt用来记录素数的下标 bool st[N];//用来标记合数 int minp[N];//最小质因数 void get_primes(int n) {for(int i 2;i < n;i )//从2开始找数 {if(!st[i])//如果这个数没有被筛出去过&#xff0c;说明是一…...

触动精灵对某东cookie读取并解密--记lua调用C语言

在Mac上构建Lua扩展模块&#xff1a;AES解密与Base64解码实战 今天我要分享一个实用技术&#xff1a;如何在Mac系统上为Lua编写和编译C扩展模块&#xff0c;特别是实现一个某东iOS PIN码解密功能的扩展。这对于需要在Lua环境中执行高性能计算或使用底层系统功能的开发者非常有…...

GEO:在AI时代抢占DeepSeekC位?

前言&#xff1a;当SEO遇见AGI——一场静默的流量革命 在生成式AI日均处理53亿次查询的今天&#xff0c;传统SEO的「关键词-排名-点击」逻辑正在崩塌。DeepSeek、ChatGPT、豆包等大模型用动态生成的答案&#xff0c;悄然截流了68%的搜索需求。更残酷的是&#xff1a;当用户问&q…...

【设计模式】策略模式

以下是格式优化后的Markdown文档&#xff0c;仅调整代码缩进&#xff0c;保持内容不变&#xff1a; 四、策略模式 策略(Strategy) 模式是一种行为型模式&#xff0c;其实现过程与模板方法模式非常类似——都 是以扩展的方式支持未来的变化。本章通过对一个具体范例的逐步重构…...

第J6周:ResNeXt-50实战解析

文章目录 一、前期准备1.设置GPU2.导入数据3.查看数据 二、数据预处理1.加载数据2.可视化数据3.再次检查数据4.配置数据集 四、模型复现1. 分组卷积模块2. 定义残差模块3. 堆叠残差单元4. 搭建ResNext-50网络5. 查看模型摘要 五、训练模型六、结果可视化总结&#xff1a; &…...

调试 ResNet18 cpp实现中的段错误(SIGSEGV)问题

调试 ResNet18 cpp实现中的段错误&#xff08;SIGSEGV&#xff09;问题 问题分析 您的程序在运行时遇到了段错误&#xff08;SIGSEGV&#xff09;&#xff0c;GDB显示错误发生在main()函数的第一行&#xff08;resnet18_allo_test.cpp:33&#xff09;。这种情况看起来很奇怪&…...

详细介绍IDI_APPLICATION和IDC_ARROW

书籍&#xff1a;《windows程序设计(第五版)》 环境&#xff1a;visual studio 2022 内容&#xff1a;HELLOWIN程序 说明&#xff1a;以下内容大部分来自腾讯元宝。 ​IDI_APPLICATION 与 IDC_ARROW 详解 ​1. IDC_ARROW&#xff08;光标资源标识符&#xff09;​ ​定义与…...

curl库+openssl库windows编译

一、工具准备 Visual Studio 2008&#xff1a;确保安装了 C 开发工具。 Git&#xff1a;用于克隆 cURL 的源码。 Perl&#xff1a;可以从 ActiveState Perl 下载并安装。 NASM&#xff08;可选&#xff09;&#xff1a;如果需要汇编优化&#xff0c;可以从NASM 官方网站 下载并…...

今日行情明日机会——20250321

后续投资机会分析 结合2025年3月21日盘面数据&#xff08;涨停56家&#xff0c;跌停31家&#xff09;&#xff0c;市场呈现结构性分化行情&#xff0c;海洋经济成为绝对主线&#xff0c;机器人概念局部活跃&#xff0c;人工智能表现较弱。以下是具体方向与策略建议&#xff1a…...

repo init 错误 Permission denied (publickey)

一、已经生成ssh-key并设置到gerrit上 二、已经设置.gitconfig &#xff08;此步骤是公司要求&#xff0c;设置gerrit地址为一个别名之类的&#xff0c;有的公司不需要&#xff09; 然后出现下面的错误&#xff0c;最后发现忘记设置git的用户名和邮箱 1. git config --globa…...

STM32 模拟SPI 模式0

SPI 模式 0 的时钟极性&#xff08;CPOL&#xff09;为 0&#xff0c;时钟相位&#xff08;CPHA&#xff09;为 0。CPOL 0 意味着时钟信号空闲时为低电平&#xff0c;CPHA 0 表示在时钟信号的第一个跳变沿&#xff08;上升沿&#xff09;进行数据采样。 #include "stm3…...

MySQL实现全量同步和增量同步到SQL Server或其他关系型库

在将MySQL中的两张表同步到SQL Server的过程中&#xff0c;全量同步和增量同步各有其优缺点。全量同步简单直接但可能耗时较长且资源消耗大&#xff0c;而增量同步则更加高效但需要额外的逻辑来处理数据的变更。以下是对这两种同步方式的详细解释及代码示例的完善。 完整代码示…...

详细解析GetOpenFileName()

书籍&#xff1a;《Visual C 2017从入门到精通》的2.3.8 Win32控件编程 环境&#xff1a;visual studio 2022 内容&#xff1a;【例2.34】打开文件对话框和另存为。 说明&#xff1a;以下内容大部分来自腾讯元宝。 GetOpenFileName() 是 Windows API 中用于显示标准文件打开…...

FPGA----完美解决Windows下[XSIM 43-3409][XSIM 43-3915]错误

大家好久不见&#xff0c;今天开始又要重操旧业了&#xff01;最近会更新很多关于petalinux的踩坑日记&#xff0c;敬请期待&#xff01; 先更新一个常见问题&#xff0c;使用Vivado仿真时C编译器报错问题。如下所示 ERROR: [XSIM 43-3409] Failed to compile generated C fi…...

LeetCode Hot100 刷题路线(Python版)

目录 1. LeetCode Hot100 刷题笔记&#xff08;1&#xff09;—— 哈希、双指针、滑动窗口 2. LeetCode Hot100 刷题笔记&#xff08;2&#xff09;—— 子串、普通数组、矩阵 3. LeetCode Hot100 刷题笔记&#xff08;3&#xff09;—— 链表 4. LeetCode Hot100 刷题笔记&…...

宇树科技纯技能要求总结

一、嵌入式开发与硬件设计 核心技能 嵌入式开发&#xff1a; 精通C/C&#xff0c;熟悉STM32、ARM开发熟悉Linux BSP开发及驱动框架&#xff08;SPI/UART/USB/FLASH/Camera/GPS/LCD&#xff09;掌握主流平台&#xff08;英伟达、全志、瑞芯微等&#xff09; 硬件设计&#xff1a…...