首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Android Compose 框架尺寸与密度深入剖析(五十五)

  • 25-04-25 00:21
  • 4328
  • 9501
blog.csdn.net

上一期:Android Compose 框架图像与矢量图深入剖析(五十四)

上一期:Android Compose 框架的布局检查深入剖析(五十六)

本人掘金号,欢迎点击关注:https://juejin.cn/user/4406498335701950

一、引言

在 Android 应用开发中,界面元素的尺寸与密度管理是至关重要的。合理的尺寸与密度处理能够确保应用在不同屏幕分辨率和像素密度的设备上都能呈现出一致且美观的用户界面。Android Compose 作为新一代的 Android UI 工具包,为开发者提供了一种声明式的方式来构建界面,其在尺寸与密度处理方面有着独特的设计和实现。

本技术博客将深入分析 Android Compose 框架中尺寸与密度的相关概念、实现原理以及源码细节。通过对源码的剖析,我们可以更好地理解 Compose 是如何处理尺寸和密度的,从而在实际开发中更加灵活和高效地运用这些特性。

二、基本概念

2.1 尺寸(Size)

在 Android Compose 中,尺寸用于描述界面元素的大小,通常用宽度和高度来表示。尺寸可以是固定的数值,也可以是根据布局约束动态计算得到的。例如,一个按钮的尺寸可以是 200.dp 宽和 50.dp 高,也可以根据父容器的大小进行自适应调整。

2.2 密度(Density)

密度是指屏幕上每英寸所包含的像素数量(PPI,Pixels Per Inch)。不同的设备具有不同的屏幕密度,常见的屏幕密度有低密度(LDPI)、中密度(MDPI)、高密度(HDPI)、超高密度(XHDPI)等。在 Android 开发中,为了确保界面元素在不同密度的屏幕上显示效果一致,需要进行密度适配。

2.3 尺寸单位

在 Android Compose 中,常用的尺寸单位有:

  • dp(Density-independent Pixels) :密度无关像素,是一种基于屏幕密度的抽象单位。1dp 在不同密度的屏幕上代表的物理尺寸大致相同,常用于设置界面元素的大小。
  • sp(Scaled Pixels) :缩放像素,主要用于设置字体大小。与 dp 类似,sp 也是密度无关的,但会根据用户的字体大小设置进行缩放。
  • px(Pixels) :像素,是屏幕上的最小显示单位。在 Compose 中,一般不直接使用 px 作为尺寸单位,因为它会受到屏幕密度的影响,不利于跨设备的适配。

三、尺寸的表示与计算

3.1 Dp 类型

在 Android Compose 中,Dp 是表示尺寸的基本类型。它是一个数据类,定义如下:

kotlin

