在这里插入图片描述

每日一句正能量

清晨,是一个希望,一个梦想!不管昨天怎样低落,太阳照样升起;不管昨天怎样困苦,总会拥有今天的希望。人生因有梦想,而充满动力。不怕每天迈一小步,只怕你停滞不前;不怕每天做一点点事,只怕你每天无所事事。坚持,是生命的一种毅力!早安!

一、前言:PC端窗口交互的体验革新

随着HarmonyOS 5.0在PC设备上的全面落地,鸿蒙生态正式进入桌面办公与生产力工具的新战场。与移动端单窗口全屏体验不同,PC用户对多窗口并行操作、窗口智能布局有着刚性需求——这正是传统移动端开发思维需要转变的关键点。

本文将基于HarmonyOS 5.0.0+版本,从零构建一个具备窗口智能吸附多窗口协同联动跨窗口数据同步能力的PC级应用框架。通过实战代码演示,帮助开发者掌握鸿蒙PC窗口管理的核心API与最佳实践。

二、核心技术基础:鸿蒙PC窗口管理架构

2.1 窗口类型体系

HarmonyOS PC支持三种核心窗口形态,适配不同业务场景:

窗口类型 特性描述 适用场景
主窗口 应用入口,支持完整窗口操作(最大化/最小化/拖拽) 核心功能承载
子窗口 依赖主窗口,可设置模态/非模态,主窗口关闭时自动销毁 弹窗、设置面板、工具箱
独立窗口 与主窗口无依赖,支持跨屏幕拖拽 多文档编辑、多实例并行

2.2 核心API模块

实现PC级窗口交互主要依赖@ohos.window模块,关键API包括:

  • window.createSubWindow() - 创建子窗口
  • window.on('windowPositionChange') - 监听位置变化(吸附效果触发入口)
  • window.getPosition()/setPosition() - 获取/设置窗口坐标
  • window.getWindowSize() - 获取窗口尺寸
  • window.getScreenInfo() - 获取屏幕信息(用于边界限制)

三、实战案例:智能代码编辑器多窗口系统

3.1 项目概述与亮点

我们将开发一款分布式代码编辑器,核心亮点包括:

  1. 智能窗口吸附:子窗口靠近主窗口或屏幕边缘时自动对齐
  2. 多窗口协同:主窗口代码变更实时同步到预览窗口
  3. 状态联动:主窗口最大化时子窗口自动调整布局
  4. 跨窗口通信:基于AppStorage实现轻量级数据共享

3.2 工程配置

module.json5中声明窗口支持:

{
  "module": {
    "name": "CodeEditor",
    "type": "entry",
    "abilities": [
      {
        "name": "MainAbility",
        "srcEntry": "./ets/abilities/MainAbility.ets",
        "windowOptions": {
          "windowType": "normal",
          "maximizeEnable": true,
          "minimizeEnable": true,
          "fullScreenEnable": true
        }
      }
    ]
  }
}

3.3 主窗口初始化与配置

MainAbility.ets中完成主窗口的基础配置:

// abilities/MainAbility.ets
import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';

export default class MainAbility extends UIAbility {
  private mainWindow: window.Window | null = null;

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    // 获取主窗口实例
    this.mainWindow = await windowStage.getMainWindow();
    
    // 配置PC级窗口属性
    await this.configureMainWindow();
    
