juce - 界面布局

在 JUCE 5.4.3 中实现界面控件自适应窗口大小,主要依靠布局管理器和响应式设计。以下是详细解决方案:

一、核心方法:使用布局管理器(推荐)

JUCE 提供了两种现代布局系统,优先推荐使用:

1. FlexBox 布局

  • 类似 Qt 的 QBoxLayout:可实现水平和垂直方向的弹性布局

class MainComponent : public Component {
public:
    MainComponent() {
        addAndMakeVisible(button1);
        addAndMakeVisible(slider1);
        addAndMakeVisible(label1);
    }

    void resized() override {
        FlexBox fb;
        fb.flexDirection = FlexBox::Direction::column; // 垂直排列
        fb.justifyContent = FlexBox::JustifyContent::spaceAround; // 均匀分布
        fb.alignItems = FlexBox::AlignItems::stretch; // 拉伸填满

        // 添加带弹性系数的组件
        fb.items.addArray({
            FlexItem(button1).withFlex(1).withMargin(10), // 弹性系数1
            FlexItem(slider1).withFlex(2).withMargin(10),  // 弹性系数2 (占双倍空间)
            FlexItem(label1).withFlex(1).withMargin(10)
        });

        fb.performLayout(getLocalBounds());
    }

private:
    TextButton button1 {"Button"};
    Slider slider1;
    Label label1 {"Label", "Hello JUCE!"};
};

2. Grid 布局

  • 类似 Qt 的 QGridLayout:网格布局系统

void resized() override {
    Grid grid;
    
    // 定义3列:1份弹性空间 | 固定100px | 2份弹性空间
    grid.templateColumns = { Grid::Fr(1), Grid::Px(100), Grid::Fr(2) };
    
    // 定义2行:自动高度 | 弹性高度
    grid.templateRows = { Grid::auto, Grid::Fr(1) };
    
    // 添加组件到网格位置
    grid.items.addArray({
        GridItem(button1).withArea(1, 1), // 第1行第1列
        GridItem(slider1).withArea(1, 2).withSpan(2, 1), // 跨2行
        GridItem(label1).withArea(2, 3)   // 第2行第3列
    });
    
    grid.performLayout(getLocalBounds().reduced(10)); // 带边距
}

 

二、传统方法:手动计算布局

适用于简单场景:

void resized() override {
    auto area = getLocalBounds().reduced(10); // 边距10px
    
    // 顶部按钮 (高度固定40px)
    button1.setBounds(area.removeFromTop(40));
    
    // 中间滑块 (占剩余高度的30%)
    slider1.setBounds(area.removeFromTop(area.getHeight() * 0.3f));
    
    // 底部标签 (剩余空间)
    label1.setBounds(area);
}

关键方法:

  • getLocalBounds(): 获取可绘制区域

  • removeFromTop()/removeFromLeft(): 切割区域

  • withTrimmed(): 内边距调整

  • withSizeKeepingCentre() : 居中元素

关键技巧:

  1. 比例分配

    // 水平三等分
    auto thirdWidth = getWidth() / 3;
    comp1.setBounds(0, 0, thirdWidth, getHeight());
    comp2.setBounds(thirdWidth, 0, thirdWidth, getHeight());
  2. 响应缩放

    void resized() override {
        const int margin = jmin(20, getWidth()/20); // 动态边距
        // ...
    }
  3. 嵌套布局

    class ChildComponent : public Component {
    public:
        void resized() override {
            // 子组件自己的布局
        }
    };
    
    // 父组件布局
    void parentResized() {
        childComp.setBounds(getWidth()/4, 10, getWidth()/2, getHeight()-20);
    }

最佳实践:

  1. 优先选择 FlexBox/Grid - 更易维护且支持复杂响应式布局

  2. 使用相对单位 - 避免固定尺寸,多用比例和弹性系数

  3. 处理边界情况

    void resized() override {
        if (getWidth() < 300) { // 小屏幕布局
            // 垂直排列
        } else { // 大屏幕布局
            // 水平排列
        }
    }

组件约束(可选):

slider1.setSize(200, 40); // 基础尺寸
slider1.setMaximumSize(400, 60); // 最大尺寸
slider1.setMinimumSize(100, 30); // 最小尺寸

 

这些方法组合使用可创建出:

  • 按比例缩放的控件

  • 动态排列的布局(横/竖屏自适应)

  • 保持合理间距的响应式界面

  • 适应不同分辨率的UI系统

建议从简单布局开始尝试,逐步组合复杂界面。JUCE的布局系统非常强大,熟练后可以轻松实现专业级的自适应界面。

 

JUCE 矩形切割函数详解:

removeFromTop()、removeFromLeft()、removeFromBottom()、removeFromRight()

 这些函数都是juce::Rectangle类的成员函数,用于从当前矩形中切割出一部分,并修改原矩形。

