使用 Kotlin 构建桌面应用程序并为桌面编写多平台
介绍
Compose Multiplatform是由 Jetbrains 开发的 UI 框架,用于使用带有反应性和功能性 API 的 Kotlin 简化和加速桌面应用程序开发。它以 JVM 为目标,因此可用于构建跨平台 GUI 应用程序。它还支持其他平台,例如网络。
它基于 Google 为 Android 制作的Jetpack Compose声明式 UI 工具包。使用 Kotlin 设计用户界面可以减少错误、更好的工具支持以及更简洁和健壮的代码。这是使用基于可组合函数的声明性 UI 模型来实现的,该模型接受参数来描述 UI 逻辑而不返回任何内容,并且必须没有副作用。Compose 心智模型文档中提供了有关可组合的更多详细信息。
虽然本教程的主要主题是 Compose Multiplatform for Desktop,但其中的概念和大部分代码片段也适用于 Android 的 Jetpack Compose。
入门
需要 JDK 11 或更高版本。’JAVA_HOME’ 环境变量应设置为 JDK 路径位置。IntelliJ IDEA Community Edition 或 Ultimate Edition 20.2 或更高版本是首选 IDE。如果您需要安装 JDK 11 和 IntelliJ IDEA,请按照本教程进行操作。
- 从以下链接下载 Compose for Desktop 项目启动器
- 提取文件夹并将其移动到您的工作区
- 打开 IntelliJ IDEA 并从您的工作区打开项目
- 选择 Gradle Kotlin 作为构建系统
- 打开
src/main/kotlin/main.kt
,使用以下内容覆盖内容:
import androidx.compose.material.Text import androidx.compose.ui.window.Window import androidx.compose.ui.window.application fun main() = application { Window(onCloseRequest = ::exitApplication) { Text("Hello, World!") } }
6.从项目的根目录打开终端,然后启动:gradlew run
. 您也可以run
直接从 IntelliJ IDEA 启动 gradle 任务。
从实践中学习:实现一个简单的计算器
为了深入了解 Compose for Desktop 的概念并通过实践来学习,我们将在本节中实现一个计算器 UI。生成的 UI 将如下所示:
入口点
const val DEFAULT_WIDTH = 500 const val DEFAULT_HEIGHT = 500 fun main() = Window( title = "Compose Calculator - simply-how.com", size = IntSize(DEFAULT_WIDTH, DEFAULT_HEIGHT), icon = Assets.WindowIcon ) { MaterialTheme(colors = lightThemeColors) { val mainOutput = remember { mutableStateOf(TextFieldValue("0")) } Column(Modifier.fillMaxHeight()) { DisplayPanel( Modifier.weight(1f), mainOutput ) Keyboard( Modifier.weight(4f), mainOutput ) } } }
- 应用程序的窗口具有自定义的标题、大小(尺寸)和图标。此处描述了更多窗口自定义。
- 应用遵循Material Design原则的应用程序范围的主题。在此处了解有关主题的更多信息。
remember
用于在可组合项中创建内部状态。在此处了解有关状态管理的更多信息。Column
用于在屏幕上垂直放置项目。在此处了解可用的布局。- 修饰符用于自定义可组合的外观和行为。例如,
Modifier.weight
用于通过按比例划分可用空间来调整 Column 子项的大小。更多关于修饰符在这里。
显示屏
DisplayPanel
是计算器的顶部组件。
@Composable fun DisplayPanel( modifier: Modifier, mainOutput: MutableState<TextFieldValue> ) { Column( modifier = modifier .fillMaxWidth() .background(MaterialTheme.colors.surface) .padding(CALCULATOR_PADDING) .background(Color.White) .border(color = Color.Gray, width = 1.dp) .padding(start = 16.dp, end = 16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.End ) { Text( text = mainOutput.value.text, style = TextStyle( fontSize = 48.sp, fontFamily = jostFontFamily ), overflow = TextOverflow.Ellipsis, softWrap = false, maxLines = 1, ) } }
- 我们可以在这里找到更复杂的修饰符用法。
- 计算器的输出使用
Text
具有自定义样式和行为的组合来显示。 TextFieldValue
保存输出文本的编辑状态。它在应用程序的入口点初始化,并通过键盘按键进行修改。
键盘
这是放置在下方的计算器的第二个组件DisplayPanel
。
@Composable fun Keyboard( modifier: Modifier, mainOutput: MutableState<TextFieldValue> ) { Surface(modifier) { KeyboardKeys(mainOutput) } } @Composable fun KeyboardKeys(mainOutput: MutableState<TextFieldValue>) { Row(modifier = Modifier.fillMaxSize()) { KeyboardLayout.forEach { keyColumn -> Column(modifier = Modifier.weight(1f)) { keyColumn.forEach { key -> KeyboardKey(Modifier.weight(1f), key, mainOutput) } } } } }
- 可
Surface
组合项将隐式使用应用程序的 Material 主题中定义的表面颜色。 Row
用于在屏幕上水平放置项目。
@Composable fun KeyboardKey(modifier: Modifier, key: Key?, mainOutput: MutableState<TextFieldValue>) { if (key == null) { return EmptyKeyView(modifier) } KeyView(modifier = modifier.padding(1.dp), onClick = key.onClick?.let { { it(mainOutput) } } ?: { val textValue = mainOutput.value.text.let { if (it == "0") key.value else it + key.value } mainOutput.value = TextFieldValue(text = textValue) }) { if (key.icon == null) { val textStyle = if (key.type == KeyType.COMMAND) { TextStyle( color = MaterialTheme.colors.primary, fontSize = 22.sp ) } else { TextStyle( fontFamily = jostFontFamily, fontSize = 29.sp ) } Text( text = key.value, style = textStyle ) } else { Icon( asset = key.icon, tint = MaterialTheme.colors.primary ) } } } val KEY_BORDER_WIDTH = 1.dp val KEY_BORDER_COLOR = Color.Gray val KEY_ACTIVE_BACKGROUND = Color.White @Composable fun KeyView( modifier: Modifier = Modifier, onClick: () -> Unit, children: @Composable ColumnScope.() -> Unit ) { val active = remember { mutableStateOf(false) } Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxWidth() .padding(CALCULATOR_PADDING) .clickable(onClick = onClick) .background(color = if (active.value) KEY_ACTIVE_BACKGROUND else MaterialTheme.colors.background) .border(width = KEY_BORDER_WIDTH, color = KEY_BORDER_COLOR) .pointerMoveFilter( onEnter = { active.value = true false }, onExit = { active.value = false false } ), children = children ) } @Composable fun EmptyKeyView(modifier: Modifier) = Box( modifier = modifier.fillMaxWidth() .background(MaterialTheme.colors.background) .border(width = KEY_BORDER_WIDTH, color = KEY_BORDER_COLOR) )
Modifier.clickable
用于使键接收来自用户的点击交互。Modifier.pointerMoveFilter
此处用于在将鼠标悬停在键上时更改键的背景颜色。在此处了解有关鼠标事件的更多信息。
该应用程序的完整源代码可在GitHub上找到。
更多资源和文档
以下是一些涵盖本文未介绍的 Compose 方面的外部资源和教程:
- 可访问性
- 上下文菜单
- 图像和应用内图标操作
- 键盘事件处理
- 鼠标事件
- 导航
- 托盘和菜单通知
- Swing integration
- 桌面组件(例如工具提示和滚动条)
- Compose for Desktop示例
Jetpack Compose
- Jetpack Compose 基础教程
- Jetpack Compose 基础代码实验室
- Jetpack Compose 代码实验室中的布局
- Jetpack Compose示例
- Jetpack Compose Playground:社区驱动的 Jetpack Compose 示例代码和教程集合