    // 加载主页面
    windowStage.loadContent('pages/EditorPage', (err: BusinessError) => {
      if (err.code) {
        console.error(`Failed to load content: ${err.code}`);
        return;
      }
      console.info('Main window content loaded successfully');
    });
  }

  private async configureMainWindow(): Promise<void> {
    if (!this.mainWindow) return;

    // 设置初始窗口尺寸(适配PC大屏)
    await this.mainWindow.setWindowSize({ 
      width: 1400, 
      height: 900 
    });
    
    // 设置初始位置(屏幕居中偏左)
    await this.mainWindow.setPosition({ x: 100, y: 80 });
    
    // 启用窗口标题栏
    await this.mainWindow.setTitle('HarmonyOS Code Editor');
    
    // 启用所有标准窗口操作
    await this.mainWindow.setMaximizeEnable(true);
    await this.mainWindow.setMinimizeEnable(true);
    await this.mainWindow.setFullScreenEnable(true);
    await this.mainWindow.setDraggable(true);
    await this.mainWindow.setResizeEnable(true);

    // 监听窗口状态变化(用于子窗口联动)
    this.mainWindow.on('windowStateChange', (state) => {
      console.info(`Main window state changed: ${state}`);
      // 状态变化时通知所有子窗口调整
      AppStorage.setOrCreate('mainWindowState', state);
    });
  }

  onWindowStageDestroy(): void {
    // 清理窗口资源
    this.mainWindow = null;
  }
}

3.4 智能窗口吸附系统实现

窗口吸附是PC级体验的核心特性。当用户拖拽子窗口靠近主窗口边缘或屏幕边界时,自动触发吸附对齐。

创建WindowAdsorbHelper.ets工具类:

// utils/WindowAdsorbHelper.ets
import { window } from '@kit.ArkUI';

// 吸附阈值(像素距离小于此值时触发吸附)
const ADSORB_THRESHOLD = 12;
// 屏幕底部预留任务栏高度
const TASKBAR_HEIGHT = 48;

export class WindowAdsorbHelper {
  private subWindow: window.Window | null = null;
  private mainWindow: window.Window | null = null;
  private isAdsorbEnabled: boolean = true;

  constructor(subWindow: window.Window, mainWindow: window.Window) {
    this.subWindow = subWindow;
    this.mainWindow = mainWindow;
  }

  /**
   * 启用窗口吸附监听
   */
  async enableAdsorb(): Promise<void> {
    if (!this.subWindow || !this.mainWindow) return;

    // 监听子窗口位置变化事件
    this.subWindow.on('windowPositionChange', async (position) => {
      if (!this.isAdsorbEnabled) return;
      
      try {
        await this.handlePositionChange(position);
      } catch (error) {
        console.error('Adsorb handling error:', error);
      }
    });

    console.info('Window adsorb enabled');
  }

