完整案例:Kotlin+Compose+Multiplatform跨平台之桌面端实现(二)

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

本案例Demo效果图

一、前言

前一篇文章:Kotlin+Compose+Multiplatform跨平台之桌面端实现(一)Kotlin+Compose+Multiplatform跨平台开发进行了相关基础介绍,包含新项目基础工程,相关配置,打包,基础布局等,从本篇开始就正式进入到跨平台桌面端的实际内容相关开发。

源码地址

二、桌面端自定义标题栏,拖动,放大,缩小,最小化等

桌面端有一个标题,就是鼠标左键按住顶部那块区域可以拖动窗口的区域。

  1. 自定义标题栏,先要让原来自带标题栏隐藏,将Window的undecorated = true属性设置为true代表隐藏标题栏
  2. 定义桌面端窗口最小尺寸:通过:window.minimumSize = Dimension(JvmConfig.windowWidth, onfig.windowHeight)来设置最小尺寸
  3. 窗口最大化:window.placement = WindowPlacement.Maximized
  4. 窗口恢复之前小尺寸:window.placement = WindowPlacement.Floating
  5. 窗口最小化:windowState.isMinimized = true
  6. 窗口关闭:直接调用 ApplicationScope.exitApplication()
  7. 窗口拖动,在自定义标题栏外层包一层代码如下:
less 复制代码
scope.WindowDraggableArea(Modifier.fillMaxWidth().height(40.dp)) {
//里面是自定义标题栏
}

详细代码请看项目源码工程

三、换肤皮肤

  1. 通过统一UI主题:
ini 复制代码
MaterialTheme(colorScheme = ThemeManager.skinTheme, content = {
    Surface(color = MaterialTheme.colorScheme.background, content = {

    })
}
  1. 通过mutableStateOf变化让Compose发生重组来实现切换皮肤
csharp 复制代码
var skinTheme by mutableStateOf(getColorsSchemes(skinThemeType))
  1. 准备多套皮肤,给每一条一个索引,让其切换后,将索引通过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
    }
}
  1. 切换时候更改mutableStateOf的值skinTheme,并保存好索引:
ini 复制代码
PlatformKVStore.saveSkin(it)
ThemeManager.skinTheme = item

四、桌面端类似Android中ViewModel使用

  1. 使用第三方moe.tlaster:precompose:1.6.2moe.tlaster:precompose-viewmodel:1.6.2
  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") // 新增子模块
}    
  1. 它具备以下特性:

3.1)支持Windows 和 macOS,

3.2)支持与 ViewModel 和 MVI 架构深度集成,

3.3)支持通过组合多个 StateFlow 简化状态管理

3.4)与androidx.lifecycle 组件良好兼容

3.5)支持桌面应用特有的生命周期事件处理

3.6)支持协程,特别适合需要同时 开发桌面端 和 移动端 的项目

五、网络和IO文件

  1. Compose 桌面端使用的是JVM,所以,在该平台下网络请求,可以直接使用 Okhttp + Retrofit,所以Android上面那一套网络层的完全可以用上。
  2. Compose 桌面端IO文件操作也是在JVM上面的,所以Android 上原来使用的IO操作,相关库,文件写法都完全一样的。

六、多page切换类似TabHos和路由

  1. 可以直接使用ComposeTab + VerticalPager/HorizontalPager 或者 NavHost ,compose的基础组件使用可以参考我之前文章:
    Compose插件化:一个Demo带你入门Compose,同时带你入门插件化开发
  2. 路由跳转,可以直接使用 Compose 的路由,也可以使用 moe.tlaster:precompose:1.6.2 里面带的路由。
  3. 关于路由,不一定按照compose官方路由思路来,别简单问题复杂化:最终是为了解决问题,试想:在没有路由前或者不用路由前,第一个界面下面一排菜单切换,这几个菜单都是第一层,当进入第二层时候,需要新开界面(Android 的Activity),当compose界面只用1个Activity时候,当想进入第2,3,4层,只能用路由,路由可以从第2,3,4,层返回。当然第一层平行的几个tab,可以也可以用路由,也可以不用路由:用 Tab + VerticalPager/HorizontalPager 来实现,只有当需要进入第2,3,4,5层时候用路由。
  4. 使用 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_ONLYJava SwingJFileChooser 组件的一个关键参数设置,其作用为限制用户只能选择目录(文件夹)而无法选择具体文件。

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跨平台之桌面端实现的第二篇: 主要分享了:

  1. 桌面端自定义标题栏,拖动,放大,缩小,最小化等
  2. 换肤皮肤
  3. 桌面端类似Android中ViewModel使用
  4. 网络和IO文件
  5. 多page切换类似TabHos和路由管理
  6. 打开文件选择
  7. KV存储使用

相信会对你带来一定的帮助 本案例后续还有:

  1. 数据库使用
  2. 音乐播放,歌词,音频动画
  3. 视频播放等相关操作

源码地址

感谢阅读:

欢迎用你发财的小手 关注,点赞、收藏

这里你会学到不一样的东西

相关推荐
auxor19 分钟前
Android 窗口管理 - 窗口添加过程分析Client端
android
雨白1 小时前
HTTP协议详解(一):工作原理、请求方法与状态码
android·http
Yang-Never1 小时前
Kotlin -> object声明和object表达式
android·java·开发语言·kotlin·android studio
DemonAvenger2 小时前
Go语言实现高并发网络爬虫:技术实践与经验分享
网络协议·架构·go
SmalBox2 小时前
【开篇导览】探索游戏渲染从UnityURP开始
架构
小白马丶2 小时前
Jetpack Compose开发框架搭建
android·前端·android jetpack
攻城狮Talk2 小时前
FocusParkingView清除旧Window焦点
android
狂浪天涯2 小时前
Android 16 显示系统 | 从View 到屏幕系列 - 8 | SurfaceFlinger 合成 (一)
android
anyup3 小时前
uView Pro 正式开源!70+ Vue3 组件重构全记录,助力 uni-app 组件生态,你会选择吗?
前端·架构·uni-app