【Android-JetpackCompose】6、开发环境:Compose 工具、Android Studio 操作、Kotlin 语法糖

使用Jetpack Compose 需在 build.gradle(app) 添加如下依赖项:

dependencies {
    
    
    implementation("androidx.compose.ui:ui:1.2.1")
    // Tooling support (Previews, etc.)
    implementation("androidx.compose.ui:ui-tooling:1.2.1")
    implementation("androidx.compose.ui:ui-tooling-preview:1.2.1")
    // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
    implementation("androidx.compose.foundation:foundation:1.2.1")
    // Material Design
    implementation("androidx.compose.material:material:1.2.1")
    // Material design icons
    implementation("androidx.compose.material:material-icons-core:1.2.1")
    implementation("androidx.compose.material:material-icons-extended:1.2.1")
    // Integration with observables
    implementation("androidx.compose.runtime:runtime-livedata:1.2.1")
    implementation("androidx.compose.runtime:runtime-rxjava2:1.2.1")

    // UI Tests
    androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.2.1")
}

一、预览

以通过读取 LocalInspectionMode 可确认可组合项是否正在预览中呈现,代码如下:

if (LocalInspectionMode.current) {
    
    
    // Show this text in a preview window:
    Text("Hello preview user!")
} else {
    
    
    // Show this text in the app:
    Text("Hello $name!")
}

1.1 互动模式

预览互动模式直接在 Android Studio 中运行,并未运行模拟器,因此存在一些限制:

  • 无法访问网络
  • 无法访问文件
  • 有些 Context API 不一定完全可用

在这里插入图片描述

1.2 Multipreview

使用 Multipreview 时,您可以定义一个注解类,该类本身可具有多个采用不同配置的 @Preview 注解。将此注解添加到一个可组合函数后,系统会自动同时呈现所有不同的预览。例如,您可以使用此注解同时预览多个设备、字体大小或主题,而无需为每一个可组合项重复这些定义。

首先,创建您自己的自定义注解类:

@Preview(
    name = "small font",
    group = "font scales",
    fontScale = 0.5f
)
@Preview(
    name = "large font",
    group = "font scales",
    fontScale = 1.5f
)
annotation class FontScalePreviews

可以对预览可组合项使用此自定义注解:

@FontScalePreviews
@Composable
fun HelloWorldPreview() {
    
    
    Text("Hello World")
}

预览后,可看到两种自定义的效果,效果如下:

在这里插入图片描述

可以将多个 MultiPreview 注解和普通 preview 注解结合使用,从而创建一个更完整的预览集。结合使用 MultiPreview 注解并不意味着所有不同的组合都会得以呈现。实际上,每个 MultiPreview 注解会独立运行,并且仅会呈现自己的变体。

@Preview(
    name = "dark theme",
    group = "themes",
    uiMode = UI_MODE_NIGHT_YES
)
@FontScalePreviews
@DevicePreviews
annotation class CombinedPreviews

@CombinedPreviews
@Composable
fun HelloWorldPreview() {
    
    
    MyTheme {
    
     Surface {
    
     Text("Hello world") } }
}

在这里插入图片描述

通过右键点击呈现的每个预览,即可将其作为图像来复制,效果如下:

在这里插入图片描述

如需在预览中显示状态栏和操作栏,请添加 showSystemUi 参数,效果如下:

在这里插入图片描述

1.3 @PreviewParameter

可以使用 @PreviewParameter 注解来添加参数,以将示例数据传递给某个可组合项预览函数。

@Preview
@Composable
fun UserProfilePreview(
    @PreviewParameter(UserPreviewParameterProvider::class) user: User
) {
    
    
    UserProfile(user)
}

// 如需提供示例数据,请创建一个可实现 PreviewParameterProvider 并以序列形式返回示例数据的类。
class UserPreviewParameterProvider : PreviewParameterProvider<User> {
    
    
    override val values = sequenceOf(
        User("Elise"),
        User("Frank"),
        User("Julia")
    )
}

预览后,序列中的每个数据元素都会呈现一个预览,效果如下:

在这里插入图片描述

可以为多个预览使用相同的提供程序类。如有必要,可通过设置 limit 参数来限制呈现的预览数量。

@Preview
@Composable
fun UserProfilePreview(
    @PreviewParameter(UserPreviewParameterProvider::class, limit = 2) user: User
) {
    
    
    UserProfile(user)
}

1.4 uiMode

参数 uiMode 可接受任意 Configuration.UI_* 常量,并允许您相应地更改预览的行为。例如,您可以将预览设置为夜间模式,看看主题如何响应。

在这里插入图片描述

二、Android Studio 操作

Android Studio 添加了下面这些与 Compose 相关的缩写,可以提升编码效率:

  • comp,用于设置 @Composable 函数
  • prev,用于创建 @Preview 可组合函数
  • paddp,用于以 dp 为单位添加 padding 修饰符
  • weight,用于添加 weight 修饰符
  • W、WR、WC,用于通过 Box、Row 或 Column 容器设置当前可组合项的呈现效果

