使用Jetpack Compose开发PC桌面应用

d68de31190ba4c7b64335861b9a552e0.jpeg

/   今日科技快讯   /

近日,市场研究公司Motor Intelligence发布的最新报告显示,尽管2022年美国整体汽车市场出现萎缩,但电动汽车销量飙升了三分之二。同时,传统汽车制造商推出大量混合动力车型,这帮助缩小了电动汽车巨头特斯拉的领先优势。Motor Intelligence的数据显示,汽车制造商去年在美国共售出了807180辆电动汽车,占汽车总销量的5.8%,高于2021年的3.2%。相比之下,2022年美国汽车总销量同比下降8%。

/   作者简介   /

大家周一好,春节将至,努力坚守好最后一班岗~

本篇文章转自黄林晴的博客,文章主要分享了如何使用Compose for Desktop进行开发应用,相信会对大家有所帮助!

原文地址:

https://juejin.cn/post/7185423734864347193

/   前言   /

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即可。

9941381b25c27e979475a26d05721826.png

创建好项目后,来看项目目录结构,目录结构如下图所示。

18fdf80a0a71da09d2c792bfaeba085e.png

在配置文件中指定了程序入口为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方法中绘制了一个按钮,运行程序,结果如下图所示。

750138ba93eb5723dacb3f5ba535430b.png

我们可以看到一个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("登陆")
        }
    }

}

再次运行程序,页面如下所示。

dfe9718059e0fa3976adde74cc8dc694.png

添加头像

接着我们再来添加头像显示,我们将下载好的图片资源放在resources目录下。

4dfd686400fe6ef46eb1e89ccce5bcaf.png

然后使用Image组件将头像显示出来即可,代码如下所示。

Image(
    painter = painterResource("photo.png"),
    contentDescription = null,
    modifier = Modifier.size(width = 100.dp, height = 100.dp)
        .clip(CircleShape)
)

再次运行程序,结果如下所示。

229f1c08808f8bfb535760bd33ae08f4.png

当然我们还可以将布局稍微修饰一下,使得布局看起来更好看一些。但这并不是这里的重点。

添加退出弹窗

当我们点击左上角(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号,弹出退出确认弹窗,点击确定,应用程序将退出。效果如下图所示。

ad8c99d0531c12e0ae479a1ec42d77fa.png

实现一个网络请求功能

在 KMM入门中我们借用「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}")
        }
    }
}

运行程序,点击“请求数据”,结果如下图所示。

4198f2c7ec7a1e1b6af4ca8e41048006.png

这样我们就实现了一个简单的桌面端数据请求与显示功能。

/   写在最后   /

当然,在Compose For Desktop中还有许多的组件,比如Tooltips、Context Menu等等,这里无法一一介绍,需要我们在使用的时候去实践,我们将在后面的N弹中持续探索...

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Android 13 Developer Preview一览

2022年终总结,我的10年Android之旅

欢迎关注我的公众号

学习技术或投稿

287e7248bbc8d7f5d74c7e77e3d85cbb.png

b94e8d9daf91194493c43b7c4c55bcea.jpeg

长按上图,识别图中二维码即可关注

Compose返回的图片格式可以是Painter类型的对象。在使用Compose展示图片时,可以使用Image函数,其中一个函数的第一个入参是Painter类型的对象。可以通过使用rememberImagePainter函数来创建Painter对象,该函数接受一个图片的URL作为参数。例如: ``` Image(rememberImagePainter(url), "") ``` 此外,还可以使用Coil库来加载网络图片或其他本地路径下的文件。Coil提供了rememberAsyncImagePainter函数来创建异步加载图片的Painter对象。可以通过指定图片的URL来加载网络图片。例如: ``` Image( painter = rememberAsyncImagePainter("https://picsum.photos/300/300"), contentDescription = null ) ``` 总之,Compose返回的图片格式是Painter类型的对象,可以通过不同的函数来创建和加载不同类型的图片。 #### 引用[.reference_title] - *1* [Compose-jb图片加载库load-the-image,适用于KMM Compose desktop桌面端(Windows,Linux,MacOs)](https://blog.csdn.net/qq_33505109/article/details/125194044)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Jetpack Compose 中的基础组件](https://blog.csdn.net/lyabc123456/article/details/131059925)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值