JetBrains

Compose Multiplatform 1.2 发布:全新鼠标和键盘 API、ProGuard 支持、在线更新等

Image

Compose Multiplatform 1.2 现已正式推出! 最新版本的 Kotlin 声明式 UI 框架与最新 Kotlin 版本兼容,并为桌面目标带来了多个强大功能。以下是本次更新的亮点:

  • 新添加的 ProGuard 支持允许您在发布前缩小和混淆应用程序。

  • 焦点管理得到改进,带来更出色的表单元素键盘导航。

  • 处理鼠标和键盘输入的 API 经过重做,使对点击和拖动做出反应的操作更易实现。

  • 我们正在为布局和输入字段引入对从右到左语言 (RTL) 的官方支持。

  • 焦点管理和上下文菜单的 Swing 互操作性更加无缝。

  • 您现在可以将第三方工具 Hydraulic Conveyor 用作 Compose Gradle 插件的替代方案来打包桌面应用程序,使构建下载页面、针对多个平台公证应用程序以及在应用程序中集成自动更新功能都更加轻松。

下面,就让我们来详细了解 Compose Multiplatform 1.2 中的新功能!

对 ProGuard 优化器

和混淆器的支持

从最新版本的 Compose Multiplatform 开始,您现在可以使用 ProGuard 改善捆绑应用程序的性能和大小,而无需提供特殊配置。ProGuard 应用优化技术来缩小代码,从而缩减最终二进制文件的大小。它还能够优化代码,移除多余函数调用或对象分配来提供轻微的性能改进。它还提供了将代码混淆添加到应用程序的选项。

我们在测试中观察到,使用 ProGuard 处理应用程序显著缩减了示例应用程序的最终分发大小。

Image

即使没有在项目的 Gradle 配置中指定任何选项,您也可以使用新的runReleaseDistributable和packageRelease<PACKAGE_FORMAT>任务来处理代码,并随后使用 ProGuard 进行运行或打包。

提供自定义 ProGuard 规则

如果应用程序使用反射或在运行时生成字节码,您可能需要向 ProGuard 说明优化过程中不应移除哪些类。您可以提供标准 ProGuard 配置文件并在 Gradle 构建文件的buildTypes.release.proguard块中进行配置:

compose.desktop {
    application {
        // ...
        buildTypes.release.proguard {
            configurationFiles.from("rules.pro")
        }
    }
}

如需详细了解 Compose Multiplatform 中提供的 ProGuard 集成,请查看 GitHub 仓库中的示例项目。

改进的键盘导航焦点管理

我们重做了一些 UI 元素焦点管理的行为,涉及用户通过键盘快捷键浏览元素和输入的方式。可点击和可切换元素(带有修饰符Modifier.clickable和Modifier.toggleable)现在都会在被点击时请求焦点,这使可获得焦点的条目之间的导航能够按用户预期工作(选择被点击条目之后的下一个可获得焦点的条目)。此外,在元素获得焦点后,我们还引入了新的行为,让用户可以更轻松地直接使用键盘快捷键:

  • 现在,可以使用空格键打开和关闭标记为Modifier.toggleable的可组合对象。

  • 通过箭头键(一次增加一个值)、Page Up 和 Page Down 键(一次移动 10% 的值范围)以及 Home 和 End 键(将值分别设为范围的 0% 或 100%)可以控制Slider可组合对象。

  • DropDownMenu、CursorDropDownMenu和ContextMenu中的菜单项现在可以通过键盘箭头导航。

鼠标和键盘 API 改进

我们还在改进 Compose for Desktop 的实验性 API,使鼠标和键盘输入更加方便。我们引入了onClick和onDrag修饰符,让修饰键更易访问。

请注意,鼠标和键盘 API 目前仅适用于 Compose Multiplatform 的桌面目标。

新的onClick修饰符