每个函数的作用:

  1. removeFromTop(int height): 从当前矩形的顶部切割出一个高度为height的矩形,原矩形的高度减少height(原矩形的y坐标增加height,高度减少height)。
  2. removeFromLeft(int width): 从当前矩形的左侧切割出一个宽度为width的矩形,原矩形的宽度减少width(原矩形的x坐标增加width,宽度减少width)。
  3. removeFromBottom(int height): 从当前矩形的底部切割出一个高度为height的矩形,原矩形的高度减少height(原矩形的高度减少height,y坐标不变)。
  4. removeFromRight(int width): 从当前矩形的右侧切割出一个宽度为width的矩形,原矩形的宽度减少width(原矩形的宽度减少width,x坐标不变)。

 

注意:这些函数都会返回被切割出来的新矩形,同时修改调用函数的矩形对象本身(即原矩形会减去被切割掉的部分)。

在JUCE的布局系统中,这四个矩形操作函数是构建复杂界面的基础工具。它们允许你通过"切割"矩形区域来精确分配空间。下面我将详细解释每个函数的工作原理和使用方法。

核心概念:矩形切割原理

JUCE中的juce::Rectangle对象代表一个矩形区域。这些切割函数的核心特点是:

  • 修改原始矩形:调用函数后会修改原始矩形

  • 返回切割部分:函数返回被切割下来的矩形

  • 保持剩余区域:原始矩形变为剩余部分

视觉化表示

初始矩形:
+-------------------+
|                   |
|                   |
|                   |
+-------------------+

1. removeFromTop() - 从顶部切割

功能

从矩形顶部切下一块指定高度的区域

语法

Rectangle<int> topPart = originalRect.removeFromTop(int height);

详细行为

  • 从矩形顶部切下高度为height的部分

  • 返回被切下的顶部矩形

  • 原始矩形的高度减少height,Y坐标增加height

代码示例

void resized() override {
    auto area = getLocalBounds(); // 假设为 400x300
    
    // 切下顶部100像素
    auto header = area.removeFromTop(100);
    headerComponent.setBounds(header); // 设置header组件
    
    // 剩余区域: 400x200 (Y从100开始)
    mainContent.setBounds(area);
}

视觉变化

初始:
+-------------------+
|      header       | ← 切下的部分 (100px)
+-------------------+
|                   |
|   remaining area  | ← 修改后的原始矩形
|                   |
+-------------------+

2. removeFromBottom() - 从底部切割

功能

从矩形底部切下一块指定高度的区域

语法

Rectangle<int> bottomPart = originalRect.removeFromBottom(int height);

详细行为

  • 从矩形底部切下高度为height的部分

  • 返回被切下的底部矩形

  • 原始矩形的高度减少height,Y坐标不变

代码示例

void resized() override {
    auto area = getLocalBounds(); // 400x300
    
    // 切下底部50像素
    auto footer = area.removeFromBottom(50);
    statusBar.setBounds(footer); // 设置footer组件
    
    // 剩余区域: 400x250 (高度减少)
    contentArea.setBounds(area);
}

视觉变化

初始:
+-------------------+
|                   |
|   remaining area  | ← 修改后的原始矩形
|                   |
+-------------------+
|      footer       | ← 切下的部分 (50px)
+-------------------+

3. removeFromLeft() - 从左侧切割

功能

从矩形左侧切下一块指定宽度的区域

语法

Rectangle<int> leftPart = originalRect.removeFromLeft(int width);

详细行为

  • 从矩形左侧切下宽度为width的部分

  • 返回被切下的左侧矩形

  • 原始矩形的宽度减少width,X坐标增加width

代码示例

void resized() override {
    auto area = getLocalBounds(); // 400x300
    
    // 切下左侧150像素
    auto sidebar = area.removeFromLeft(150);
    navigationPanel.setBounds(sidebar); // 设置侧边栏
    
    // 剩余区域: 250x300 (X从150开始)
    mainContent.setBounds(area);
}

视觉变化

初始:
+-------------------+
|     |             |
| 切  |   remaining |
| 下  |    area     |
| 的  |             |
| 左  |             |
| 侧  |             |
+-------------------+

4. removeFromRight() - 从右侧切割

功能

从矩形右侧切下一块指定宽度的区域

语法

Rectangle<int> rightPart = originalRect.removeFromRight(int width);

详细行为

  • 从矩形右侧切下宽度为width的部分

  • 返回被切下的右侧矩形

  • 原始矩形的宽度减少width,X坐标不变

代码示例

void resized() override {
    auto area = getLocalBounds(); // 400x300
    
    // 切下右侧80像素
    auto toolbar = area.removeFromRight(80);
    toolsPanel.setBounds(toolbar); // 设置工具栏
    
    // 剩余区域: 320x300 (宽度减少)
    editorArea.setBounds(area);
}

