前言
Compose是Android官方提供的声明式UI开发框架,而Compose Multiplatform是由JetBrains 维护的,对于Android开发来说,个人认为学习Jetpack Compose是必须的,因为它会成为Android主流的开发模式,而compose-jb作为一个扩展能力,我们可以有选择的去尝试。今天我们先来了解一下使用compose-jb开发一个桌面端应用的流程。
接下来还会有第二弹,第三弹...
环境要求
开发Compose for Desktop环境要求主要有两点:
-
JDK 11或更高版本
-
IntelliJ IDEA 2020.3 或更高版本(也可以使用AS,这里为了使用IDEA提供的项目模板)
接着我们来一步步体验Compose for Desktop的开发流程。
开发流程
创建项目
下载好IDEA后,我们直接新建项目,选择Compose Multipalteform类型,输入项目名称,这里只选择Single platform且平台为Desktop即可。
创建好项目后,来看项目目录结构,目录结构如下图所示。
在配置文件中指定了程序入口为MainKt以及包名、版本号等。MainKt文件代码如下所示。
@Composable
@Preview
fun App() {
var text by remember { mutableStateOf("Hello, World!") }
MaterialTheme {
Button(onClick = {
text = "Hello, Desktop!"
}) {
Text(text)
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
在MainKt文件中,入口处调用了App()方法,App方法中绘制了一个按钮,运行程序,结果如下图所示。
我们可以看到一个Hello World的桌面端程序就显示出来了。接下来我们来添加一些页面元素。
添加输入框
为了让桌面端程序更“像样子”,我们首先修改桌面程序的标题为“学生管理系统”,这毕竟是我们学生时代最喜欢的名字。代码如下所示:
fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "学生管理系统") {
App()
}
}
在App方法中,添加两个输入框分别为学号、密码,添加一个登陆按钮,写法与Android中的Compose一致,代码如下所示。
MaterialTheme {
var name by remember {
mutableStateOf("")
}
var password by remember {
mutableStateOf("")
}
Column {
TextField(name, onValueChange = {
name = it
}, placeholder = {
Text("请输入学号")
})
TextField(password, onValueChange = {
password = it
}, placeholder = {
Text("请输入密码")
})
Button(onClick = {
}) {
Text("登陆")
}
}
}
再次运行程序,页面如下所示。
添加头像
接着我们再来添加头像显示,我们将下载好的图片资源放在resources目录下
然后使用Image组件将头像显示出来即可,代码如下所示。
Image(
painter = painterResource("photo.png"),
contentDescription = null,
modifier = Modifier.size(width = 100.dp, height = 100.dp)
.clip(CircleShape)
)
再次运行程序,结果如下所示。
当然我们还可以将布局稍微修饰一下,使得布局看起来更好看一些。但这并不是这里的重点。
添加退出弹窗
当我们点击左上角(macOS)的X号时,应用程序就直接退出了,这是因为在Window函数中指定了退出事件,再来看一下这部分代码,如下所示。
fun main() = application {
Window(onCloseRequest = ::exitApplication, title = "学生管理系统") {
App()
}
}
接下来我们增加一个确认退出的弹窗提醒。代码如下所示。
fun main() = application {
var windowsOpen by remember {
mutableStateOf(true)
}
var isClose by remember {
mutableStateOf(false)
}
if (windowsOpen) {
Window(onCloseRequest = { isClose = true }, title = "学生管理系统") {
App()
if (isClose) {
Dialog(onCloseRequest = { isClose = false }, title = "确定退出应用程序吗?") {
Row {
Button(onClick = {
windowsOpen = false
}) {
Text("确定")
}
}
}
}
}
}
}
这里我们新增了两个变量windowsOpen、isClose分别用来控制应用程序的Window是否显示与确认弹窗的显示。这部分代码相信使用过Jetpack Compose的都可以看得懂。
运行程序,点击X号,弹出退出确认弹窗,点击确定,应用程序将退出。效果如下图所示。
实现一个网络请求功能
在 Kotlin 跨平台开发入门 中我们借用「wanandroid」中「每日一问」接口实现了一个网络请求,现在我们将这部分功能移植到Desktop程序中,网络请求框架仍然使用Ktor,当然其实你也可以使用Retrofit,这一点并不重要。
首先添加Ktor的依赖,代码如下所示。
val jvmMain by getting {
dependencies {
implementation(compose.desktop.currentOs)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
val ktorVersion = "2.1.2"
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-android:$ktorVersion")
}
}
添加一个Api接口
object Api {
val dataApi = "https://wanandroid.com/wenda/list/1/json"
}
创建HttpUtil类,用于创建HttpClient对象和获取数据的方法,代码如下所示。
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
class HttpUtil {
private val httpClient = HttpClient {
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
}
/**
* 获取数据
*/
suspend fun getData(): String {
val rockets: DemoReqData =
httpClient.get(Api.dataApi).body()
return "${rockets.data} "
}
}
DemoReqData是接口返回数据对应映射的实体类,这里就不再给出了。
然后我们编写UI,点击按钮开始网络请求,代码如下所示。
Column() {
val scope = rememberCoroutineScope()
var demoReqData by remember { mutableStateOf(DemoReqData()) }
Button(onClick = {
scope.launch {
try {
demoReqData = HttpUtil().getData()
} catch (e: Exception) {
}
}
}) {
Text(text = "请求数据")
}
LazyColumn {
repeat(demoReqData.data?.datas?.size ?: 0) {
item {
Message(demoReqData.data?.datas?.get(it))
}
}
}
}
获取数据后,通过Message方法将数据展示出来,这里只将作者与标题内容显示出来,代码如下所示。
@Composable
fun Message(data: DemoReqData.DataBean.DatasBean?) {
Card(
modifier = Modifier
.background(Color.White)
.padding(10.dp)
.fillMaxWidth(), elevation = 10.dp
) {
Column(modifier = Modifier.padding(10.dp)) {
Text(
text = "作者:${data?.author}"
)
Text(text = "${data?.title}")
}
}
}
运行程序,点击“请求数据”,结果如下图所示。
这样我们就实现了一个简单的桌面端数据请求与显示功能。
写在最后
当然,在Compose For Desktop中还有许多的组件,比如Tooltips、Context Menu等等,这里无法一一介绍,需要我们在使用的时候去实践,我们将在后面的N弹中持续探索...