// Dp 数据类,用于表示密度无关像素
@JvmInline
value class Dp internal constructor(val value: Float) {
    // 重写 toString 方法,方便调试和日志输出
    override fun toString(): String = "${value}dp"
    // 定义一些常用的运算符重载,方便进行尺寸的计算
    operator fun plus(other: Dp): Dp = Dp(value + other.value)
    operator fun minus(other: Dp): Dp = Dp(value - other.value)
    operator fun times(factor: Float): Dp = Dp(value * factor)
    operator fun div(factor: Float): Dp = Dp(value / factor)
    operator fun unaryMinus(): Dp = Dp(-value)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从源码可以看出,Dp 是一个值类(value class),它内部封装了一个 Float 类型的 value,表示密度无关像素的值。同时,它还提供了一些运算符重载方法,方便进行尺寸的加减乘除运算。

3.2 尺寸的创建

在 Compose 中,可以使用扩展函数来创建 Dp 类型的尺寸值。例如:

kotlin

// 扩展函数,将 Float 类型的值转换为 Dp 类型
val Float.dp: Dp
    get() = Dp(this)
// 扩展函数,将 Int 类型的值转换为 Dp 类型
val Int.dp: Dp
    get() = Dp(toFloat())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用这些扩展函数,我们可以很方便地创建 Dp 类型的尺寸值,例如:

kotlin

val width = 200.dp
val height = 50.dp
  • 1
  • 2

3.3 尺寸的测量与布局

在 Compose 中,界面元素的尺寸是通过测量和布局过程来确定的。每个可组合函数都可以通过 Modifier 来设置自身的尺寸,例如:

kotlin

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color

@Composable
fun SizedBox() {
    // 使用 Box 组件作为示例
    Box(
        // 使用 Modifier.size 设置 Box 的尺寸
        modifier = Modifier
           .size(200.dp, 50.dp) 
           .background(Color.Blue) 
    )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在上述代码中,Modifier.size 方法用于设置 Box 组件的宽度和高度。Modifier.size 方法的源码如下:

kotlin

// Modifier.size 方法,用于设置组件的尺寸
fun Modifier.size(size: Dp): Modifier = size(size, size)
fun Modifier.size(width: Dp, height: Dp): Modifier = this.then(
    SizeModifier(
        width = width,
        height = height,
        widthScale = 1f,
        heightScale = 1f
    )
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

从源码可以看出,Modifier.size 方法最终会创建一个 SizeModifier 对象,并将其添加到 Modifier 链中。SizeModifier 类的源码如下:

kotlin

// SizeModifier 类,用于设置组件的尺寸
private class SizeModifier(
    private val width: Dp,
    private val height: Dp,
    private val widthScale: Float,
    private val heightScale: Float
) : LayoutModifier {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // 将 Dp 类型的宽度和高度转换为像素值
        val widthPx = with(LayoutDirectionLtr) { width.toPx() }
        val heightPx = with(LayoutDirectionLtr) { height.toPx() }
        // 根据转换后的像素值创建新的约束条件
        val newConstraints = Constraints.fixed(widthPx.roundToInt(), heightPx.roundToInt())
        // 使用新的约束条件测量可组合元素
        val placeable = measurable.measure(newConstraints)
        return layout(placeable.width, placeable.height) {
            // 放置可组合元素
            placeable.placeRelative(0, 0)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

在 SizeModifier 的 measure 方法中,首先将 Dp 类型的宽度和高度转换为像素值,然后根据这些像素值创建新的约束条件,最后使用新的约束条件测量可组合元素并进行布局。

3.4 尺寸计算的架构图

Composable Function
Modifier.size
SizeModifier
MeasureScope.measure
Dp.toPx
Constraints.fixed
Measurable.measure
Layout

这个架构图展示了尺寸计算的主要流程:可组合函数调用 Modifier.size 方法,该方法创建 SizeModifier 对象,SizeModifier 在 measure 方法中进行尺寸转换和约束条件的创建,然后使用新的约束条件测量可组合元素,最后进行布局。

四、密度的处理

4.1 Density 类

在 Android Compose 中,Density 类用于表示屏幕的密度信息。它包含了屏幕的密度、字体缩放比例等信息,定义如下:

kotlin

// Density 类,用于表示屏幕的密度信息
data class Density(
    // 屏幕的密度,即每英寸的像素数量
    val density: Float,
    // 字体的缩放比例
    val fontScale: Float
) {
    // 将 Dp 类型的值转换为像素值
    fun Dp.toPx(): Float = value * density
    // 将像素值转换为 Dp 类型的值
    fun Float.toDp(): Dp = Dp(this / density)
    // 将像素值转换为 Dp 类型的值
    fun Int.toDp(): Dp = Dp(toFloat() / density)
    // 将 Sp 类型的值转换为像素值
    fun Sp.toPx(): Float = value * density * fontScale
    // 将像素值转换为 Sp 类型的值
    fun Float.toSp(): Sp = Sp(this / (density * fontScale))
    // 将像素值转换为 Sp 类型的值
    fun Int.toSp(): Sp = Sp(toFloat() / (density * fontScale))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

从源码可以看出,Density 类提供了一些扩展函数,用于在 Dp、Sp 和像素值之间进行转换。这些转换函数是基于屏幕的密度和字体缩放比例进行计算的。

4.2 密度的获取

在 Compose 中,可以通过 LocalDensity 来获取当前的 Density 对象。LocalDensity 是一个组合状态,定义如下:

kotlin

// LocalDensity 组合状态,用于获取当前的 Density 对象
val LocalDensity = staticCompositionLocalOf { error("No Density provided") }
  • 1
  • 2

在实际使用中,可以通过 with(LocalDensity.current) 来获取当前的 Density 对象,并进行尺寸转换。例如:

kotlin

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalDensity

@Composable
fun DensityExample() {
    // 获取当前的 Density 对象
    val density = LocalDensity.current
    // 将 Dp 类型的值转换为像素值
    val widthInPx = with(density) { 200.dp.toPx() }
    // 将像素值转换为 Dp 类型的值
    val heightInDp = with(density) { 100f.toDp() }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4.3 密度适配的原理

在 Android Compose 中,密度适配的核心原理是通过 Density 类进行尺寸转换。当我们使用 Dp 或 Sp 作为尺寸单位时,Compose 会在测量和布局过程中根据当前的 Density 对象将这些单位转换为像素值。例如,在 SizeModifier 的 measure 方法中,会调用 Dp.toPx 方法将 Dp 类型的尺寸转换为像素值,然后使用这些像素值进行布局。

4.4 密度处理的架构图

Composable Function
LocalDensity.current
Density
Dp.toPx
Float.toDp
MeasureScope.measure
MeasureScope.measure
Layout

这个架构图展示了密度处理的主要流程:可组合函数通过 LocalDensity.current 获取当前的 Density 对象,然后使用 Density 对象提供的转换函数在 Dp 和像素值之间进行转换,最后在测量和布局过程中使用转换后的像素值进行布局。

五、响应式布局与尺寸适配

5.1 响应式布局的概念

响应式布局是指界面能够根据不同的屏幕尺寸和设备方向自动调整布局和元素的大小,以提供一致的用户体验。在 Android Compose 中,可以通过使用 Modifier 和布局组件来实现响应式布局。

5.2 使用 Modifier 进行尺寸适配

在 Compose 中,可以使用 Modifier 的一些方法来实现尺寸适配。例如,Modifier.fillMaxWidth 和 Modifier.fillMaxHeight 方法可以让组件填充父容器的宽度或高度。

kotlin

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color

@Composable
fun ResponsiveBox() {
    // 使用 Box 组件作为示例
    Box(
        // 使用 Modifier.fillMaxWidth 填充父容器的宽度
        modifier = Modifier
           .fillMaxWidth() 
           .height(50.dp) 
           .background(Color.Green) 
    )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Modifier.fillMaxWidth 方法的源码如下:

kotlin

// Modifier.fillMaxWidth 方法,用于让组件填充父容器的宽度
fun Modifier.fillMaxWidth(fraction: Float = 1f): Modifier = this.then(
    FillMaxWidthModifier(fraction)
)
  • 1
  • 2
  • 3
  • 4

FillMaxWidthModifier 类的源码如下:

kotlin

// FillMaxWidthModifier 类,用于让组件填充父容器的宽度
private class FillMaxWidthModifier(
    private val fraction: Float
) : LayoutModifier {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // 根据 fraction 计算新的宽度
        val width = (constraints.maxWidth * fraction).roundToInt()
        // 创建新的约束条件
        val newConstraints = constraints.copy(minWidth = width, maxWidth = width)
        // 使用新的约束条件测量可组合元素
        val placeable = measurable.measure(newConstraints)
        return layout(placeable.width, placeable.height) {
            // 放置可组合元素
            placeable.placeRelative(0, 0)
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

从源码可以看出,FillMaxWidthModifier 在 measure 方法中根据 fraction 计算新的宽度,然后创建新的约束条件,最后使用新的约束条件测量可组合元素并进行布局。

5.3 使用布局组件进行响应式布局

Compose 提供了一些布局组件,如 Row、Column、Box 等,可以用于实现响应式布局。例如,Column 组件可以根据子组件的大小自动调整布局:

kotlin

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color

@Composable
fun ResponsiveColumn() {
    // 使用 Column 组件作为示例
    Column {
        // 第一个 Box 组件
        Box(
            modifier = Modifier
               .width(200.dp) 
               .height(50.dp) 
               .background(Color.Red) 
        )
        // 第二个 Box 组件
        Box(
            modifier = Modifier
               .width(150.dp) 
               .height(30.dp) 
               .background(Color.Yellow) 
        )
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

Column 组件的布局逻辑是由 ColumnLayout 类实现的,其源码如下:

kotlin

// ColumnLayout 类,用于实现 Column 组件的布局逻辑
private class ColumnLayout(
    modifier: Modifier,
    verticalArrangement: Arrangement.Vertical,
    horizontalAlignment: Alignment.Horizontal
) : LayoutModifier {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // 测量所有子组件
        val placeables = measurable.measureChildren(constraints)
        // 计算布局的总高度
        var height = 0
        placeables.forEach { placeable ->
            height += placeable.height
        }
        // 计算布局的最大宽度
        var maxWidth = 0
        placeables.forEach { placeable ->
            maxWidth = maxOf(maxWidth, placeable.width)
        }
        return layout(maxWidth, height) {
            // 放置所有子组件
            var y = 0
            placeables.forEach { placeable ->
                placeable.placeRelative(0, y)
                y += placeable.height
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

从源码可以看出,ColumnLayout 在 measure 方法中测量所有子组件,计算布局的总高度和最大宽度,然后根据这些信息进行布局。

5.4 响应式布局的架构图

Composable Function
Modifier.fillMaxWidth
Column
FillMaxWidthModifier
ColumnLayout
MeasureScope.measure
Layout

这个架构图展示了响应式布局的主要流程:可组合函数可以使用 Modifier.fillMaxWidth 或 Column 等布局组件,这些方法或组件会创建相应的 Modifier 或布局类,然后在 measure 方法中进行尺寸计算和布局,最后进行布局操作。

六、字体尺寸与密度

6.1 字体尺寸单位 Sp

在 Android Compose 中,Sp 是用于表示字体尺寸的单位。它与 Dp 类似,但会根据用户的字体大小设置进行缩放。Sp 是一个数据类,定义如下:

kotlin

// Sp 数据类,用于表示缩放像素
@JvmInline
value class Sp internal constructor(val value: Float) {
    // 重写 toString 方法,方便调试和日志输出
    override fun toString(): String = "${value}sp"
    // 定义一些常用的运算符重载,方便进行字体尺寸的计算
    operator fun plus(other: Sp): Sp = Sp(value + other.value)
    operator fun minus(other: Sp): Sp = Sp(value - other.value)
    operator fun times(factor: Float): Sp = Sp(value * factor)
    operator fun div(factor: Float): Sp = Sp(value / factor)
    operator fun unaryMinus(): Sp = Sp(-value)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从源码可以看出,Sp 是一个值类,内部封装了一个 Float 类型的 value,表示缩放像素的值。同时,它也提供了一些运算符重载方法,方便进行字体尺寸的计算。

6.2 字体尺寸的设置

在 Compose 中,可以使用 Text 组件来显示文本,并通过 fontSize 参数设置字体大小。例如:

kotlin

import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp

@Composable
fun TextWithFontSize() {
    // 使用 Text 组件显示文本,并设置字体大小
    Text(
        text = "Hello, Compose!",
        style = TextStyle(fontSize = 20.sp) 
    )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

TextStyle 类的 fontSize 属性是 TextUnit 类型,sp 扩展函数会将 Int 或 Float 类型的值转换为 TextUnit.Sp 类型。sp 扩展函数的源码如下:

kotlin

// 扩展函数,将 Float 类型的值转换为 TextUnit.Sp 类型
val Float.sp: TextUnit
    get() = TextUnit.Sp(this)
// 扩展函数,将 Int 类型的值转换为 TextUnit.Sp 类型
val Int.sp: TextUnit
    get() = TextUnit.Sp(toFloat())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6.3 字体尺寸与密度的关系

字体尺寸与密度的关系是通过 Density 类来处理的。在 Density 类中,提供了 Sp.toPx 和 Float.toSp 等扩展函数,用于在 Sp 和像素值之间进行转换。例如,在绘制文本时,Compose 会根据当前的 Density 对象将 Sp 类型的字体尺寸转换为像素值,然后进行绘制。

6.4 字体尺寸处理的架构图

Composable Function
Text
TextStyle.fontSize
TextUnit.Sp
Density.Sp.toPx
TextDrawing

这个架构图展示了字体尺寸处理的主要流程:可组合函数使用 Text 组件显示文本,设置 TextStyle 的 fontSize 属性为 TextUnit.Sp 类型,然后在绘制文本时,根据当前的 Density 对象将 Sp 类型的字体尺寸转换为像素值,最后进行文本绘制。

七、源码中的尺寸与密度管理

7.1 Constraints 类

Constraints 类用于表示布局过程中的约束条件,包括最小宽度、最大宽度、最小高度和最大高度。它在尺寸计算和布局过程中起着重要的作用。Constraints 类的定义如下:

kotlin

// Constraints 类,用于表示布局过程中的约束条件
data class Constraints(
    // 最小宽度
    val minWidth: Int,
    // 最大宽度
    val maxWidth: Int,
    // 最小高度
    val minHeight: Int,
    // 最大高度
    val maxHeight: Int
) {
    // 判断是否是固定宽度的约束条件
    val hasFixedWidth: Boolean get() = minWidth == maxWidth
    // 判断是否是固定高度的约束条件
    val hasFixedHeight: Boolean get() = minHeight == maxHeight
    // 创建一个固定宽度和高度的约束条件
    companion object {
        fun fixed(width: Int, height: Int): Constraints =
            Constraints(minWidth = width, maxWidth = width, minHeight = height, maxHeight = height)
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

从源码可以看出,Constraints 类封装了布局过程中的约束信息,并提供了一些属性和方法来方便使用。在 MeasureScope.measure 方法中,会根据 Constraints 对象来测量可组合元素的大小。

7.2 MeasureScope 类

MeasureScope 类是一个接口,定义了测量可组合元素的上下文。它提供了一些方法和属性,用于在测量过程中进行尺寸转换和约束条件的处理。MeasureScope 类的部分定义如下:

kotlin

// MeasureScope 接口,定义了测量可组合元素的上下文
interface MeasureScope {
    // 当前的布局方向
    val layoutDirection: LayoutDirection
    // 将 Dp 类型的值转换为像素值
    fun Dp.toPx(): Float
    // 将像素值转换为 Dp 类型的值
    fun Float.toDp(): Dp
    // 将像素值转换为 Dp 类型的值
    fun Int.toDp(): Dp
    // 测量可组合元素
    fun Measurable.measure(constraints: Constraints): Placeable
    // 测量所有子可组合元素
    fun Measurable.measureChildren(constraints: Constraints): List<Placeable>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

从源码可以看出,MeasureScope 接口提供了尺寸转换方法和测量方法,在 Modifier 的 measure 方法中会使用这些方法进行尺寸计算和测量。

7.3 Placeable 类

Placeable 类表示一个已经测量好的可组合元素,它包含了元素的宽度、高度和位置信息。Placeable 类的部分定义如下:

kotlin

// Placeable 类,表示一个已经测量好的可组合元素
interface Placeable {
    // 元素的宽度
    val width: Int
    // 元素的高度
    val height: Int
    // 放置元素的方法
    fun placeRelative(x: Int, y: Int)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在布局过程中,MeasureScope.measure 方法会返回一个 Placeable 对象,然后在 layout 方法中调用 Placeable.placeRelative 方法来放置元素。

7.4 源码中尺寸与密度管理的架构图

Composable Function
Modifier
MeasureScope.measure
Constraints
Density
Measurable.measure
Dp.toPx
Placeable
Layout

这个架构图展示了源码中尺寸与密度管理的主要流程:可组合函数使用 Modifier 进行尺寸设置,在 MeasureScope.measure 方法中,会使用 Constraints 对象和 Density 对象进行尺寸计算和测量,测量结果会返回一个 Placeable 对象,最后在 Layout 中进行元素的放置。

Android小码峰
微信公众号
专门提供Android,AI领域的知识
注:本文转载自blog.csdn.net的Android 小码蜂的文章"https://blog.csdn.net/qq_28540861/article/details/146995706"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top