  /**
   * 处理位置变化,计算吸附逻辑
   */
  private async handlePositionChange(currentPos: window.Position): Promise<void> {
    if (!this.subWindow || !this.mainWindow) return;

    // 获取当前窗口尺寸
    const currentSize = await this.subWindow.getWindowSize();
    // 获取主窗口信息
    const mainPos = await this.mainWindow.getPosition();
    const mainSize = await this.mainWindow.getWindowSize();
    // 获取屏幕信息
    const screenInfo = await window.getScreenInfo();
    const screenWidth = screenInfo.width;
    const screenHeight = screenInfo.height;

    let finalX = currentPos.x;
    let finalY = currentPos.y;
    let isAdsorbed = false;

    // ========== 主窗口边缘吸附逻辑 ==========
    
    // 1. 子窗口右边缘 → 主窗口左边缘吸附
    if (Math.abs(currentPos.x + currentSize.width - mainPos.x) < ADSORB_THRESHOLD) {
      finalX = mainPos.x - currentSize.width;
      isAdsorbed = true;
    }
    
    // 2. 子窗口左边缘 → 主窗口右边缘吸附
    if (Math.abs(currentPos.x - (mainPos.x + mainSize.width)) < ADSORB_THRESHOLD) {
      finalX = mainPos.x + mainSize.width;
      isAdsorbed = true;
    }
    
    // 3. 子窗口下边缘 → 主窗口上边缘吸附
    if (Math.abs(currentPos.y + currentSize.height - mainPos.y) < ADSORB_THRESHOLD) {
      finalY = mainPos.y - currentSize.height;
      isAdsorbed = true;
    }
    
    // 4. 子窗口上边缘 → 主窗口下边缘吸附
    if (Math.abs(currentPos.y - (mainPos.y + mainSize.height)) < ADSORB_THRESHOLD) {
      finalY = mainPos.y + mainSize.height;
      isAdsorbed = true;
    }

    // ========== 屏幕边缘吸附逻辑 ==========
    
    // 左屏幕边缘吸附
    if (Math.abs(finalX) < ADSORB_THRESHOLD) {
      finalX = 0;
      isAdsorbed = true;
    }
    
    // 右屏幕边缘吸附
    if (Math.abs(finalX + currentSize.width - screenWidth) < ADSORB_THRESHOLD) {
      finalX = screenWidth - currentSize.width;
      isAdsorbed = true;
    }
    
    // 上屏幕边缘吸附
    if (Math.abs(finalY) < ADSORB_THRESHOLD) {
      finalY = 0;
      isAdsorbed = true;
    }
    
    // 下屏幕边缘吸附(预留任务栏)
    if (Math.abs(finalY + currentSize.height - (screenHeight - TASKBAR_HEIGHT)) < ADSORB_THRESHOLD) {
      finalY = screenHeight - TASKBAR_HEIGHT - currentSize.height;
      isAdsorbed = true;
    }

    // 执行吸附:位置发生变化时更新窗口位置
    if (finalX !== currentPos.x || finalY !== currentPos.y) {
      await this.subWindow.setPosition(finalX, finalY);
      
      // 触发吸附反馈(可添加视觉或触觉反馈)
      if (isAdsorbed) {
        this.triggerAdsorbFeedback();
      }
    }
  }

  /**
   * 吸附反馈效果
   */
  private triggerAdsorbFeedback(): void {
    // 可在此添加吸附时的视觉动画或轻微震动反馈
    console.info('Window adsorbed');
  }

  /**
   * 临时禁用吸附(如用户按住特定键拖拽时)
   */
  setAdsorbEnabled(enabled: boolean): void {
    this.isAdsorbEnabled = enabled;
  }

  /**
   * 清理资源
   */
  destroy(): void {
    if (this.subWindow) {
      this.subWindow.off('windowPositionChange');
      this.subWindow = null;
    }
    this.mainWindow = null;
  }
}

3.5 多窗口协同与数据同步

实现主窗口(代码编辑)与子窗口(实时预览)的数据联动:

// pages/EditorPage.ets
import { window } from '@kit.ArkUI';
import { WindowAdsorbHelper } from '../utils/WindowAdsorbHelper';

@Entry
@Component
struct EditorPage {
  @State codeContent: string = '// 在此输入代码...\nfunction hello() {\n  console.log("Hello HarmonyOS PC!");\n}';
  @State isPreviewOpen: boolean = false;
  
  private mainWindow: window.Window | null = null;
  private previewWindow: window.Window | null = null;
  private adsorbHelper: WindowAdsorbHelper | null = null;

  aboutToAppear() {
    // 初始化全局状态存储
    AppStorage.setOrCreate('codeContent', this.codeContent);
    this.initializeMainWindow();
  }

  private async initializeMainWindow(): Promise<void> {
    try {
      const windowStage = await window.getLastWindowStage();
      this.mainWindow = await windowStage.getMainWindow();
    } catch (error) {
      console.error('Failed to get main window:', error);
    }
  }

