鸿蒙 ArkTS 布局深入浅出:Panel 组件三种模式(Mini / Half / Full)完全解析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前言

在 HarmonyOS NEXT(API 24)的应用开发中,UI 布局是绕不开的核心话题。ArkTS 作为鸿蒙原生声明式开发语言,提供了一系列强大且易用的布局组件。其中,Panel(面板)组件 是一个非常实用的容器类控件——它从屏幕底部滑入,支持三种高度模式切换,广泛应用于播放器、设置面板、评论区域等需要「展开-收起」交互的场景。

本文将围绕 Panel 的三种模式——Mini(最小)Half(半屏)Full(全屏)——展开详细讲解,并附上完整可运行的示例代码,帮助读者快速掌握这一布局利器。


一、Panel 组件概述

1.1 什么是 Panel

Panel 是 ArkUI 提供的一种可拖拽、可切换高度模式的底部面板组件。与传统的 Dialog 或 BottomSheet 不同,Panel 具有以下特性:

  • 三种预设高度模式:迷你、半屏、全屏,可通过代码或手势切换
  • 内置拖拽条(dragBar):用户可直接拖拽面板边缘切换模式
  • 灵活的高度自定义:每种模式均可独立设置高度值
  • 圆角和阴影支持:视觉层次感强
  • 回调监听完善:模式变化、可见性变化均可捕捉

1.2 适用场景

场景 推荐模式 说明
音乐播放器迷你栏 Mini 展示歌曲名 + 播放控制按钮
评论列表 / 弹幕面板 Half 半屏展示,留出内容区域
商品详情 / 文章阅读 Full 全屏沉浸式浏览
设置选项 / 筛选器 Half 半屏表单,操作后自动收起
地图底部的路线面板 Half → Full 拖拽渐变展开

二、Panel 三种模式详解

Panel 的核心能力围绕 PanelMode 枚举展开。API 24 中该枚举的三个成员为:PanelMode.MiniPanelMode.HalfPanelMode.Full

注意:早期 API(如 API 11~12)中枚举值名为 MIN / HALF / FULL(全大写),API 24 统一为 PascalCase 风格 Mini / Half / Full,迁移时需相应调整。

Mini 模式

面板的最小高度形态,由 .miniHeight() 属性控制(单位为 vp)。适用于展示简短的提示信息、播放器迷你控制栏、通知预览等轻量场景。

Half 模式

面板展开至屏幕约一半高度,由 .halfHeight() 属性控制(支持百分比与数值)。适用于列表选择器、评论输入区域、配置选项面板等中等体量的内容。

Full 模式

面板几乎占满屏幕(通常 90%~95%),由 .fullHeight() 属性控制。适用于详情展示页面、编辑器和媒体全屏控制面板等需要大面积展示的场景。

三种模式之间可以通过手势拖拽无缝切换,体验流畅自然。


三、项目实战:三种模式切换演示

3.1 核心代码实现

以下为基于 API 24 的 Panel 演示页面的完整实现,已通过 hvigorw assembleApp 编译验证。

@Entry
@Component
struct PanelDemo {
  @State isPanelShow: boolean = false;
  @State currentMode: PanelMode = PanelMode.Mini;
  @State modeLabel: string = 'Mini(最小)';

  private readonly modeList: PanelMode[] = [
    PanelMode.Mini, PanelMode.Half, PanelMode.Full
  ];
  private readonly modeLabels: string[] = [
    'Mini(最小)', 'Half(半屏)', 'Full(全屏)'
  ];

  switchToNextMode(): void {
    const i = this.modeList.indexOf(this.currentMode);
    const next = (i + 1) % this.modeList.length;
    this.currentMode = this.modeList[next];
    this.modeLabel = this.modeLabels[next];
  }

  setMode(mode: PanelMode, label: string): void {
    this.currentMode = mode;
    this.modeLabel = label;
    if (!this.isPanelShow) this.isPanelShow = true;
  }

