鸿蒙PC应用开发:窗口管理 + 鼠标键盘交互 + 文件对话框

一、PC 开发和平板有什么本质区别
在手机上做鸿蒙开发,窗口大小基本固定、交互全靠触摸、文件操作很少涉及。但到了 PC 上,窗口可以自由缩放和最大化、鼠标有悬停和右键、键盘有快捷键、应用需要读写本地文件系统。这些能力在手机上要么不需要、要么体验很弱,PC 开发本质上是要补全这些桌面场景的能力。
HarmonyOS NEXT 的 PC 应用开发,是在 ArkUI 基础上叠加一套完整的桌面开发能力:窗口管理 API、鼠标键盘事件、文件选择器、本地存储。
二、窗口管理:最大化、最小化、置顶
PC 应用最基础的能力是窗口控制。用户习惯了自己拖动标题栏、点最大化、把窗口置顶,ArkUI 提供了完整的窗口操作 API:
import Window from '@ohos.window'
import promptAction from '@ohos.promptAction'
const mainWindow = Window.getLastWindowSync(getContext(this))
async function minimizeWindow() {
await mainWindow.minimize()
}
async function toggleMaximize() {
const isMax = await mainWindow.isMaximized()
if (isMax) await mainWindow.restore()
else await mainWindow.maximize()
}
async function setFullScreen(enabled: boolean) {
await mainWindow.setFullScreen(enabled)
}
async function setAlwaysOnTop(onTop: boolean) {
await mainWindow.setTopOnOtherEnabled(onTop)
promptAction.showToast({ message: onTop ? '已置顶' : '已取消置顶', duration: 1500 })
}
自定义标题栏组件,包含上述所有窗口控制能力:
@Builder
CustomTitleBar() {
Row({ space: 12 }) {
Row({ space: 8 }) {
Text('📝').fontSize(20)
Text('笔记应用').fontSize(15).fontWeight(FontWeight.Medium)
}
Blank()
Row({ space: 4 }) {
Text('📌').fontSize(16).padding(8).onClick(() => this.setAlwaysOnTop(!this.isOnTop))
Text('⛶').fontSize(16).padding(8).onClick(() => this.setFullScreen(!this.isFullScreen))
Text('−').fontSize(18).fontWeight(FontWeight.Bold).padding(8).onClick(() => this.minimizeWindow())
Text('□').fontSize(16).padding(8).onClick(() => this.toggleMaximize())
Text('✕').fontSize(14).padding(8).fontColor('#FF4757').onClick(() => mainWindow.close())
}
}
.width('100%').height(44).padding({ left: 16, right: 16 }).backgroundColor('#1A1A2E')
}

三、窗口监听:实时响应大小变化
PC 应用需要实时响应窗口大小改变。用户拖动窗口边缘,布局要跟着自动调整,不能出现内容溢出或大量空白。通过 window.on('windowSizeChange') 监听窗口尺寸变化:
@State windowWidth: number = 1024
@State windowHeight: number = 768
private windowChangeCallback = (size: Window.Size) => {
this.windowWidth = px2vp(size.width)
this.windowHeight = px2vp(size.height)
console.info(`[Window] ${this.windowWidth}vp × ${this.windowHeight}vp`)
}
aboutToAppear() {
const win = Window.getLastWindowSync(getContext(this))
win.on('windowSizeChange', this.windowChangeCallback)
const props = win.getWindowProperties()
this.windowWidth = px2vp(props.windowWidth)
this.windowHeight = px2vp(props.windowHeight)
}
aboutToDisappear() {
const win = Window.getLastWindowSync(getContext(this))
win.off('windowSizeChange') // 必须取消监听,否则内存泄漏
}
最常见的坑:每次 aboutToAppear 都会注册新监听,如果不在 aboutToDisappear 中清理,监听器会越叠越多直到内存溢出。窗口监听必须配对注册和取消,这是硬性规范。
四、鼠标交互:悬停和右键菜单
PC 上鼠标比触摸有更多交互状态——进入、离开、悬停、右键。ArkUI 中这些都有对应 API:
@State hoverText: string = '将鼠标移入蓝色区域'
@State contextMenuVisible: boolean = false
@State contextMenuX: number = 0
@State contextMenuY: number = 0
// 悬停效果
Column()
.width(240).height(80).backgroundColor('#667eea').borderRadius(12)
.onHover((isHover: boolean) => {
animateTo({ duration: 200 }, () => {
this.hoverText = isHover ? '✅ 鼠标已悬停' : '将鼠标移入蓝色区域'
})
})
.child(Text(this.hoverText).fontSize(13).fontColor('#FFFFFF')
.width('100%').textAlign(TextAlign.Center))
.alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
// 右键菜单(需要自己实现浮层)
if (this.contextMenuVisible) {
Column({ space: 2 }) {
ForEach(['📋 复制', '📝 粘贴', '✂️ 剪切', '🗑 删除'], (item: string) => {
Text(item).fontSize(13).fontColor('#333333')
.width('100%').padding({ left: 12, top: 8, bottom: 8 })
.onClick(() => {
this.contextMenuVisible = false
promptAction.showToast({ message: `执行: ${item}`, duration: 1500 })
})
})
}
.width(160).backgroundColor('#FFFFFF').borderRadius(8)
.shadow({ radius: 8, color: 'rgba(0,0,0,0.15)', offsetX: 2, offsetY: 2 })
.position({ x: this.contextMenuX, y: this.contextMenuY })
}