我们引入了Modifier.onClick来替代已弃用的Modifier.mouseClickable。新的修饰符支持使用回调精细控制处理任何类型的点击。您可以使用PointerMatcher区分不同类型的点击(常规、双击、长按)和正在使用的按钮(鼠标左键、鼠标右键、鼠标中键等)。您还可以通过keyboardModifierslambda 形参使这些回调以需要按下的特定键盘修饰键为条件。

Box(modifier = Modifier.size(200.dp).background(Color.Yellow)
   .onClick(
       matcher = PointerMatcher.mouse(PointerButton.Secondary), // Right Mouse Button
       keyboardModifiers = { isAltPressed }, // accept clicks only when Alt pressed
       onLongClick = { // optional
           println("Long Click with secondary button and Alt pressed")
       },
       onDoubleClick = { // optional
           println("Double Click with secondary button and Alt pressed")
       },
       onClick = {
           println("Click with secondary button and Alt pressed")
       }
   )
   .onClick { // onClick with all default parameters
       println("Click with primary button (mouse left button)")
   }
) {
   Text(text = "Box with onClick", modifier = Modifier.align(Alignment.Center))
}

如果您想详细了解此 API,我们准备了一个教程,其中介绍了如何使用此修饰符公开的不同选项,并解释了如何链接多个onClick修饰符以分隔不同的条件和修饰键。

新的onDrag修饰符

使用onDrag修饰符,您可以指定在拖动事件上调用的回调。它与onClick相同,配合PointerMatcher运作,您可以配置额外约束,例如拖动事件应该使用哪个鼠标按钮。同样,您可以指定多个onDrag修饰符来使用不同的鼠标按钮,例如,为鼠标左键和右键实现不同的拖动行为。

var boxOffset by remember { mutableStateOf(Offset(0f, 0f)) }

Box(modifier = Modifier
   .offset {
       IntOffset(boxOffset.x.toInt(), boxOffset.y.toInt())
   }.size(200.dp).background(Color.Yellow)
   .onDrag(
       matcher = PointerMatcher.mouse(PointerButton.Secondary), // it's Primary by default, can be configured with other pointer buttons such as Secondary, Tertiary, etc.
       onDragStart = { }, // optional
       onDragEnd = { },   // optional
       onDragCancel = { } // optional
   ) {
       println("Drag offset x=${it.x} y=${it.y}")
       boxOffset += it
   }

) {
   Text(text = "Box with onDragn(using right mouse button)", modifier = Modifier.align(Alignment.Center))
}

这也使此实验性 API 与始终使用主按钮的draggable修饰符等元素区分开来,这些元素位于 Compose Multiplatform 的 Android 目标中。

如需了解此 API 的更多信息,我们提供的教程包含更广泛的用法示例,还说明了高级功能,例如在PointerInputScope中侦听拖动和点按手势。

访问键盘修饰键

在可组合对象的上下文中,您现在可以使用LocalWindowInfo.current.keyboardModifiers状态,访问当前按下的键盘修饰键(Ctrl、Shift、Alt 等)。由于keyboardModifiers是在按下或释放修饰键时自动更新的状态,依赖于这些键盘修饰键的可组合对象将重新组合。

val windowInfo = LocalWindowInfo.current
var hovered by remember { mutableStateOf(false) }
val color by derivedStateOf {
  if (hovered && windowInfo.keyboardModifiers.isCtrlPressed) {
      Color.Yellow
  } else {
      Color.Gray
  }
}

Box(modifier = Modifier
   .size(200.dp)
   .background(color)
   .onPointerEvent(PointerEventType.Enter) {
       hovered = true
   }.onPointerEvent(PointerEventType.Exit) {
       hovered = false
   }
) {
   Text("Hover me and then press Ctrl", modifier = Modifier.align(Alignment.Center))
}

支持从右到左的语言和脚本

