用Jetpack Compose Desktop极简配置做一个Windows桌面时间显示器(compose框架入门向)

简介: compose的模板配置多少有些臃肿,如果只做单一平台多少是会简单一些的。但几乎没怎么见过配置很简单的例子,都是套那些模板,我觉得没必要搞那么复杂,那么本文就做一个非常简单的只有几行代码的小例子

眼看着微软的winuiMAUI用起来还不如以前的winformWPF,很多桌面开发人员都想尝试新兴平台和语言,传统的网页套壳方面除了很多年前就爆火的Electron,紧跟在后面的还有React Nativetauri,UI毕竟是网页前端一贯的优势,天生就是干这个的,做起来确实方便。但也并非只能考虑套壳写html,其他新兴语言也纷纷加入windows桌面开发的市场,dartFluttergo-langwailsfyne,都很有很多应用采用,他们胜在一个语法好看,维护也得力,跟那堆浏览器套壳相比性能要强些。当然最早搞跨平台的JVM平台也不能一直只用祖传SwingJavaFX了,Jetpack Compose依靠Android官方的背书做大做强,也进入到桌面开发的市场。

尽管这些框架都是以多平台自称,但并非所有应用都面向所有用户,单一平台的开发需求还是有很多的,尤其是windows这样一个体量的平台,做好这一个就难。不过上面说的这些框架的配置都是一上来就搞多平台,多多少少有些臃肿,如果只做单一平台多少是会简单一些的。但几乎没怎么见过配置很简单的例子,都是套那些模板,我觉得没必要搞那么复杂,那么本文就做一个非常简单的只有几行代码的小例子:时间显示器

缘起

可能有人会觉得,什么时间显示器,做这么个东西也太没用了,windows任务栏右下角不是就一直在显示时间吗?其实我就是为了这个东西才写的这例子,有的系统会显示,有的则不会,那么怎么修改呢?windows经典技能修改注册表HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced,新建DWORD(32bit)项,命名ShowSecondsInSystemClock,赋值1就显示秒。

image.png
image.png

终于显示秒了,看着挺简单,但细想是真的太搞笑了,这么简单的需求都要去改注册表,就没办法在控制面板设置,真是绝了,而且现在这系统耗费这么多资源的情况下,却连个秒都不舍得显示了,所以我特意做个时间显示器,连毫秒一起显示,帮Windows把时间显示全。

配置

首先不需要在IDEA或跑到Android studio去选什么模板,因为无论选什么模板,都给你搞一大堆配置,目录结构也不舒服。我们空白文件夹起步,先写gradle的配置,settings.gradle.kts就配置下rootProject.name就好,当然这个也并非必要,不写这个文件都没关系。然后写build.gradle.kts的部分:

import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
    kotlin("jvm") version "1.8.20"
    id("org.jetbrains.compose") version "1.4.0"
}
dependencies {
    implementation(compose.desktop.currentOs)
}

compose.desktop {
    application {
        mainClass = "MainKt"
        nativeDistributions {
            targetFormats(TargetFormat.Exe)
            packageName = "时间显示器"
            packageVersion = "1.0.0"
        }
    }
}

就这么简单就妥了,目录照样用传统的src\main\kotlin,一个平台有什么好建那么多main文件夹的,然后建个kotlin文件名为Main.kt,然后准备写main()方法,kotlin会自动为这个main()方法生成一个名为MainKt的对象名。

窗口绘制

下面就直接在新建的Main.kt中绘窗口,和flutter有些像:

fun main() = application {
    Window(
        state = rememberWindowState(
            size = DpSize(350.dp, 100.dp),
            position = WindowPosition.Aligned(Alignment.Center)
        ),
        resizable = false,
        title = "时间显示器",
        onCloseRequest = ::exitApplication
    ) {
        App()
    }
}