ArkUI 没有内置右键菜单组件,需要自己实现:监听右键事件获取坐标,然后渲染浮层。关闭逻辑是监听全局左键点击,菜单外点击时隐藏。
五、键盘快捷键
键盘快捷键是 PC 效率应用的核心。Ctrl+S 保存、Ctrl+N 新建、Ctrl+F 搜索、Escape 关闭——这些约定俗成的交互不容忽视:
function handleKeyDown(event: KeyEvent) {
if (event.type === KeyType.Down && event.ctrlKey) {
switch (event.keyCode) {
case 49: // Ctrl+S 保存
this.saveData()
break
case 50: // Ctrl+N 新建
this.createNewNote()
break
case 52: // Ctrl+F 搜索
this.focusSearch()
break
}
}
if (event.type === KeyType.Down && event.keyCode === 2) { // Escape
this.closeDialog()
}
}
// 快捷键提示 UI
@Builder
ShortcutBar() {
Column({ space: 8 }) {
Text('⌨️ 快捷键').fontSize(14).fontWeight(FontWeight.Medium).width('100%')
ForEach([
{ keys: 'Ctrl + S', action: '保存', color: '#667eea' },
{ keys: 'Ctrl + N', action: '新建', color: '#43E97B' },
{ keys: 'Ctrl + F', action: '搜索', color: '#4facfe' },
{ keys: 'Escape', action: '关闭', color: '#FF4757' },
], (item) => {
Row() {
Text(item.keys).fontSize(12).fontWeight(FontWeight.Medium).fontColor(item.color)
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
.backgroundColor(item.color + '20').borderRadius(6)
Text(item.action).fontSize(13).fontColor('#666666').margin({ left: 12 })
}.width('100%')
})
}
.width('100%').padding(12).backgroundColor('#FFFFFF').borderRadius(12)
}
六、文件对话框:读写本地文件
PC 应用最核心的能力之一是访问本地文件系统。@ohos.file.picker 负责唤起系统文件选择器,@ohos.file.fs 负责读写文件内容:
import filePicker from '@ohos.file.picker'
import file from '@ohos.file.fs'
// 打开文件选择器
async function pickFile(): Promise<string | null> {
try {
const picker = new filePicker.DocumentPicker()
const result = await picker.select({ multiSelect: false })
return result?.[0] ?? null
} catch (e) {
console.error('File pick failed:', JSON.stringify(e))
return null
}
}
// 另存为对话框
async function saveFile(content: string, defaultName: string): Promise<boolean> {
try {
const savePath = await picker.save({ newFileName: defaultName })
if (savePath) {
await file.writeText(savePath, content)
promptAction.showToast({ message: '文件已保存', duration: 2000 })
return true
}
return false
} catch (e) {
console.error('Save failed:', JSON.stringify(e))
return false
}
}
// 读取文件
async function readFile(filePath: string): Promise<string> {
try {
return await file.readText(filePath)
} catch (e) {
console.error('Read failed:', JSON.stringify(e))
return ''
}
}
文件操作按钮组 UI:
@Builder
FileOperationBar() {
Column({ space: 12 }) {
Text('📁 文件操作').fontSize(14).fontWeight(FontWeight.Bold).width('100%')
Row({ space: 10 }) {
Button('📂 打开', { type: ButtonType.Normal })
.fontSize(13).height(38).borderRadius(8).backgroundColor('#667eea')
.onClick(async () => {
const path = await this.pickFile()
if (path) promptAction.showToast({ message: `已选择: ${path}`, duration: 2000 })
})
Button('💾 导出', { type: ButtonType.Normal })
.fontSize(13).height(38).borderRadius(8).backgroundColor('#43E97B')
.onClick(async () => {
const content = this.notes.map(n => `# ${n.title}\n\n${n.content}\n\n---\n`).join('')
await this.saveFile(content, 'notes.md')
})
}.width('100%')
}.width('100%').padding(16).backgroundColor('#FFFFFF').borderRadius(12)
}
七、实战建议
PC 应用开发有两条硬性规范:窗口操作必须配对注册和取消、文件访问必须处理异常。这两条踩过一次的坑,基本不会再犯。
窗口控制按钮的视觉设计要和系统风格一致。Windows 平台的窗口控制按钮在右上角,如果应用有自定义标题栏,三个控制按钮的视觉风格要尽量和系统一致,避免用户感到割裂。
右键菜单实现逻辑不复杂,但菜单项不宜过多,通常 4-6 项最佳,层级控制在两层以内。快捷键功能的优先级:Ctrl+S 和 Escape 必须有,是最高频的操作;Ctrl+N 和 Ctrl+F 其次;其他根据业务需求决定。
更多推荐

所有评论(0)