  /**
   * 创建预览子窗口
   */
  private async createPreviewWindow(): Promise<void> {
    if (!this.mainWindow || this.previewWindow) return;

    try {
      const windowStage = await window.getLastWindowStage();
      const mainPos = await this.mainWindow.getPosition();
      const mainSize = await this.mainWindow.getWindowSize();

      // 创建预览子窗口,位于主窗口右侧
      this.previewWindow = await windowStage.createSubWindow('previewWindow', {
        windowType: window.WindowType.TYPE_APP_SUB_WINDOW,
        bounds: {
          x: mainPos.x + mainSize.width + 20,
          y: mainPos.y,
          width: 500,
          height: mainSize.height
        },
        isModal: false
      });

      // 配置子窗口
      await this.previewWindow.setTitle('代码预览');
      await this.previewWindow.setDraggable(true);
      await this.previewWindow.setResizeEnable(true);

      // 启用智能吸附
      this.adsorbHelper = new WindowAdsorbHelper(this.previewWindow, this.mainWindow);
      await this.adsorbHelper.enableAdsorb();

      // 监听主窗口状态变化,实现联动
      this.setupWindowLinkage();

      // 加载预览页面
      await this.previewWindow.loadContent('pages/PreviewPage');
      await this.previewWindow.show();

      this.isPreviewOpen = true;
    } catch (error) {
      console.error('Failed to create preview window:', error);
    }
  }

  /**
   * 设置窗口联动逻辑
   */
  private setupWindowLinkage(): void {
    if (!this.mainWindow || !this.previewWindow) return;

    // 监听主窗口最大化/还原状态
    AppStorage.link('mainWindowState').onChange((state) => {
      this.handleMainWindowStateChange(state as window.WindowState);
    });

    // 监听主窗口尺寸变化,调整子窗口
    this.mainWindow.on('windowSizeChange', async (size) => {
      if (!this.previewWindow) return;
      
      const mainPos = await this.mainWindow!.getPosition();
      const previewPos = await this.previewWindow.getPosition();
      
      // 如果预览窗口在主窗口右侧,保持相对位置
      if (previewPos.x > mainPos.x) {
        await this.previewWindow.setPosition({
          x: mainPos.x + size.width + 20,
          y: mainPos.y
        });
        await this.previewWindow.setWindowSize({
          width: 500,
          height: size.height
        });
      }
    });
  }

  /**
   * 处理主窗口状态变化
   */
  private async handleMainWindowStateChange(state: window.WindowState): Promise<void> {
    if (!this.previewWindow) return;

    switch (state) {
      case window.WindowState.STATE_MAXIMIZE:
        // 主窗口最大化时,预览窗口调整为侧边栏模式
        const screenInfo = await window.getScreenInfo();
        await this.previewWindow.setPosition({
          x: screenInfo.width - 400,
          y: 0
        });
        await this.previewWindow.setWindowSize({
          width: 400,
          height: screenInfo.height - TASKBAR_HEIGHT
        });
        break;
        
      case window.WindowState.STATE_NORMAL:
        // 主窗口还原时,预览窗口恢复默认尺寸
        const mainPos = await this.mainWindow!.getPosition();
        const mainSize = await this.mainWindow!.getWindowSize();
        await this.previewWindow.setPosition({
          x: mainPos.x + mainSize.width + 20,
          y: mainPos.y
        });
        await this.previewWindow.setWindowSize({
          width: 500,
          height: mainSize.height
        });
        break;
    }
  }

  /**
   * 代码内容变化时同步到预览窗口
   */
  private onCodeChange(newContent: string): void {
    this.codeContent = newContent;
    // 同步到全局存储,预览窗口通过监听获取更新
    AppStorage.set('codeContent', newContent);
  }

  /**
   * 关闭预览窗口
   */
  private async closePreviewWindow(): Promise<void> {
    if (this.previewWindow) {
      if (this.adsorbHelper) {
        this.adsorbHelper.destroy();
        this.adsorbHelper = null;
      }
      await this.previewWindow.destroy();
      this.previewWindow = null;
      this.isPreviewOpen = false;
    }
  }