这个部分可以说一眼就能看清在干什么,main()不用说,这个application就是Application.desktop.kt中的application(),就两个参数。上面默认composable{}的是他的第2个参数content,就是在这里面绘制应用内容。如果你想关闭程序后仍在后台运行一些东西可以更改他的第1个参数exitProcessOnExitfalse

然后就是调用Window,是在Window.desktop.kt中的Window(),这个参数多但都可以字面理解,上面的代码分别赋值了状态(窗口大小,定位),禁止手动调大小,标题,关闭事件。请注意这个关闭事件onCloseRequest和上面提到的那个不一样,这个决定你是否关闭,就像Windows记事本notepad那个经典的“你想将更改保存到 无标题 吗?

就是这图:

image.png

所以你写onCloseRequest = {}的话,他就真的不关了,只能去任务管理器关。然后就是这个Window里面的内容App(),下面就实现这个方法,首先这种方法是在绘制嘛,自然要特殊对待,也就要给注解的,两个注解:@Composable@Preview,然后是内容:

@Composable
@Preview
fun App() {
    MaterialTheme {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.fillMaxSize()
        ) {
            Text("时间")
        }
    }
}

这段就是就是先定一个皮肤MaterialTheme,但不要一觉得是皮肤就能换,这东西要写的话也就一个material没别的,然后就是定个铺满窗口的Box,第一个参数contentAlignment跟名字意思一样就是居中,第二个modifier是大小和padding的设置,这个值除了用前面窗口传下来的就是用Modifier这个类,他除了元素的尺寸大小还可以定一个debug用的诊断信息参数inspectorInfo,但用处不大。前端网页开发人员可能一看到这里面只有padding就不能设置margin吗,用过flutter的可能就会一笑,其实Box这种单纯的容器就是用来做margin的,具体到一些组件的padding可能有的还有自己独特的padding参数,不用这个modifier设置。

下面就是效果:

image.png

可以看到确实定在中间了,那么下一步就是显示日期

显示日期

如果你第一时间就想到java.util.Datejava.text.SimpleDateFormat这俩祸害,请缓一下,千万别用这两个历史遗留问题满满的类了!日期我们用java1.8就引入的LocalDateTime.now(),他对应的格式化是DateTimeFormatter,他们的注释都写了大量的例子可以看,使用其实很简单就是:

val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S")
var time = dateFormatter.format(LocalDateTime.now())

这个格式化是挺厉害的可以自己灵活的用代码去拼接,如果有多个日期格式需求就不用和以前一样定义多个SimpleDateFormat,像下面这样就能控制:

val timeFormatter = DateTimeFormatterBuilder()
    .append(DateTimeFormatter.ISO_LOCAL_DATE)
    .appendLiteral(' ')
    .appendValue(ChronoField.HOUR_OF_DAY, 2)
    .appendLiteral(':')
    .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
    .optionalStart()
    .appendLiteral(':')
    .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
    .optionalStart()
    .appendFraction(ChronoField.MICRO_OF_SECOND, 0, 1, true)
    .toFormatter()

可以用java.time.format.DateTimeFormatterBuilder来动态的构建日期格式化,用java.time.temporal.ChronoField来选中日期类型,这一段格式化是和前面完全一样的,当然了这里不用这种写法,只是了解一下。

然后把下面的Text("时间")改成Text(time)就行了,然后再运行:

image.png

可以看到时间了,但这个当然没办法动态变化了,这时候你可能想到多线程来持续修改时间,但这可是kotlin,用协程就好了!他就像java拖了很久才在JDK19出的虚拟线程,用Thread.startVirtualThread()来启动,由于这不是个长期支持版本,我想大部分人最多还是只会用到JDK17,没办法用到这个好玩意。但kotlin就不一样了,先天自带这个好东西,但是要想在compose直接跑这个,我们还需要在build.gradle.kts那边引入一个小小的包kotlinx-coroutines-swing

dependencies {
    implementation(compose.desktop.currentOs)
    runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.1")
}

然后可以在text定义的下面启动最简单的MainScope协程:

MainScope().launch {
    while (true) {
        time = dateFormatter.format(LocalDateTime.now())
        delay(100)
    }
}

这段不用说自然是放到time定义的与MaterialTheme之间的地方,写起来也是很简单呐,但只是这样是没效果的,因为这个变量修改想想就知道不可能直接传递给UI的,需要给一个state,所以修改一下time的定义:

var time by mutableStateOf(dateFormatter.format(LocalDateTime.now()))

这个写by来代替=赋值是有好处的,改完time仍然是String类型,后面可以直接用=去修改和获取,否则因为是MutableState类型,要调用get()set(),这个语法糖确实黑科技,其他平台几乎做不到。下面效果就出来了:

e5b33d9719574ae2a2675dee73dae862~tplv-k3u1fbpfcp-watermark.gif

下面我又想到Windows时间显示的一大痛点,居然不能复制!想记录时间只能看着手敲,这也太蠢了,所以我特地做个复制按钮吧!

复制日期

复制自然要访问剪切板,剪切板不必用awt那个破Toolkit.getDefaultToolkit().systemClipboard了,可以用LocalClipboardManager.current轻松搞定,写起来简单的多,在time定义的下面再定义这个剪切板对象:

val clipboard = LocalClipboardManager.current

然后改下Text(text)的位置,给他套个Row对象让他和按钮排一行,然后放入这个按钮:

Row(
    verticalAlignment = Alignment.CenterVertically
) {
    Text(time)
    Button(modifier = Modifier.padding(horizontal = 10.dp), onClick = {
        clipboard.setText(AnnotatedString(time))
    }) {
        Text("复制")
    }
}

Row需要设定下垂直对齐,因为按钮比文本长一些,然后就是Button,如前文所说用Modifier定义下padding控制与左边文本的距离,从这里就可以看到其实这个padding就是前端网页的margin,想调padding是用他的contentPadding属性来调的。然后在onClick中赋值剪切板即可,按下按钮就复制:

image.png

综合源代码

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import kotlinx.coroutines.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

@Composable
@Preview
fun App() {
    val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S")
    var time by mutableStateOf(dateFormatter.format(LocalDateTime.now()))
    val clipboard = LocalClipboardManager.current
    MainScope().launch {
        while (true) {
            time = dateFormatter.format(LocalDateTime.now())
            delay(100)
        }
    }
    MaterialTheme {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.fillMaxSize()
        ) {
            Row(
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(time)
                Button(modifier = Modifier.padding(horizontal = 10.dp), onClick = {
                    clipboard.setText(AnnotatedString(time))
                }) {
                    Text("复制")
                }
            }
        }
    }
}

fun main() = application {
    Window(
        state = rememberWindowState(
            size = DpSize(350.dp, 100.dp),
            position = WindowPosition.Aligned(Alignment.Center)
        ),
        resizable = false,
        title = "时间显示器",
        onCloseRequest = ::exitApplication
    ) {
        App()
    }
}

可以看到就一个配置文件,一个代码文件,几十行代码(一大半还是import、括号符号、空行),相当的简洁。本篇属于composekotlin的入门向教学,后续会根据此文展开说一些其他内容,已经在编辑中。

本文写作于2023年6月27日并发布于lyrieek的掘金,于2023年7月19日进行修订发布于lyrieek的阿里云开发者社区。