图像资源选择器
无论是在可组合项内部还是外部定义可绘制对象、矢量或图像,边线上都会显示其预览。您可以点击它,通过图像资源选择器进行更改,如下所示:

在这里插入图片描述

通过“Live Edit of Literals”界面指示器启用字面量修饰功能,无需进行编译即可查看触发实时更新的常量字面量:

在这里插入图片描述

Apply Changes 可更新代码和资源,而不必将应用重新部署到模拟器或实体设备上。每当添加、修改或删除可组合项时,只需点击一下此按钮,即可更新应用,而不必重新部署:

在这里插入图片描述

2.1 重组次数

您可以使用布局检查器检查某个可组合项的重组频率或跳过重组的频率。如果界面呈现效果不佳,通常是因为出现编码错误,导致界面过度重组。另一方面,某些编码错误可能会在界面需要重组时阻止其重组,这意味着界面更改将不会呈现在屏幕上。跟踪重组有助于发现这两类问题。在视图选项中启用 Show Recomposition Counts 即可开启此功能,效果如下:

在这里插入图片描述

启用后,布局检查器会在左侧显示重组次数,在右侧显示跳过重组的次数:

在这里插入图片描述

2.2 动画

Android Studio 允许您从动画预览中检查动画。如果您在可组合项预览中描述了动画效果,则可以检查每个动画值在给定时间点的确切值,并且可以暂停、循环播放、快进或放慢动画,以便在动画过渡过程中调试动画:

在这里插入图片描述

有些功能只有在 Android Studio 偏好设置的“Experimental”部分中手动启用后才能使用:File > Settings > Experimental(在 Mac 上:Android Studio > Preferences > Experimental)。

在这里插入图片描述

三、Compose 的 Kotlin 语法糖

3.1 尾随 lambda

Kotlin 提供了一种特殊语法来调用最后一个参数为 lambda 的高阶函数。如果您要将一个 lambda 表达式作为该参数传递,您可以使用尾随 lambda 语法。您应将 lambda 表达式放在圆括号后面,而不是将其放在圆括号内。这是 Compose 中的一种常见情况,因此您需要熟悉代码是什么样子的。

例如,所有布局的最后一个参数(如 Column() 可组合函数)均为 content,它是一个发出子界面元素的函数。假设您想要创建一个包含三个文本元素的列,并且需要应用某种格式设置。以下代码行得通,但它非常繁琐:

Column(
    modifier = Modifier.padding(16.dp),
    content = {
    
    
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
)

由于 content 参数是函数签名中的最后一个参数,并且我们要将其值作为 lambda 表达式传递,因此我们可以将其从圆括号中取出:

Column(modifier = Modifier.padding(16.dp)) {
    
    
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

这两个示例的含义完全相同。大括号定义传递给 content 参数的 lambda 表达式。

事实上,如果您要传递的唯一一个参数是该尾随 lambda,也就是说,如果最后一个参数是 lambda,并且您不会传递其他任何参数,则您可以完全省略圆括号。例如,假设您不需要将修饰符传递给 Column。您可以像下面这样编写代码:

Column {
    
    
    Text("Some text")
    Text("Some more text")
    Text("Last text")
}

此语法在 Compose 中十分常见,尤其是对于诸如 Column 之类的布局元素。最后一个参数是一个 lambda 表达式,它用于定义元素的子元素,这些子元素在函数调用后在大括号中指定。

3.2 类型安全构建器和 DSL

Kotlin 允许使用类型安全构建器创建领域特定语言 (DSL)。使用 DSL,您能够以一种更易维护且可读性更高的方式构建复杂的分层数据结构。

Jetpack Compose 使用 DSL 来实现某些 API(例如 LazyRow 和 LazyColumn)。

@Composable
fun MessageList(messages: List<Message>) {
    
    
    LazyColumn {
    
    
        // Add a single item as a header
        item {
    
    
            Text("Message List")
        }

        // Add list of messages
        items(messages) {
    
     message ->
            Message(message)
        }
    }
}

Kotlin 使用具有接收器的函数字面量来确保创建类型安全的构建器。我们以 Canvas 可组合项为例,它将一个以 DrawScope 为接收器的函数 (onDraw: DrawScope.() -> Unit) 作为参数,从而允许代码块调用 DrawScope 中定义的成员函数。

Canvas(Modifier.size(120.dp)) {
    
    
    // Draw grey background, drawRect function is provided by the receiver
    drawRect(color = Color.Gray)

    // Inset content by 10 pixels on the left/right sides
    // and 12 by the top/bottom
    inset(10.0f, 12.0f) {
    
    
        val quadrantSize = size / 2.0f

        // Draw a rectangle within the inset bounds
        drawRect(
            size = quadrantSize,
            color = Color.Red
        )

        rotate(45.0f) {
    
    
            drawRect(size = quadrantSize, color = Color.Blue)
        }
    }
}

猜你喜欢

转载自blog.csdn.net/jiaoyangwm/article/details/127186080