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

Compose 插槽 API 简介、实战

如今已对 “可组合函数是什么” 以及 “如何创建可组合函数” 有了更清晰的认识,接下来该探索支持插槽 API(Slot API) 的可组合函数了。本章将讲解插槽 API 的定义、用途,以及如何在自定义可组合函数中加入插槽,同时还会介绍一些支持插槽 API 的内置可组合函数。

理解插槽 API

可组合函数中可以调用一个或多个其他可组合函数。这代表一个可组合函数的内容是 “预定义” 的,取决于它调用了哪些其他可组合函数,进而决定了它显示的内容。

以如下函数为例,它包含一个 Column 和三个 Text 组件:

@Composable
fun SlotDemo() {Column {Text("Top Text")Text("Middle Text")Text("Bottom Text")}
}

通过修改该函数能实现传入参数来指定要显示的文本内容,甚至文本的颜色和字体大小。但无论如何修改,这个函数始终只能显示三个 Text 组件的 Column。

插槽 API 案例示例

假设我们需要在 Column 中显示三个元素,但直到调用该可组合函数前,都不知道中间位置要放哪个可组合函数。那么在当前代码形式下,中间位置只能显示预先声明的 Text 组件,无法替换为其他内容。

解决方案是将中间位置的可组合函数开放为一个插槽 ,调用该函数时,可在这个插槽中放入任意其他可组合函数。这种为可组合函数提供插槽的设计,就称为 “提供插槽 API”。

“API” 是 “应用程序编程接口(Application Programming Interface)” 的缩写,在此语境下,它意味着我们为可组合函数添加了一个 “编程接口”,调用者可通过这个接口指定要在插槽中显示的可组合函数。

实际上一个可组合函数可以为调用者提供多个插槽。例如在上述函数中,若有需求,所有 Text 组件的位置都可以声明为插槽。

定义插槽 API

插槽 API 就是将参数定义为接收其它可组合函数,调用者传入不同的可组合函数实现灵活 UI 定制。

步骤 1:声明接收插槽的参数。第一步是指定可以接收一个插槽作为参数。本质上,这相当于声明 “该可组合函数接受其他可组合函数作为参数”。

以我们的示例 SlotDemo 可组合函数为例,需按如下方式修改函数签名:

@Composable
fun SlotDemo(middleContent: @Composable () -> Unit) {// ...(函数体)
}

调用 SlotDemo 可组合函数时,需要传入一个可组合函数作为参数。注意,这里声明的参数类型是 “返回 Unit 的可组合函数”:

  • Unit 是 Kotlin 中的一种类型,表示函数没有返回值,相当于其他语言中的 void。
  • 参数名 middleContent 用于描述该插槽的作用,也可以是任何合法的名称,只要能清晰标识插槽用途并便于在函数体内引用即可。

步骤 2:在布局中插入插槽。对该可组合函数的最后一处修改,是将 middleContent 组件替换到 Column 声明中,代码如下:

