Jetpack Compose for Desktop 使用过程中遇到的几个大坑

2022-10-27 10:01:49 浏览数 (1)

本文最后更新于 187 天前,其中的信息可能已经有所发展或是发生改变。

Jetpack Compose for Desktop 使用过程中遇到的几个大坑

最近在用 Jetpack Compose for Desktop 写一些好玩的,用的时候遇到了很多大坑,在这里总结如下:

Binary distribution 无法访问 ClassLoader Resources

我们有时候一定会希望从 jar 内部读取资源,这个时候我们一般会使用 this::class.java.getResource。当你将你的应用打包为 Jar 时,这么做完全没有问题,但当你打包为 native 的 binary distribution 时,这些资源都将直接返回 null。目前还没有看到解决方案。 @See https://github.com/JetBrains/compose-jb/issues/2011

错误的使用二进制流方式从 ClassLoader Resources 中加载字体导致界面整体卡顿

当我们需要加载自定义字体时,我们首先看到的方式是

代码语言:javascript复制
fun Font(
    identity: String,
    data: ByteArray,
    weight: FontWeight = FontWeight.Normal,
    style: FontStyle = FontStyle.Normal
): Font = LoadedFont(identity, data, weight, style)

然而当你试图从 ClassLoader Resources 中传入字体的二进制流时,你就会发现这会导致界面十分卡顿,这可能是因为重复加载了二进制流导致的。

解决方案是,改用 ResourceFont

代码语言:javascript复制
fun Font(
    resource: String,
    weight: FontWeight = FontWeight.Normal,
    style: FontStyle = FontStyle.Normal
): Font = ResourceFont(resource, weight, style)

如此一来,卡顿问题便得到了解决

Tray 过晚被创建导致 Notification 被忽略

我们有时候需要向操作系统发送一些通知,这个时候就必须用到 Tray(托盘),为其绑定一个 TrayState,然后调用这个 TrayState 的 sendNotification 函数,向操作系统发送通知。但是需要注意的是,sendNotification 有一个限制:

代码语言:javascript复制
    /**
     * Send notification to tray. If [TrayState] is attached to [Tray], notification will be sent to
     * the platform. If [TrayState] is not attached then notification will be lost.
     */
    fun sendNotification(notification: Notification) {
        notificationChannel.trySend(notification)
    }

这会导致一个问题,如果你希望在窗口最小化的时候显示托盘,然后发送通知的话,考虑如下情况:

代码语言:javascript复制
fun main() = application {

    val isShowTray = remember { mutableStateOf(false) }

    if (isShowTray.value) {
        Tray(
            icon = BitmapPainter(useResource("/icons/logo.png") { loadImageBitmap(it) }),
            state = trayState,
            tooltip = getLang("application.tray.tooltip.title").asString,
            onAction = {
                isShowTray.value = false
                isMainWindowVisible.value = true
            }
        )
        isMainWindowVisible.value = false
    }
}

fun onMinimize(){
    isShowTray.value = true
    trayState.sendNotification(
        Notification("Title", "Contet", Notification.Type.Info)
    )
}

当我们调用 onMinimize 时,isShowTray 被设置为 true,此时托盘应该被显示,然后发送通知,但是结果是,通知并未被发送。

这实际上是当我们发送 Notification 时,此时 trayState 还未被 attach 到我们的 Tray 上,导致了这个问题。

要想解决这个问题,只需延迟一定时间再发送 Notification 即可

代码语言:javascript复制
fun onMinimize(){
    isShowTray.value = true
    scope.launch {
        delay(500)
        trayState.sendNotification(
            Notification("Title", "Contet", Notification.Type.Info)
    )
}

加载 SVG 图片时显示为全黑

当我们试图加载一个 SVG 图片时,会发现有部分图片显示为全黑,这是 skia 的一个已知问题,原因是其不支持 CSS Style,解决方案有两种: 1. 在导出 SVG 图片时不要将样式导出到 CSS,而是选择内联 (inline) 样式 2. 改用其他类型图片,如 PNG @see https://github.com/JetBrains/compose-jb/issues/1217

SVG 图片宽高比被错误计算导致 SVG 图片缩放不符合预期

有些 SVG 图片被加载后会被错误的认为其宽高比为 1:1,而不是正常的宽高。为了解决这个问题,(经过了一整天的研究),我设计了一个 ContentScale,只要你直到这张 SVG 图片的宽高比,手动录入后即可令结果恢复正常。

代码语言:javascript复制
class KnownScaleWrapperContentScale(
    private val scale: Float,
    private val baseContentScale: ContentScale
) : ContentScale {
    override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
        baseContentScale.computeScaleFactor(Size(srcSize.width * scale, srcSize.height), dstSize)
            .let { it.copy(it.scaleX * scale, it.scaleY) }
}

0 人点赞