查看原文
其他

Jetpack Compose for Desktop 初体验

fundroid AndroidPub 2022-05-15


随着去年底 Jetpack Compose for Desktop 的正式发布, Compose 终于迈向了跨平台的道路,再配合 Kotlin MultiPlatform ,前景不可限量。Kotlin 生态的不断壮大,对于广大 Android 开发者来说也是一个福音。

从 https://github.com/JetBrains/compose-jb 中可以获得项目源码和Sample,本文基于其中一些 Sample 来一个上手体验。

运行环境如下:

  • macOS Catalina 10.15.7
  • IntelliJ IDEA 2020.3 (Community Edition)
  • AdoptOpenJDK 14.0.2
  • Kotlin 1.4.20
  • Desktop Compose 0.2.0-build132

1. 显示图片

fun main() {
    Window {
        Image(
            bitmap = TODO("Create ImageBitmap"),
            modifier = Modifier.fillMaxSize()
        )
    }
}


创建 ImageBitmap有以下几种方法:

1.1 从Resource中加载

从Resource目录中加载图片

bitmap = imageResource("sample.png"),

注意 imageResource 是 @Composable 函数,只能在 @Composable 中调用。

除了png等一般图片外,还可以加载xml格式的矢量图,此时需要使用 vectorXmlResource 方法

imageVector = vectorXmlResource("images/compose-logo.xml"),

1.2 从Device加载

从设备的指定路径中加载图片文件

bitmap = Image.makeFromEncoded(File("sample.png").readBytes()).asImageBitmap(),

1.3 Canvas绘制

除了加载资源外,也可以直接在Canvas上绘制图片。例子比较长,此处省略。


2. APP图标

创建APP窗口的Window时,可以指定APP图标

fun main() {
    Window(icon = ImageIO.read(File("sample.png"))) {
    }
}

3. 托盘图标

在onActive中可以设置系统托盘图标,menu必须设置否则无法显示

fun main() {
    Window {
        onActive {
            val tray = Tray().apply {
                icon(ImageIO.read(File("sample.png")))
                menu(
                    MenuItem(
                        name = "Quit App",
                        onClick = { AppManager.exit() }
                    )
                )
            }
            onDispose {
                tray.remove()
            }
        }
    }
}

4. 滚动条

Scrollbar的显示至少要完成以下配置:

fun main() {
    Window {
        Box {
            val stateVertical = rememberScrollState(0f)
            val stateHorizontal = rememberScrollState(0f)

            ScrollableColumn(scrollState = stateVertical) {
                ScrollableRow(scrollState = stateHorizontal) {
                    Column {
                        for (item in 0..100) {
                            Box { Text("Item in ScrollableColumn #$item") }
                        }
                    }
                }
            }
            VerticalScrollbar(adapter = rememberScrollbarAdapter(stateVertical))
            HorizontalScrollbar(adapter = rememberScrollbarAdapter(stateHorizontal))
        }
    }
}
  1. Compose是基于State驱动的,所以需要通过 rememberScrollState() 为各个滚动条创建 ScrollState
  2. 将创建的state传递给 ScrollableColumn 和 ScrollableRow
  3. 通过 rememberScrollbarAdapter(scrollState: ScrollState) 创建 ScrollAdapter
  4. 将adapter传递给 Scrollbar

5. 鼠标事件

在UI中通过Modifier.clickable监听鼠标的Click事件回调

5.1 Click事件

Box(
    modifier = Modifier.clickable(
        onClick = {
        },
        onDoubleClick = {
        },
        onLongClick = {
        }
    )
)

5.2 鼠标移动

Modifier.pointerMoveFilter监听鼠标移动

Box(
    modifier = Modifier.pointerMoveFilter(
        onMove = {
            false
        },
        onEnter = {
            false
        },
        onExit = {
            false
        }
    )
)
  • onMove: 获取鼠标当前坐标
  • onEnter: 鼠标进入此View的区域
  • onExit: 鼠标移除此区域

6 键盘事件

下面的例子中,我们在 TextField 中输入的文字通过 “Cmd+Enter” 会出现在上面 Text 的 "Apply text:"中;通过 "Cmd+R" 会清空 Text

@OptIn(ExperimentalKeyInput::class)
fun main() = Window {
    MaterialTheme {
        var applyText by remember { mutableStateOf("") }
        var inputText by remember { mutableStateOf("") }
        Column {
            Text("Apply text: $applyText")
            TextField(
                value = inputText,
                onValueChange = { inputText = it },
                modifier = Modifier.shortcuts {
                    on(Key.CtrlLeft + Key.Enter) {
                        applyText = inputText
                        inputText = ""
                    }
                    on(Key.CtrlLeft + Key.R) {
                        applyText = ""
                        inputText = ""
                    }
                }
            )
        }
    }
}

KeyInut 相关的API目前还是Experimental状态,后期有可能会变化,需注意。

另外,通过 AppWindow 的 keyboard 属性,可以定义一些全局快捷键,如下:

AppWindow().also {
    it.keyboard.setShortcut(Key.Escape) {
        it.close()
    }
}.show {
}

7. 兼容Java Swing

Swing 是我们常用的桌面端GUI库,Compose可以在Swing中使用,ComposePanel 可以作为Composable的容器使用:

fun main() {
    val window = JFrame()
    val composePanel = ComposePanel()
    window.contentPane.add(composePanel, BorderLayout.CENTER)
    composePanel.setContent {
        Box {
            Text("text")
        }
    }
    window.setSize(100100)
    window.isVisible = true
}

最后

虽然Kotlin-Multiplatform在跨平台领域早已显露头角,但多是用于逻辑层,如今Compose的出现把UI层的短板也补齐了,这将大大提升Kotlin在跨平台领域的整体竞争力,对Kotlin的未来无限看好!

https://github.com/JetBrains/compose-jb/tree/master/tutorials




↓关注公众号↓↓添加微信交流↓


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存