目录
相关文章
|
7天前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
9天前
|
JavaScript Java Android开发
kotlin安卓在Jetpack Compose 框架下跨组件通讯EventBus
**EventBus** 是一个Android事件总线库,简化组件间通信。要使用它,首先在Gradle中添加依赖`implementation 'org.greenrobot:eventbus:3.3.1'`。然后,可选地定义事件类如`MessageEvent`。在活动或Fragment的`onCreate`中注册订阅者,在`onDestroy`中反注册。通过`@Subscribe`注解方法处理事件,如`onMessageEvent`。发送事件使用`EventBus.getDefault().post()`。
|
9天前
|
JavaScript 前端开发 Android开发
kotlin安卓在Jetpack Compose 框架下使用webview , 网页中的JavaScript代码如何与native交互
在Jetpack Compose中使用Kotlin创建Webview组件,设置JavaScript交互:`@Composable`函数`ComposableWebView`加载网页并启用JavaScript。通过`addJavascriptInterface`添加`WebAppInterface`类,允许JavaScript调用Android方法如播放音频。当页面加载完成时,执行`onWebViewReady`回调。
|
7天前
|
JavaScript iOS开发 开发者
pnpm的安装与配置(Windows/macOS)
pnpm的安装与配置(Windows/macOS)
32 0
|
7天前
|
资源调度 JavaScript iOS开发
yarn的安装与配置(Windows/macOS)
yarn的安装与配置(Windows/macOS)
60 0
|
10天前
|
安全 网络安全 API
kotlin安卓开发JetPack Compose 如何使用webview 打开网页时给webview注入cookie
在Jetpack Compose中使用WebView需借助AndroidView。要注入Cookie,首先在`build.gradle`添加WebView依赖,如`androidx.webkit:webkit:1.4.0`。接着创建自定义`ComposableWebView`,通过`CookieManager`设置接受第三方Cookie并注入Cookie字符串。最后在Compose界面使用这个自定义组件加载URL。注意Android 9及以上版本可能需要在网络安全配置中允许第三方Cookie。
|
24天前
|
存储 安全 Android开发
构建高效的Android应用:Kotlin与Jetpack的结合
【5月更文挑战第31天】 在移动开发的世界中,Android 平台因其开放性和广泛的用户基础而备受开发者青睐。随着技术的进步和用户需求的不断升级,开发一个高效、流畅且易于维护的 Android 应用变得愈发重要。本文将探讨如何通过结合现代编程语言 Kotlin 和 Android Jetpack 组件来提升 Android 应用的性能和可维护性。我们将深入分析 Kotlin 语言的优势,探索 Jetpack 组件的核心功能,并通过实例演示如何在实际项目中应用这些技术。
|
12天前
|
数据管理 API 数据库
探索Android Jetpack:现代安卓开发的利器
Android Jetpack是谷歌为简化和优化安卓应用开发而推出的一套高级组件库。本文深入探讨了Jetpack的主要构成及其在应用开发中的实际运用,展示了如何通过使用这些工具来提升开发效率和应用性能。
|
27天前
|
Java 数据库 Android开发
构建高效Android应用:Kotlin与Jetpack的完美结合
【5月更文挑战第28天】 在现代移动开发领域,Android平台以其广泛的用户基础和开放性受到开发者青睐。随着技术的不断进步,Kotlin语言以其简洁性和功能性成为Android开发的首选。而Android Jetpack组件则为开发者提供了一套高质量的设计架构、工具和UI组件,以简化应用程序的开发过程。本文将探讨如何利用Kotlin语言和Android Jetpack组件共同构建一个高效的Android应用程序,涵盖从语言特性到架构模式的全面分析,并提供具体的实践指导。
|
1月前
|
安全 数据库 Android开发
构建高效Android应用:采用Kotlin与Jetpack的实践指南
【5月更文挑战第22天】 在移动开发领域,Android系统因其开放性和广泛的用户基础而备受开发者青睐。随着技术的不断演进,Kotlin语言以其简洁性和功能性成为Android开发的首选语言。本文将深入探讨如何结合Kotlin和Android Jetpack组件来构建一个高效且易于维护的Android应用。我们将重点讨论如何使用Jetpack的核心组件,如LiveData、ViewModel和Room,以及Kotlin的语言特性来优化代码结构,提高应用性能,并简化数据管理。通过具体案例分析,本文旨在为开发者提供一套实用的技术指导,帮助他们在竞争激烈的市场中脱颖而出。