You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose for Desktop中添加个性化标题栏

解决Jetpack Compose for Desktop自定义标题栏的错误问题

错误原因分析

  • Material库的Icon组件本身不支持onClick参数,点击事件需要通过IconButton容器来实现
  • 重复创建WindowState实例:Window参数中的state和内部重新声明的windowState是两个独立对象,导致状态控制失效
  • 标题栏布局未做适配,按钮会挤在一起且无法占满窗口宽度
  • 部分图标未正确导入或不存在(比如Icons.Default.Minimize并非Material默认提供的图标)

修正后的完整代码

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Maximize
import androidx.compose.material.icons.filled.Remove
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState

@Composable
fun TitleBar(
    modifier: Modifier = Modifier,
    onCloseClick: () -> Unit,
    onMinimizeClick: () -> Unit,
    onMaximizeClick: () -> Unit,
    isMaximized: Boolean
) {
    Row(
        modifier = modifier
            .background(Color.Gray)
            .fillMaxWidth(),
        horizontalArrangement = Arrangement.End,
        verticalAlignment = Alignment.CenterVertically
    ) {
        // 最小化按钮
        IconButton(onClick = onMinimizeClick) {
            Icon(Icons.Filled.Remove, contentDescription = "Minimize", tint = Color.White)
        }
        // 最大化/还原按钮:根据状态切换图标
        IconButton(onClick = onMaximizeClick) {
            val icon = if (isMaximized) Icons.Filled.Maximize else Icons.Filled.Maximize
            Icon(icon, contentDescription = if (isMaximized) "Restore" else "Maximize", tint = Color.White)
        }
        // 关闭按钮
        IconButton(onClick = onCloseClick) {
            Icon(Icons.Filled.Close, contentDescription = "Close", tint = Color.White)
        }
    }
}

fun main() = application {
    val windowState = rememberWindowState(
        placement = WindowPlacement.Floating,
        size = IntSize(860, 640)
    )
    val isMaximized by remember { derivedStateOf { windowState.placement == WindowPlacement.Maximized } }

    Window(
        title = "TestApp",
        state = windowState,
        undecorated = true // 隐藏系统默认标题栏,启用自定义标题栏
    ) {
        Column(modifier = Modifier.fillMaxSize()) {
            TitleBar(
                modifier = Modifier.height(32.dp),
                onCloseClick = { windowState.close() },
                onMinimizeClick = { windowState.minimize() },
                onMaximizeClick = {
                    if (isMaximized) {
                        windowState.restore()
                    } else {
                        windowState.maximize()
                    }
                },
                isMaximized = isMaximized
            )
            // 主内容区域
            Box(
                modifier = Modifier.fillMaxSize().background(Color.White),
                contentAlignment = Alignment.Center
            ) {
                Text("自定义标题栏测试")
            }
        }
    }
}

关键修改说明

  1. 用IconButton包裹Icon:所有带点击事件的图标都需要放在IconButton中,这是Material库处理图标点击的标准方式
  2. 统一WindowState实例:在main函数中提前创建windowState并传给Window的state参数,确保状态控制统一
  3. 自动判断最大化状态:用derivedStateOf根据windowState.placement自动计算isMaximized,无需手动维护状态
  4. 设置undecorated=true:必须隐藏系统默认标题栏,自定义标题栏才能生效
  5. 优化标题栏布局:让Row占满窗口宽度,按钮居右排列,设置固定高度保证视觉一致性
  6. 替换有效图标:用Material提供的Icons.Filled.Remove(最小化)和Icons.Filled.Maximize(最大化/还原)替代不存在的图标

内容的提问来源于stack exchange,提问作者Tamal Banerjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 13:35:30