
循序渐进Kotlin+Compose+Multiplatform跨平台之桌面端实现系列:
Kotlin+Compose+Multiplatform跨平台之桌面端实现(一)
完整案例:Kotlin+Compose+Multiplatform跨平台之桌面端实现(二)
本案例Demo效果图
:

一、前言
前一篇文章:Kotlin+Compose+Multiplatform跨平台之桌面端实现(一)对Kotlin+Compose+Multiplatform
跨平台开发进行了相关基础介绍,包含新项目基础工程,相关配置,打包,基础布局等,从本篇开始就正式进入到跨平台桌面端的实际内容相关开发。
二、桌面端自定义标题栏,拖动,放大,缩小,最小化等
桌面端有一个标题,就是鼠标左键按住顶部那块区域可以拖动窗口的区域。
- 自定义标题栏,先要让原来自带标题栏隐藏,将Window的
undecorated = true
属性设置为true代表隐藏标题栏 - 定义桌面端窗口最小尺寸:通过:
window.minimumSize = Dimension(JvmConfig.windowWidth, onfig.windowHeight)
来设置最小尺寸 - 窗口最大化:
window.placement = WindowPlacement.Maximized
- 窗口恢复之前小尺寸:
window.placement = WindowPlacement.Floating
- 窗口最小化:
windowState.isMinimized = true
- 窗口关闭:直接调用
ApplicationScope.exitApplication()
- 窗口拖动,在自定义标题栏外层包一层代码如下:
less
scope.WindowDraggableArea(Modifier.fillMaxWidth().height(40.dp)) {
//里面是自定义标题栏
}
详细代码请看项目源码工程
三、换肤皮肤
- 通过统一UI主题:
ini
MaterialTheme(colorScheme = ThemeManager.skinTheme, content = {
Surface(color = MaterialTheme.colorScheme.background, content = {
})
}
- 通过
mutableStateOf
变化让Compose发生重组来实现切换皮肤
csharp
var skinTheme by mutableStateOf(getColorsSchemes(skinThemeType))
- 准备多套皮肤,给每一条一个索引,让其切换后,将索引通过KV存储保存在本地,记录下来:
rust
object ThemeManager {
var skinThemeType by mutableStateOf(PlatformKVStore.getSkinType())
var skinTheme by mutableStateOf(getColorsSchemes(skinThemeType))
private fun getColorsSchemes(skinType: Int) = when (skinType) {
0 -> LightColors
1 -> DarkColors
2 -> Colors72
3 -> RedColors
4 -> defaultColors
5 -> Colors60
6 -> Colors61
7 -> Colors62
8 -> Colors63
9 -> Colors65
10 -> Colors66
11 -> Colors67
12 -> Colors68
13 -> Colors71
14 -> Colors69
else -> LightColors
}
}
- 切换时候更改
mutableStateOf的值skinTheme
,并保存好索引:
ini
PlatformKVStore.saveSkin(it)
ThemeManager.skinTheme = item
四、桌面端类似Android中ViewModel使用
- 使用第三方
moe.tlaster:precompose:1.6.2
和moe.tlaster:precompose-viewmodel:1.6.2
- 在
jvmMain.dependencies {}
内部添加依赖:
scss
jvmMain.dependencies {
//桌面程序中使用ViewModel
implementation("moe.tlaster:precompose:1.6.2") // 必须≥1.6.0
implementation("moe.tlaster:precompose-viewmodel:1.6.2") // 新增子模块
}
- 它具备以下特性:
3.1)支持Windows 和 macOS,
3.2)支持与 ViewModel 和 MVI 架构深度集成,
3.3)支持通过组合多个 StateFlow 简化状态管理
3.4)与androidx.lifecycle 组件良好兼容
3.5)支持桌面应用特有的生命周期事件处理
3.6)支持协程,特别适合需要同时 开发桌面端 和 移动端
的项目
五、网络和IO文件
- Compose 桌面端使用的是JVM,所以,在该平台下网络请求,可以直接使用 Okhttp + Retrofit,所以Android上面那一套网络层的完全可以用上。
- Compose 桌面端IO文件操作也是在JVM上面的,所以Android 上原来使用的IO操作,相关库,文件写法都完全一样的。
六、多page切换类似TabHos和路由
- 可以直接使用
Compose
的Tab + VerticalPager/HorizontalPager
或者NavHost
,compose的基础组件使用可以参考我之前文章:
Compose插件化:一个Demo带你入门Compose,同时带你入门插件化开发 - 路由跳转,可以直接使用 Compose 的路由,也可以使用
moe.tlaster:precompose:1.6.2
里面带的路由。 - 关于路由,不一定按照compose官方路由思路来,别简单问题复杂化:最终是为了解决问题,试想:在没有路由前或者不用路由前,第一个界面下面一排菜单切换,这几个菜单都是第一层,当进入第二层时候,需要新开界面(
Android 的Activity
),当compose界面只用1个Activity时候,当想进入第2,3,4层,只能用路由,路由可以从第2,3,4,层返回。当然第一层平行的几个tab,可以也可以用路由,也可以不用路由:用Tab + VerticalPager/HorizontalPager
来实现,只有当需要进入第2,3,4,5层时候用路由。 - 使用
moe.tlaster:precompose:1.6.2
简单实现路由如下:
scss
@Composable
fun NavGraph() {
val navigator = rememberNavigator("key222222")
val viewModel = viewModel { TabViewModel9() }
NavHost(
navigator = navigator, navTransition = remember {
NavTransition(
createTransition = fadeIn(),
destroyTransition = fadeOut(),
pauseTransition = fadeOut(),
resumeTransition = fadeIn(),
)
}, initialRoute = RouterUrls.news_first
) {
scene(RouterUrls.news_first) {
RouterFirst("BA8D4A3Rwangning", viewModel) {
navigator.navigate("${RouterUrls.news_second}?title=${it.title}&docid=${it.docid}")
}
}
scene(RouterUrls.news_second) { backStackEntry ->
val title = backStackEntry.query<String>("title") ?: ""
val docid = backStackEntry.query<String>("docid") ?: ""
RouterSample(navigator, docid, title)
}
}
七、打开文件选择
Compose
桌面端原生自带JFileChooser
,参数就是文件夹路径,
其中fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
是 Java Swing
中 JFileChooser
组件的一个关键参数设置,其作用为限制用户只能选择目录(文件夹)而无法选择具体文件。
JFileChooser.FILES_ONLY
:默认模式,仅允许选择文件
JFileChooser.FILES_AND_DIRECTORIES
:同时允许选择文件和目录
选择的结果路径 通过 val selectedDir = selectedFile.absolutePath
,我们可以拿到选择的结果路径。
相关代码如下:
ini
JFileChooser(PlatformKVStore.getDownloadDir()).apply {
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
if (showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
val selectedDir = selectedFile.absolutePath
_downloadPath.value = selectedDir
PlatformKVStore.saveDownloadDir(selectedDir)
}
}
八、KV存储使用
compose 桌面端使用 com.russhwolf:multiplatform-settings:1.2.0
实现KV存储,它支持 Android、iOS、macOS、JS、JVM 及 Windows
等平台,通过统一 API 存取键值数据。 它的用法简单如下:
存储整型和去整型((带默认值
java
val settings = Settings()
settings.putInt("count", 5) // 存储整型
val value = settings.getInt("count", 0) // 读取(带默认值
九、总结
本文是完整案例:Kotlin+Compose+Multiplatform跨平台之桌面端实现的第二篇: 主要分享了:
- 桌面端自定义标题栏,拖动,放大,缩小,最小化等
- 换肤皮肤
- 桌面端类似Android中ViewModel使用
- 网络和IO文件
- 多page切换类似TabHos和路由管理
- 打开文件选择
- KV存储使用
相信会对你带来一定的帮助 本案例后续还有:
- 数据库使用
- 音乐播放,歌词,音频动画
- 视频播放等相关操作