  build() {
    Stack() {
      // 背景内容区域
      Column() {
        Text('Panel 三种模式演示')
          .fontSize(24).fontWeight(FontWeight.Bold).margin({ top: 24 });
        Text(`当前模式:${this.modeLabel}`)
          .fontSize(18).margin({ top: 12, bottom: 24 });

        // 方式一:轮换切换
        Button('切换至下一模式')
          .width('90%').height(48).type(ButtonType.Capsule)
          .backgroundColor('#FF007AFF').fontColor(Color.White)
          .onClick(() => { this.switchToNextMode(); this.isPanelShow = true; })
          .margin({ bottom: 24 });

        // 方式二:直接跳转
        Row() {
          Button('MIN').height(44).layoutWeight(1).type(ButtonType.Capsule)
            .backgroundColor(this.currentMode === PanelMode.Mini
              ? '#FF007AFF' : '#FFCCCCCC')
            .onClick(() => this.setMode(PanelMode.Mini, 'Mini(最小)'));
          Button('HALF').height(44).layoutWeight(1).type(ButtonType.Capsule)
            .backgroundColor(this.currentMode === PanelMode.Half
              ? '#FF007AFF' : '#FFCCCCCC')
            .onClick(() => this.setMode(PanelMode.Half, 'Half(半屏)'));
          Button('FULL').height(44).layoutWeight(1).type(ButtonType.Capsule)
            .backgroundColor(this.currentMode === PanelMode.Full
              ? '#FF007AFF' : '#FFCCCCCC')
            .onClick(() => this.setMode(PanelMode.Full, 'Full(全屏)'));
        }.width('90%').margin({ bottom: 24 });
      }.width('100%').height('100%').backgroundColor('#FFF5F5F5');

      // Panel 组件
      Panel(this.isPanelShow) {
        Column() {
          Text('▼ 向上拖拽可切换模式').fontSize(14).margin({ top: 8 });
          Text('面板内容区').fontSize(20).fontWeight(FontWeight.Bold);
          Text(this.modeLabel).fontSize(28).fontColor('#FF007AFF');

          if (this.currentMode === PanelMode.Mini) {
            this.modeDesc('Mini', '面板仅展示最小高度内容。');
          } else if (this.currentMode === PanelMode.Half) {
            this.modeDesc('Half', '面板展开至屏幕一半高度。');
          } else {
            this.modeDesc('Full', '面板几乎填满整个屏幕。');
          }
        }.width('100%').padding({ left: 16, right: 16, bottom: 24 });
      }
      .mode(this.currentMode)
      .dragBar(true)
      .miniHeight(200)
      .halfHeight('50%')
      .fullHeight('90%')
      .backgroundColor('#FFFFFFFF')
      .borderRadius(20)
      .onChange((m: PanelMode): void => {
        this.currentMode = m;
        const idx = this.modeList.indexOf(m);
        if (idx !== -1) this.modeLabel = this.modeLabels[idx];
      })
      .onVisibleAreaChange([0.0, 0.5, 1.0],
        (v: boolean): void => { this.isPanelShow = v; })
      .shadow({ radius: 16, color: 'rgba(0,0,0,0.15)',
        offsetX: 0, offsetY: -4 });
    }.width('100%').height('100%').alignContent(Alignment.Bottom);
  }

  @Builder
  modeDesc(title: string, desc: string): void {
    Column() {
      Text(title).fontSize(16).fontWeight(FontWeight.Medium);
      Text(desc).fontSize(14).fontColor('#FF666666').lineHeight(22);
    }.width('90%').padding(16).backgroundColor('#FFF0F4FF')
      .borderRadius(12).margin({ top: 8, bottom: 8 });
  }
}

3.2 页面路由注册

resources/base/profile/main_pages.json 中:

{ "src": ["pages/Index", "pages/PanelDemo"] }

四、核心 API 深度解读

4.1 构造函数

Panel(value: boolean)

API 24 接受直接布尔值,不再支持 { show: boolean } 对象字面量。

4.2 关键属性

属性方法 说明 示例值
.mode(PanelMode) 设置面板高度模式 PanelMode.Half
.miniHeight(length) Mini 模式高度(vp) 200
.halfHeight(len | str) Half 模式高度 '50%'400
.fullHeight(len | str) Full 模式高度 '90%'700
.dragBar(boolean) 显示拖拽条 true

4.3 回调事件

回调 说明 参数
.onChange(cb) 模式切换时触发 (PanelMode) => void
.onVisibleAreaChange(ratios, cb) 可见比例变化时触发 number[] + callback

onVisibleAreaChange 替代了旧版 onVisibleChange,需额外传入阈值数组(如 [0.0, 0.5, 1.0])。


五、API 24 迁移对照

从 API 11~12 迁移到 API 24 的核心变更:

旧 API(11~12) 新 API(24) 说明
Panel({ show }) Panel(bool) 构造参数简化
PanelMode.MIN PanelMode.Mini PascalCase 统一
PanelMode.HALF PanelMode.Half 同上
PanelMode.FULL PanelMode.Full 同上
.minHeight(v) .miniHeight(v) 属性重命名
.onVisibleChange(cb) .onVisibleAreaChange(ratios, cb) 新增 ratios 参数
import { PanelMode } 无需 import 全局类型

六、最佳实践

高度单位选择

  • Mini 模式推荐使用固定 vp 值(如 200),使内容紧凑可控
  • Half / Full 模式推荐使用百分比字符串(如 '50%'),自适应屏幕

状态同步

务必实现 onChange 回调,将拖拽产生的模式变化同步到应用状态,避免「按钮显示 Half 而面板实际已到 Full」的不一致。

Stack 容器对齐

Panel 从底部弹出,包裹它的 Stack 容器应设为 alignContent(Alignment.Bottom)

圆角与阴影

为 Panel 添加顶部圆角和阴影可显著提升视觉层次:

.borderRadius(20)
.shadow({ radius: 16, color: 'rgba(0,0,0,0.15)', offsetX: 0, offsetY: -4 })

七、扩展思路

与 List 结合

在 Panel 内部放置 List,实现可拖拽展开的评论列表或通知中心——最常用的生产级场景。

配合动画

结合 animateTo 在模式切换时添加内容自定义动画,进一步提升体验。

自定义拖拽条

设置 dragBar(false),用 PanGesture 自行实现拖拽手势区域,配合 onChange 完成模式切换,适合需要精致设计的场景。


八、总结

Panel 组件是 HarmonyOS NEXT 中极具实用价值的布局组件。它的三种高度模式——Mini、Half、Full——覆盖了从「轻量提示」到「全屏详情」的全场景需求。配合拖拽手势、灵活的样式定制和完备的回调体系,开发者只需少量代码就能构建出体验流畅的底部面板交互。

API 24 的命名变更虽然带来了一些迁移成本,但统一后的 PascalCase 风格使代码更加规整,与 ArkTS 整体语言风格保持一致。希望本文能帮助你在实际项目中高效地使用 Panel 组件。

Logo

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

更多推荐