  build() {
    Column() {
      // 顶部工具栏
      Row() {
        Text('HarmonyOS Code Editor')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        
        Blank()
        
        Button(this.isPreviewOpen ? '关闭预览' : '打开预览')
          .onClick(() => {
            if (this.isPreviewOpen) {
              this.closePreviewWindow();
            } else {
              this.createPreviewWindow();
            }
          })
      }
      .width('100%')
      .padding(16)
      .backgroundColor('#f5f5f5')

      // 代码编辑区
      TextArea({ text: this.codeContent })
        .width('100%')
        .height('90%')
        .backgroundColor('#1e1e1e')
        .fontColor('#d4d4d4')
        .fontFamily('Consolas, monospace')
        .fontSize(14)
        .onChange((value) => this.onCodeChange(value))
    }
    .width('100%')
    .height('100%')
  }

  aboutToDisappear() {
    this.closePreviewWindow();
  }
}

3.6 预览窗口页面实现

预览窗口通过AppStorage监听代码变化:

// pages/PreviewPage.ets
@Entry
@Component
struct PreviewPage {
  @StorageLink('codeContent') codeContent: string = '';
  @State highlightedCode: string = '';

  aboutToAppear() {
    this.updateHighlight();
  }

  onPageShow() {
    // 页面显示时更新高亮
    this.updateHighlight();
  }

  /**
   * 简单的语法高亮处理
   */
  private updateHighlight(): void {
    // 实际项目中可接入专业的高亮库
    this.highlightedCode = this.codeContent;
  }

  build() {
    Column() {
      // 预览窗口标题栏
      Row() {
        Text('实时预览')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        Blank()
        
        Text(`${this.codeContent.length} 字符`)
          .fontSize(12)
          .fontColor('#666')
      }
      .width('100%')
      .padding(12)
      .backgroundColor('#f0f0f0')

      // 代码展示区域
      Scroll() {
        Text(this.highlightedCode)
          .width('100%')
          .padding(16)
          .fontFamily('Consolas, monospace')
          .fontSize(13)
          .fontColor('#333')
          .lineHeight(20)
      }
      .width('100%')
      .height('90%')
      .backgroundColor('#fafafa')
    }
    .width('100%')
    .height('100%')
  }
}

四、进阶优化与最佳实践

4.1 窗口边界限制

防止窗口被拖出屏幕可视区域:

// 在吸附逻辑中添加边界检查
private async constrainToScreen(position: window.Position, size: window.Size): Promise<window.Position> {
  const screenInfo = await window.getScreenInfo();
  
  let x = Math.max(0, Math.min(position.x, screenInfo.width - size.width));
  let y = Math.max(0, Math.min(position.y, screenInfo.height - TASKBAR_HEIGHT - size.height));
  
  return { x, y };
}

4.2 多显示器适配

HarmonyOS PC支持多屏扩展,可通过window.getScreenInfo()获取当前屏幕参数,在多显示器环境下精确定位窗口。

4.3 性能优化建议

  1. 防抖处理:窗口位置变化事件触发频繁,吸附计算建议使用防抖函数
  2. 异步操作:所有窗口API均为异步,避免同步阻塞UI线程
  3. 资源释放:子窗口关闭时务必移除事件监听,防止内存泄漏

五、总结与展望

本文通过构建一个具备智能吸附、多窗口协同的代码编辑器,系统演示了HarmonyOS PC窗口管理的核心能力。关键要点包括:

  1. 窗口吸附通过监听windowPositionChange事件,结合距离计算实现智能对齐
  2. 多窗口协同利用AppStorage实现轻量级跨窗口数据同步
  3. 状态联动通过监听主窗口状态变化,自动调整子窗口布局

随着HarmonyOS 6.0的发布,窗口管理能力进一步增强,新增了setRelativePositionToParentWindowEnabled等接口,支持子窗口与主窗口位置联动。建议开发者持续关注官方API演进,构建更完善的PC级应用体验。

完整代码示例已按HarmonyOS 5.0.0+版本规范编写,可直接在DevEco Studio 4.0及以上版本中运行测试。


转载自:https://blog.csdn.net/u014727709/article/details/160086066
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