从 1.2 版开始,Compose for Desktop 将支持从右到左的语言。默认情况下,将根据检测到的系统语言自动切换布局和输入。如果您实现的行为取决于所用脚本的方向,可以使用公开此信息的LocalLayoutDirectionCompositionLocal。

与 Swing 的键盘导航互操作性

和共享上下文菜单

Compose for Desktop 构建的用户界面和 Swing 构建的用户界面在 1.2 版中更易结合。在键盘导航方面,SwingPanel(允许在 Compose for Desktop 应用程序中嵌入 Swing 元素)或ComposePanel(在现有 Swing 应用程序中嵌入 Compose for Desktop)可以无缝工作。通过 Tab 键切换时,两个 UI 框架中的可获得焦点的元素都将正确选择。

Image

将 Compose for Desktop 添加到现有 Swing 应用程序时,您现在可以在应用程序的两个部分对文本和文本字段使用相同的上下文菜单。为此,您可以使用可组合对象JPopupTextMenu。

val localization = LocalLocalization.current
CompositionLocalProvider(
    LocalTextContextMenu provides JPopupTextMenu(owner
) 
{ textManager, items ->
        JPopupMenu().apply {
            textManager.cut?.also {
                add(
                    swingItem(localization.cut, Color.RED, KeyEvent.VK_X, it)
                )
            }
            textManager.copy?.also {
                add(
                    swingItem(localization.copy, Color.GREEN, KeyEvent.VK_C, it)
                )
            }
            textManager.paste?.also {
                add(
                    swingItem(localization.paste, Color.BLUE, KeyEvent.VK_V, it)
                )
            }
            textManager.selectAll?.also {
                add(JPopupMenu.Separator())
                add(
                   swingItem(localization.selectAll, Color.BLACK, KeyEvent.VK_A, it)
                )
            }
            ...
        }
    }
) {
    ...
}

我们还在多个教程中提供了有关上下文菜单的 Swing 互操作性的更多详细信息和完整示例。

通过 Hydraulic 的 Conveyor 

进行替代应用分发和在线更新

在 1.2 版中,您可以使用 Hydraulic 出品的第三方工具 Conveyor替代 Compose Gradle 插件来打包桌面应用。Conveyor 对开源项目免费,并且在试用期也可免费用于商业用途。

与 Compose Gradle 插件一样,您可以用它创建完全独立的应用程序,使应用程序采用目标平台的原生格式并带有自己的捆绑 JVM。Conveyor 还增加了对在线更新、生成下载页面(示例)、为各平台自动转换图标以及多种其他功能的支持。此外,由于 Conveyor 不依赖原生工具,您可以从任何操作系统构建、签署和公证软件包。

Conveyor 显著简化了分发,非常适合 Compose for Desktop。这使得提供在线更新更加顺畅,并避免了复杂的多平台 CI/CD 设置。在 Windows 和 macOS 上,它还为内部或面向开发者的应用提供了额外的便利:使用默认自签名模式时,生成的下载页面为用户提供了可复制粘贴的命令以从终端安装应用(示例)。

总结

我们希望您喜欢最新版本的 Compose Multiplatform,并继续为您的应用程序构建漂亮的 UI!这篇发布博文未包含部分较小的更改,有关 Compose Multiplatform 1.2 中更改和改进的完整列表,请参阅仓库中的变更日志。

从 1.2 版开始,Compose Multiplatform 将支持多个版本的 Kotlin 编译器:桌面目标支持 Kotlin 1.7.10 和 Kotlin 1.7.20。Web 目标当前支持 Kotlin 1.7.10,并将在下一次小更新中提供对 1.7.20 的支持。

一如既往,开始使用最新版本的 Compose Multiplatform 的最简单方式是查看官方教程,或者,您也可以使用 IntelliJ IDEA 中的 Kotlin 项目向导自行开始探索。祝您编程愉快!

探索 Compose Multiplatform 网站

相关阅读

Image

本博文英文原作者:Sebastian Aigner

Image

⏬ 戳「阅读原文」了解更多