在HarmonyOS中,如何实现“可折叠中间面板”,并且再次展开时恢复上一次高度?

我想让中间面板支持“折叠/展开”。折叠时高度收成 0;展开时恢复为上一次的实际高度(不是固定值)。要怎么写?

阅读 670
avatarAI BotBETA

在 HarmonyOS 中,可通过 Panel 组件结合状态管理实现动态高度的可折叠面板。以下为完整代码示例:

// 使用 Panel 和状态管理实现
public class ExpandablePanelAbilitySlice extends AbilitySlice {
    private Panel panel;
    private float lastHeight = 200; // 默认初始高度
    private boolean isFirstExpand = true;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        DirectionalLayout rootLayout = new DirectionalLayout(this);
        
        // 创建可折叠面板
        panel = new Panel(this);
        panel.setPanelMode(PanelMode.FOLDABLE);
        panel.setMinHeight(0); // 折叠时高度为0
        
        // 面板内容容器(高度随内容自适应)
        DirectionalLayout panelContent = new DirectionalLayout(this);
        panelContent.setBackground(new ShapeElement(Color.GRAY));
        // 在此添加你的实际内容组件...
        
        // 关键:监听面板高度变化
        panel.addPanelChangedListener(new Panel.PanelChangedListener() {
            @Override
            public void onHeightChanged(int height) {
                if (panel.isFolding()) return;
                lastHeight = height; // 实时记录非折叠状态的高度
            }

            @Override
            public void onExpand() {
                // 首次展开使用默认值,后续展开恢复上次高度
                if (!isFirstExpand) {
                    panel.setHeight(lastHeight);
                }
                isFirstExpand = false;
            }

            @Override
            public void onFold() {
                // 折叠时不需特殊处理
            }
        });

        panel.addComponent(panelContent);
        rootLayout.addComponent(panel);
        super.setUIContent(rootLayout);
    }
}

关键实现点:

  1. 状态记录:通过 lastHeight 变量记录面板展开时的实际高度
  2. 折叠控制

    • setMinHeight(0) 使折叠时高度为0
    • PanelMode.FOLDABLE 启用折叠模式
  3. 高度恢复机制

    • onHeightChanged 回调中实时更新有效高度
    • onExpand 回调中应用上次记录的高度
  4. 首次展开处理isFirstExpand 标志避免初始状态干扰

补充说明:

  1. 若需要动画效果,添加以下配置:

    panel.setSmoothScroll(true);
    panel.setAnimationDuration(300); // 300ms动画
  2. 内容高度自适应:确保 panelContent 中的子组件使用 LayoutConfig.MATCH_CONTENT
  3. 折叠控制按钮:可通过外部按钮触发 panel.fold()/panel.expand()

此实现满足需求的核心:

  • ✅ 折叠时高度严格为0
  • ✅ 展开时恢复上次精确高度
  • ✅ 支持手势滑动折叠/展开
  • ✅ 适应内容高度变化
1 个回答

记录中间面板的“最近非零高度”;折叠时将其高度设为 0 并触发 ColumnSplit 重建以重新取初始值;展开时再用记录的高度重建。通过切换一个 @State version 分支来“卸载/重建”组件,从而让分割线重新计算。

// pages/SplitCollapsibleMiddle.ets
@Entry
@Component
struct SplitCollapsibleMiddle {
  @State version: number = 0
  @State topH: number = 120
  @State midH: number = 240      // 当前中间高度(0 表示折叠)
  @State lastMidH: number = 240  // 记录“最近非零高度”
  @State botH: number = 120

  private rebuild() { this.version ^= 1 }

  private toggle() {
    if (this.midH === 0) {
      this.midH = Math.max(100, this.lastMidH) // 展开并恢复
    } else {
      this.lastMidH = this.midH                // 记住非零高度
      this.midH = 0                            // 折叠
    }
    this.rebuild()                              // 触发 ColumnSplit 重新初始化
  }

  build() {
    Column({ space: 8 }) {
      Button(this.midH === 0 ? '展开中间面板' : '折叠中间面板').onClick(() => this.toggle())

      if (this.version === 0) {
        this.body()
      } else {
        this.body()
      }
    }
    .padding(12)
    .width('100%').height('100%')
  }

  @Builder
  private body() {
    ColumnSplit() {
      Column().height(this.topH).minHeight(60).backgroundColor('#FFFDE7')
        .onAreaChange((_, now) => { this.topH = now.height })

      // 中间面板:可能为 0(折叠)
      Column().height(this.midH).minHeight(0).backgroundColor('#E3F2FD')
        .onAreaChange((_, now) => {
          // 只有非零高度才更新“最近高度”
          if (now.height > 0) this.lastMidH = now.height
        })

      Column().height(this.botH).minHeight(60).backgroundColor('#E8F5E9')
        .onAreaChange((_, now) => { this.botH = now.height })
    }
    .resizeable(true)
    .divider({ startMargin: 6, endMargin: 6 })
    .width('92%').height('75%')
    .border({ width: 1, color: '#22000000' }).borderRadius(8)
  }
}
推荐问题