@Composable
fun SlotDemo(middleContent: @Composable () -> Unit) {Column {Text("Top Text")middleContent() // 插入插槽内容Text("Bottom Text")}
}

至此,我们已成功为 SlotDemo 可组合函数声明了一个插槽 API。

调用插槽 API 可组合函数

接下来需要学习如何使用为 SlotDemo 可组合函数配置的插槽 API。只需在调用 SlotDemo 函数时,将一个可组合函数作为参数传入即可。

假设我们需要让如下可组合函数显示在 middleContent 插槽中:

@Composable
fun ButtonDemo() {Button(onClick = { }) {Text("Click Me")}
}

此时,我们可以按如下方式调用 SlotDemo 可组合函数:

SlotDemo(middleContent = { ButtonDemo() })

这种语法虽然可行,但如果可组合函数有多个插槽需要填充,代码会很快变得杂乱。更简洁的写法如下:

SlotDemo {ButtonDemo()
}

当然,插槽 API 并不局限于单个插槽。SlotDemo 示例可以完全由插槽组成,代码如下:

@Composable
fun SlotDemo(topContent: @Composable () -> Unit,middleContent: @Composable () -> Unit,bottomContent: @Composable () -> Unit
) {Column {topContent()middleContent()bottomContent()}
}

修改后,调用 SlotDemo 的代码可以写成:

SlotDemo(topContent = { Text("Top Text") },middleContent = { ButtonDemo() },bottomContent = { Text("Bottom Text") }
)

和单个插槽的情况一样,为了清晰起见,也可以简写为:

SlotDemo({ Text("Top Text") },{ ButtonDemo() },{ Text("Bottom Text") }
)

项目实战

项目简介,界面将包含 “标题”、“进度指示器” 和 “两个复选框” 三个部分,各部分功能与插槽设计如下:

  • 复选框的作用:用于控制两个显示逻辑
    • 控制标题的呈现形式:是显示为文本还是图片。
    • 控制进度指示器的类型:是显示圆形进度条(CircularProgressIndicator)还是线性进度条(LinearProgressIndicator)。
  • 插槽的设计:将 “标题” 和 “进度指示器” 均声明为插槽
    • 标题插槽:可填充 Text 或 Image 可组合项。
    • 进度指示器插槽:可填充 LinearProgressIndicator 或 CircularProgressIndicator 组件。

启动 Android Studio,选择 “Empty Activity” 模板,项目名为 “SlotApiDemo”,并指定包名为“com.example.slotapidemo”,将 “Minimum API level” 设置修改为 “API 26: Android 8.0 (Oreo)”,完成后删除 Greeting 和 GreetingPreview 函数。

添加 MainScreen 可组合函数

编辑 MainActivity 类的 onCreate 方法,在 Surface 组件中调用名为 MainScreen 的可组合函数:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContent {SlotApiDemoTheme {Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->// 调用 MainScreen 并传入内边距修饰符MainScreen(modifier = Modifier.padding(innerPadding))}}}
}

MainScreen 将包含两个 Checkbox 组件的状态和事件处理器。

@Composable
fun MainScreen(modifier: Modifier = Modifier) {// 声明两个状态变量,分别对应两个复选框,初始值均为 truevar linearSelected by remember { mutableStateOf(true) }var imageSelected by remember { mutableStateOf(true) }// 线性进度条复选框的事件处理器:更新 linearSelected 状态val onLinearClick = { value: Boolean ->linearSelected = value}// 标题类型复选框的事件处理器:更新 imageSelected 状态val onTitleClick = { value: Boolean ->imageSelected = value}
}

添加 ScreenContent 可组合函数

MainScreen 函数调用 ScreenContent 时,需要向其传递状态变量和事件处理器。ScreenContent 的初始声明如下:

@Composable
fun ScreenContent(linearSelected: Boolean,imageSelected: Boolean,onTitleClick: (Boolean) -> Unit,onLinearClick: (Boolean) -> Unit
) {Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally, // 水平居中对齐子组件verticalArrangement = Arrangement.SpaceBetween     // 垂直方向均匀分布子组件(首尾无额外间距)) {// 后续将添加内容}
}

顾名思义,ScreenContent 可组合函数负责显示屏幕内容,包括标题、进度指示器和复选框。为了承载这些内容,我们调用了 Column 可组合函数,并做了如下配置:

  • 子组件沿水平轴居中对齐;
  • 垂直方向使用 SpaceBetween 排列方式 —— 这会让子组件在垂直方向均匀分布,但第一个子组件上方和最后一个子组件下方不会留出额外间距。

ScreenContent 将要调用的子可组合函数中,有一个负责渲染两个 Checkbox 组件。虽然可以直接将复选框添加到 Column 中,但更好的做法是将它们放在一个独立的可组合函数中,再由 ScreenContent 调用。

新建 Checkbox 可组合函数

包含复选框的可组合函数将由一个 Row 组件构成,其中包含两个 Checkbox 实例。此外,每个 Checkbox 的左侧会放置 Text 可组合项,两个 “文本 + 复选框” 组合之间用 Spacer 分隔。

调用该复选框可组合函数(CheckBoxes)时,需要传入两个状态变量(用于确保复选框显示当前状态),以及 onLinearClick 和 onTitleClick 事件处理器的引用(这两个处理器将赋值给两个 Checkbox 组件的 onCheckChange 属性)。

在 MainActivity.kt 文件中,添加 CheckBoxes 可组合函数,代码如下:

@Composable
fun CheckBoxes(linearSelected: Boolean,imageSelected: Boolean,onTitleClick: (Boolean) -> Unit,onLinearClick: (Boolean) -> Unit
) {Row(Modifier.padding(50.dp), // 整体添加 50dp 内边距verticalAlignment = Alignment.CenterVertically // 子组件垂直居中对齐) {// 第一个复选框:控制标题类型(图像/文本)Checkbox(checked = imageSelected, // 绑定 imageSelected 状态onCheckedChange = onTitleClick // 绑定标题切换事件处理器)Text("Image Title") // 复选框说明文本Spacer(Modifier.width(20.dp)) // 两个复选框之间的水平间隔// 第二个复选框:控制进度条类型(线性/圆形)Checkbox(checked = linearSelected, // 绑定 linearSelected 状态onCheckedChange = onLinearClick // 绑定进度条切换事件处理器)Text("Linear Progress") // 复选框说明文本}
}

如果想在继续之前预览该可组合函数,可以添加如下预览声明,然后点击预览面板中的 “Build & Refresh”(构建并刷新)链接:

@Preview
@Composable
fun DemoPreview() {CheckBoxes(linearSelected = true,imageSelected = true,onTitleClick = { /* 暂不实现 */ }, // 占位 lambda,无实际操作onLinearClick = { /* 暂不实现 */ })
}

在上述预览函数中调用 CheckBoxes 时,我们将两个状态属性设为 true,并为事件回调赋值了 “空实现的 lambda 表达式”(仅作为占位)。
刷新预览后,布局应与下图所示一致。

插槽 API 案例

实现 ScreenContent 插槽 API

我们已添加包含两个复选框的可组合函数,现在可在 ScreenContent 内部的 Column 中调用它。由于状态变量和事件处理器已传入 ScreenContent,调用 CheckBoxes 时直接传递这些参数即可。找到 ScreenContent 可组合函数,按如下方式修改:

@Composable
fun ScreenContent(linearSelected: Boolean,imageSelected: Boolean,onTitleClick: (Boolean) -> Unit,onLinearClick: (Boolean) -> Unit
) {Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.SpaceBetween) {// 调用复选框可组合函数,传递状态与事件处理器CheckBoxes(linearSelected, imageSelected, onTitleClick, onLinearClick)}
}

除了复选框行,ScreenContent 还需要为 “标题” 和 “进度指示器” 添加插槽。这两个插槽分别命名为 titleContent(标题插槽)和 progressContent(进度指示器插槽),需将它们作为参数添加到函数中,并作为 Column 的子组件引用。修改后的 ScreenContent 代码如下:

@Composable
fun ScreenContent(linearSelected: Boolean,imageSelected: Boolean,onTitleClick: (Boolean) -> Unit,onLinearClick: (Boolean) -> Unit,// 新增标题插槽:参数类型为“返回可组合函数的无参Lambda”titleContent: @Composable () -> Unit,// 新增进度指示器插槽:同上progressContent: @Composable () -> Unit
) {Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.SpaceBetween) {titleContent() // 渲染标题插槽内容progressContent() // 渲染进度指示器插槽内容CheckBoxes(linearSelected, imageSelected, onTitleClick, onLinearClick)}
}

目前仅剩最后一步:在 MainScreen 的声明中添加代码,根据 linearSelected 和 imageSelected 状态变量的当前值,为插槽提供不同的可组合函数。不过在此之前,我们还需要添加一个可组合函数,用于在标题插槽中显示图片。

添加 Image drawable 资源

本示例将使用 Android SDK 自带的一个矢量图资源。如需选择图片并添加到项目中,可按以下步骤操作:

  • 在 “Project” 工具窗口中找到 drawable 文件夹(路径:app -> res -> drawable),右键点击该文件夹。
  • 在弹出的菜单中,选择 “New -> Vector Asset” 菜单选项。

添加 Image drawable 资源

选择该菜单选项后,Android Studio 会弹出 “Asset Studio” 对话框。

在这里插入图片描述

在对话框中,点击 “Clip art:” 标签右侧的图片(如上图箭头所示),会显示可用图标列表。操作步骤如下:

  • 在搜索框中输入 “cloud”。
  • 从下拉菜单中选择 “Filled”(填充样式)。
  • 最后选择 “Cloud Download” 图标。

在这里插入图片描述

点击 “OK” 按钮选择该图片,返回 “Asset Studio” 对话框。将图片尺寸调整为 150dp × 150dp,然后点击 “Next” 按钮。在后续界面点击 “Finish”按钮,将文件保存到默认位置。

虽然在 “Asset Studio” 对话框中可以修改图片颜色,但颜色选择器仅支持通过 RGB 值指定颜色。最好修改项目资源中定义的颜色,因此需按以下步骤操作:

  1. 在 “Project” 工具窗口中,找到并打开 colors.xml 文件(路径:app -> res -> values)。该文件包含一组命名颜色属性,本示例将使用名为 purple_700 的颜色,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources><!-- 其他颜色定义 --><color name="purple_700">#FF3700B3</color>
</resources>
  1. 在 “Project” 工具窗口中,双击矢量资源文件 baseline_cloud_download_24.xml 加载到编辑器中。
  2. 将编辑器从 “Design” 模式切换到 “Code” 模式,修改 android:tint(色调)属性,使其引用 purple_700 颜色,修改后代码片段如下:
<vector android:height="150dp" android:tint="@color/purple_700"  <!-- 引用命名颜色 -->android:viewportHeight="24" android:viewportWidth="24" android:width="150dp" xmlns:android="http://schemas.android.com/apk/res/android"><!-- 矢量图路径信息 -->
</vector>

新建 TitleImage 可组合函数

我们已准备好用于标题的图片资源,下一步需在 MainActivity.kt 文件中添加一个可组合函数来显示该图片。为了让这个可组合函数尽可能具备复用性,我们将设计它通过参数接收待显示的图片资源,代码如下:

@Composable
fun TitleImage(drawing: Int) {Image(painter = painterResource(drawing), // 通过参数接收图片资源,创建图片绘制器contentDescription = "title image", // 图片描述(用于无障碍访问)modifier = Modifier.size(150.dp)    // 设置图片尺寸为 150dp × 150dp)
}

Image 组件提供了多种渲染图片的方式,具体取决于调用时传入的参数。由于我们使用的是项目资源中的图片,因此该组件通过调用 painterResource 方法来渲染图片 —— 该方法会根据传入的资源 ID(即 drawing 参数)加载对应的图片资源。

完成 MainScreen 可组合函数

所有子可组合函数已添加,状态变量与事件处理器也已实现,现在可以完成 MainScreen 的声明了。核心任务是在该可组合函数中添加代码,根据当前复选框的选择状态,为 ScreenContent 的两个插槽填充不同内容。

在 MainActivity.kt 文件中找到 MainScreen 可组合函数,添加调用 ScreenContent 的代码,完整实现如下:

@Composable
fun MainScreen(modifier: Modifier = Modifier) {// 声明并初始化状态变量:控制进度条类型(线性/圆形)和标题类型(图片/文本)var linearSelected by remember { mutableStateOf(true) }var imageSelected by remember { mutableStateOf(true) }// 进度条复选框的事件处理器:更新线性进度条选择状态val onLinearClick = { value: Boolean ->linearSelected = value}// 标题复选框的事件处理器:更新标题类型选择状态val onTitleClick = { value: Boolean ->imageSelected = value}// 调用 ScreenContent,为其传递状态、事件处理器和插槽内容ScreenContent(linearSelected = linearSelected,imageSelected = imageSelected,onLinearClick = onLinearClick,onTitleClick = onTitleClick,// 标题插槽:根据 imageSelected 状态切换“图片标题”或“文本标题”titleContent = {if (imageSelected) {// 显示图片标题:传入之前添加的“云下载”图标资源TitleImage(drawing = R.drawable.baseline_cloud_download_24)} else {// 显示文本标题:使用 Material 主题的小标题样式,添加 30dp 内边距Text(text = "Downloading",style = MaterialTheme.typography.headlineSmall,modifier = Modifier.padding(30.dp))}},// 进度指示器插槽:根据 linearSelected 状态切换“线性进度条”或“圆形进度条”progressContent = {if (linearSelected) {// 显示线性进度条:设置高度为 40dpLinearProgressIndicator(Modifier.height(40.dp))} else {// 显示圆形进度条:设置尺寸为 200dp,进度条宽度为 18dpCircularProgressIndicator(modifier = Modifier.size(200.dp),strokeWidth = 18.dp)}})
}

对 ScreenContent 的调用首先会传递状态变量和事件处理器,这些参数随后会被进一步传递给两个 Checkbox 实例:

ScreenContent(linearSelected = linearSelected,  // 线性进度条选择状态imageSelected = imageSelected,    // 标题类型选择状态onLinearClick = onLinearClick,    // 线性进度条复选框点击事件onTitleClick = onTitleClick,      // 标题类型复选框点击事件

接下来的参数用于配置 titleContent 插槽,它通过 if 语句根据 imageSelected 状态的当前值,决定传递 TitleImage 还是 Text 组件:

titleContent = {if (imageSelected) {// 若勾选“Image Title”复选框,显示图片标题TitleImage(drawing = R.drawable.baseline_cloud_download_24)} else {// 若未勾选,显示文本标题(使用小标题样式并添加内边距)Text("Downloading",style = MaterialTheme.typography.headlineSmall,modifier = Modifier.padding(30.dp))}
},

最后,根据 linearSelected 状态的当前值,使用线性或圆形进度指示器填充 ScreenContent 的 progressContent 插槽:

progressContent = {if (linearSelected) {// 若勾选“Linear Progress”复选框,显示线性进度条(高度40dp)LinearProgressIndicator(Modifier.height(40.dp))} else {// 若未勾选,显示圆形进度条(尺寸200dp,线条宽18dp)CircularProgressIndicator(Modifier.size(200.dp),strokeWidth = 18.dp)}
}

需要注意的是,我们并未向任何一个进度指示器传递进度值。这会导致这些组件进入不确定进度模式,表现为进度指示器显示持续循环的动画效果。

预览项目

完成上述修改后,项目已可进行预览。找到本章前文添加的 DemoPreview 可组合函数,将其修改为调用 MainScreen(而非原来的 Checkboxes 可组合函数),同时为预览添加系统 UI 显示,修改后代码如下:

// showSystemUi = true:在预览中显示系统 UI(如状态栏、导航栏)
@Preview(showSystemUi = true)
@Composable
fun DemoPreview() {Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->// 调用 MainScreen 并传入内边距,与 MainActivity 中的调用逻辑保持一致MainScreen(modifier = Modifier.padding(innerPadding))}
}

执行重建(rebuild)操作后,预览面板显示的内容应与下图 所示一致。

在这里插入图片描述

要测试项目是否正常运行,需通过下图所示的菜单启动交互模式(interactive mode)。

在这里插入图片描述

启动交互模式后,可尝试切换复选框的不同组合状态,验证 ScreenContent 可组合函数的插槽 API 是否按预期工作。下图展示了 “两个复选框均取消勾选” 时的界面渲染效果。

在这里插入图片描述

小结

本章介绍了插槽 API(Slot API)的概念,并演示了如何将其添加到可组合函数中。通过实现插槽 API,可在调用可组合函数时实现 “动态指定”,这与普通可组合函数的 “静态内容” 形成对比:普通可组合函数的内容在编写函数时就已定义,后续无法更改。

插槽 API 本质就是可组合函数接受其他可组合函数作为参数。

本章还演示了如何使用插槽 API(Slot API),在运行时调用可组合函数时向其动态插入不同内容。

  • 将状态变量和事件处理器引用,在多个层级的可组合函数间向下传递;
  • 学习如何使用 Android Studio 的 Asset Studio(资源工作室),选择并配置 Android SDK 内置的矢量图资源;
  • 使用 Compose 内置的 Image 组件,在用户界面布局中渲染图片。
http://www.dtcms.com/a/526430.html

相关文章:

  • 网站建设实习困难网站sem优化怎么做
  • 我的网站别人给黑链 攻击天元建设集团有限公司经济活动分析
  • 优质的网站建设公司沧县做网站价格
  • 50个单页面网站设计欣赏(2)十大工业互联网平台
  • 静态网站开发外文文献株洲论坛
  • 网站语言选择查询公司营业执照的网站
  • 【Linux】常见的系统调用 函数和功能简单总结
  • 个人做网站费用wordpress开放平台
  • 东莞网站建设aj博客wordpress地址和找点地址
  • 常见网站模式如何申请一个自己的网站
  • 我想找个郑州做网站的西安门户网站建设公司哪家好
  • 深入浅出Java 8 Lambda表达式
  • 软件开发 网站开发 不同赣州企业网
  • 域名注册万网北京网站优化步
  • 东莞电子网站建设dw设计一个简单网站
  • 东莞网站维护买到域名怎么做网站
  • 【内存池】动态内存分配机制
  • 一键注册所有网站涉密网络运行维护服务外包的单位
  • 深圳市 网站建设450做自己的视频网站
  • 免费空间的个人网站为什么wordpress在ie打开很慢
  • 【1024节】一年一年又是一年
  • 武义建设局网站网站建设 技术方案
  • 网站别人备案怎么办dedecms wap网站模板下载
  • 青岛seo整站优化织梦网站如何做地区分站
  • 网站首页图片尺寸广州安全教育平台官网登录
  • 南阳网站推广站长之家alexa排名怎么看
  • 开家网站建设培训学校付钱做编程题目的网站
  • 商业网站的特点wordpress需要多少运存
  • 怎么做网站盗号市场推广的方法和规划
  • 好的h5制作网站模板下载专业类搜题软件