视觉变化

初始:
+-------------------+
|            |      |
|  remaining |  切  |
|    area    |  下  |
|            |  的  |
|            |  右  |
|            |  侧  |
+-------------------+

组合使用:完整布局示例

void resized() override {
    auto area = getLocalBounds(); // 获取整个可用区域
    
    // 顶部标题栏 (固定高度60px)
    auto titleBar = area.removeFromTop(60);
    titleComponent.setBounds(titleBar);
    
    // 底部状态栏 (固定高度40px)
    auto statusBar = area.removeFromBottom(40);
    statusComponent.setBounds(statusBar);
    
    // 左侧导航栏 (固定宽度200px)
    auto sidebar = area.removeFromLeft(200);
    navComponent.setBounds(sidebar);
    
    // 右侧工具栏 (固定宽度80px)
    auto tools = area.removeFromRight(80);
    toolsComponent.setBounds(tools);
    
    // 中间主内容区 (剩余所有空间)
    contentComponent.setBounds(area);
}

动态尺寸布局技巧

比例切割

void resized() override {
    auto area = getLocalBounds();
    
    // 顶部占20%高度
    int headerHeight = getHeight() * 0.2;
    header.setBounds(area.removeFromTop(headerHeight));
    
    // 左侧占30%宽度
    int sidebarWidth = getWidth() * 0.3;
    sidebar.setBounds(area.removeFromLeft(sidebarWidth));
    
    // 剩余区域给主内容
    mainContent.setBounds(area);
}

带间距的切割

void resized() override {
    auto area = getLocalBounds();
    
    // 顶部区域带10px下边距
    auto top = area.removeFromTop(100);
    topComponent.setBounds(top.withTrimmedBottom(10)); // 实际高度90px
    
    // 剩余区域带10px上边距
    area = area.withTrimmedTop(10);
    
    // 左侧区域带5px右边距
    auto left = area.removeFromLeft(150);
    leftComponent.setBounds(left.withTrimmedRight(5));
    
    // 主内容区
    mainContent.setBounds(area);
}

高级链式操作

void resized() override {
    auto area = getLocalBounds();
    
    // 链式切割
    titleBar.setBounds(area.removeFromTop(50)
        .reduced(5); // 添加5px内边距
    
    statusBar.setBounds(area.removeFromBottom(30)
        .withTrimmedLeft(10) // 左侧留白
        .withTrimmedRight(10); // 右侧留白
    
    sidebar.setBounds(area.removeFromLeft(150));
    
    // 在剩余区域中创建两个等高的面板
    auto topPanel = area.removeFromTop(area.getHeight() / 2);
    panel1.setBounds(topPanel.reduced(2));
    
    panel2.setBounds(area.reduced(2));
}

 

JUCE中withSizeKeepingCentre函数详解:

withSizeKeepingCentre是JUCE矩形操作中一个非常有用的函数,它可以在保持矩形中心点不变的情况下改变矩形尺寸。这个函数在创建居中元素时特别有用,比如居中按钮、图标或其他UI组件。

函数定义和作用

函数签名

Rectangle<T> withSizeKeepingCentre(T newWidth, T newHeight) const noexcept;

核心功能

  • 保持中心不变:新矩形的中心点与原矩形相同

  • 改变尺寸:将矩形尺寸调整为指定的新宽度和新高度

  • 不修改原矩形:返回一个新矩形,原始矩形保持不变

使用示例分析

让我们分解你提供的代码:

pTextButton[BTN_MUTE]->setBounds(r.removeFromTop(36).withSizeKeepingCentre(50, 20));

逐步解释

  1. r.removeFromTop(36)

    • 从矩形r中切下顶部36像素的区域

    • 返回一个高度为36像素,宽度与r相同的矩形

  2. .withSizeKeepingCentre(50, 20)

    • 基于上一步得到的矩形创建一个新矩形

    • 新矩形宽度为50像素,高度为20像素

    • 新矩形的中心位置与原矩形中心相同

  3. setBounds()

    • 将静音按钮放置在这个新创建的矩形区域内

视觉化表示

原始矩形r:
+-------------------+
|                   | ← 切下的36px区域
|                   |
+-------------------+
|                   |
|                   |
|                   |
+-------------------+

切下的顶部区域:
+-------------------+
|                   | ← 高度36px
|                   |
+-------------------+

应用withSizeKeepingCentre(50, 20):
+-------------------+
|      +----+       | ← 新矩形50x20px
|      |    |       |     居中显示
|      +----+       |
+-------------------+

 

 

posted @ 2025-08-04 17:53  [BORUTO]  阅读(19)  评论(0)    收藏  举报