チュートリアル:FlexBox と Grid を使用したレスポンシブ GUI レイアウト
FlexBoxとGridクラスを使用して、さまざまな画面サイズと向きで動作するレスポンシブ GUI レイアウトを構築します。
レベル: 中級
プラットフォーム: Windows, macOS, Linux, iOS, Android
クラス: FlexBox, FlexItem, Grid, GridItem
はじめに
このチュートリアルは、チュートリアル:高度な GUI レイアウト技術で説明されているRectangleクラスを使用したシンプルなレイアウト技術の理解を前提としています。まだ行っていない場合は、まずそのチュートリアルを読んでください。
このチュートリアルのデモプロジェクトをこちらからダウンロードしてください:PIP | ZIP。プロジェクトを解凍し、最初のヘッダファイルを Projucer で開いてください。
この手順でサポートが必要な場合は、チュートリアル:Projucer Part 1: Projucer を始めようを参照してください。
デモプロジェクト
デモプロジェクトは、可変画面サイズと解像度を扱うときにFlexBoxとGridオブジェクトを使用したさまざまなレスポンシブレイアウト技術を示します。最初にプロジェクトを初期状態で実行すると、以下のようになるはずです:

現在、レイアウトは一般的な非レスポンシブ技術を使用して画面上にコンポーネントをレイアウトしており、向きの変更に対応していません。これらの問題を解消するためにFlexBoxとGridアイテムを使用します。
FlexBox と Grid レイアウトシステム
FlexBoxとGridクラスは、CSS Web 開発で使用されるレスポンシブレイアウトプラクティスに強くインスパイアされています。以前にレスポンシブ Web サイトをデザインしたことがあれば、このセクションで説明するレイアウトシステムに馴染みがあるはずです。
FlexBoxを使用する場合、まずレイアウトの方向を水平または垂直として定義する必要があり、その後のすべての計算はこの基礎に基づいて実行されます。この方向をメイン軸と呼び、その垂直方向をクロス軸と呼びます。この情報に基づいて、以下のプロパティが次のようにレイアウトに影響します:
- 配置(Justification)はメイン軸に沿った項目の位置に影響します。
- 整列(Alignment)はクロス軸に沿った項目の位置に影響します。
- 折り返し(Wrapping)はメイン軸での項目のオーバーフロー時にクロス軸に溢れることで実行されます。
コンテナ内の項目はFlexItemクラスによって定義され、動的リサイズに影響する 3 つの柔軟なプロパティがあります:
- Flex-grow は必要に応じて項目が成長する能力を定義します。
- Flex-shrink は必要に応じて項目が縮小する能力を定義します。
- Flex-basis は動的リサイズ前の項目のデフォルトサイズを定義します。
2 次元レイアウトシステムとして、Gridは行軸と列軸の両方で動作します。FlexBoxと同様に、以下のプロパティが次のようにレイアウトに影響します:
- 配置(Justification)は行軸に沿った項目の位置に影響します。
- 整列(Alignment)は列軸に沿った項目の位置に影響します。
- 折り返し(Wrapping)は行または列での項目のオーバーフロー時に実行できます。
GridItemオブジェクトはGrid内に含まれ、サイズに影響する便利なプロパティがあります:
- マージンは特定の項目の周りにギャップを提供できます。
- Spanは複数のグリッドセルを埋めるように項目を拡張できます。
特定のプロパティがこれらのレイアウトシステムにどのように影響するかがわかったので、デモプロジェクトでこれらの変更を実装し始めることができます。
FlexItem と GridItem オブジェクトの使用
まず、FlexBoxを使用してRightSidePanel::resized()メソッドのボタンレイアウトを置き換えましょう:
void resized() override
{
juce::FlexBox fb; // [1]
fb.flexWrap = juce::FlexBox::Wrap::wrap; // [2]
fb.justifyContent = juce::FlexBox::JustifyContent::center; // [3]
fb.alignContent = juce::FlexBox::AlignContent::center; // [4]
for (auto* b : buttons) // [5]
fb.items.add (juce::FlexItem (*b).withMinWidth (50.0f).withMinHeight (50.0f));
fb.performLayout (getLocalBounds()); // [6]
}
- [1]:FlexBoxオブジェクトを作成します。
- [2]:オーバーフローの場合にオブジェクトを折り返すかどうかを指定できます。
- [3]:コンテンツを境界の中央に配置します。
- [4]:コンテンツの整列を中央に指定します。
- [5]:TextButtonコンポーネントを反復し、FlexBoxオブジェクトの items 配列にFlexItemオブジェクトとして追加します。FlexItemは、この場合ボタンの最小幅と高さを設定するように制約できます。
withMaxWidth()とwithMaxHeight()メソッドを使用して最大幅と高さを設定することもできます。 - [6]:
performLayout()メソッドに境界を指定してFlexItemオブジェクトにレイアウトロジックを実行します。
左側パネルの回転スライダーレイアウトについては、LeftSidePanel::resized()メソッドを調整します:
void resized() override
{
//==============================================================================
juce::FlexBox knobBox;
knobBox.flexWrap = juce::FlexBox::Wrap::wrap;
knobBox.justifyContent = juce::FlexBox::JustifyContent::spaceBetween; // [1]
for (auto* k : knobs)
knobBox.items.add (juce::FlexItem (*k).withMinHeight (50.0f).withMinWidth (50.0f).withFlex (1)); // [2]
//==============================================================================
juce::FlexBox fb; // [3]
fb.flexDirection = juce::FlexBox::Direction::column;
fb.items.add (juce::FlexItem (knobBox).withFlex (2.5)); // [4]
fb.performLayout (getLocalBounds());
}
- [1]:今回は
JustifyContent::spaceBetweenプロパティを指定して項目を間隔を空けて配置したいことを指定します。 - [2]:ノブは同じ方法で items 配列に追加され、追加の flex-grow 値
1があります。flex-grow ファクターは、コンテナ内で項目が占めるべきスペースの量を決定します。 - [3]:前に作成したFlexItemオブジェクトのコンテナとして機能する別のFlexBoxが作成され、flex レイアウトのメイン軸が
Direction::columnプロパティで設定されます。 - [4]:前に定義したFlexBoxが flex-grow ファクター
2.5でコンテナFlexBoxにFlexItemとして追加されます。
FlexBoxオブジェクトをネストすると、コンポーネントの小さなグループをカプセル化することで、複雑なレスポンシブレイアウトを簡単に作成できます。
最後に、MainPanel::resized()メソッドでメインパネルのスライダーを向きの変更に対応させることができます:
void resized() override
{
auto isPortrait = getLocalBounds().getHeight() > getLocalBounds().getWidth(); // [1]
juce::FlexBox fb;
fb.flexDirection = isPortrait ? juce::FlexBox::Direction::column // [2]
: juce::FlexBox::Direction::row;
for (auto* s : sliders)
{
s->setSliderStyle (isPortrait ? juce::Slider::SliderStyle::LinearHorizontal // [3]
: juce::Slider::SliderStyle::LinearVertical);
fb.items.add (juce::FlexItem (*s).withFlex (0, 1, isPortrait ? (float) getHeight() / 5.0f // [4]
: (float) getWidth() / 5.0f));
}
fb.performLayout (getLocalBounds());
}
- [1]:まず、幅と高さをチェックしてデバイスが縦向きか横向きかを判断します。
- [2]:次に、それに応じてメイン軸の方向を決定し、適切なプロパティを設定します。
- [3]:同様に、デバイスの向きに合わせて適切なスライダースタイルを設定します。
- [4]:スライダーを items 配列に追加するとき、フローの方向における各スライダーの比率を決定して flex-basis を提供します。
スライダーはデバイスの向きに適応し、それに応じて方向を調整するようになります。
最後に、パネルの全体的なレイアウトシステムも flex を使用するように変更できます:
void resized() override
{
juce::FlexBox fb;
juce::FlexItem left ((float) getWidth() / 4.0f, (float) getHeight(), leftPanel);
juce::FlexItem right ((float) getWidth() / 4.0f, (float) getHeight(), rightPanel);
juce::FlexItem main ((float) getWidth() / 2.0f, (float) getHeight(), mainPanel);
fb.items.addArray ({ left, main, right });
fb.performLayout (getLocalBounds());
}
flex を使用するように修正したコードを実行すると、以下のようになるはずです:

この修正版のコードのソースコードはデモプロジェクトのFlexBoxGridTutorial_02.hファイルにあります。
代わりにGridクラスを使用してコードの最後の部分を実装してみましょう。ここでは、flex と同様にレイアウト操作を実行するためのGridオブジェクトを作成します:
void resized() override
{
juce::Grid grid;
using Track = juce::Grid::TrackInfo;
using Fr = juce::Grid::Fr;
grid.templateRows = { Track (Fr (1)) };
grid.templateColumns = { Track (Fr (1)), Track (Fr (2)), Track (Fr (1)) };
grid.items = { juce::GridItem (leftPanel), juce::GridItem (mainPanel), juce::GridItem (rightPanel) };
grid.performLayout (getLocalBounds());
}
ただし、個々のFlexItemオブジェクトに flex-grow、flex-shrink、flex-basis の値を指定する代わりに、この場合は TrackInfo オブジェクトを使用してGridオブジェクトに行と列の数を設定します。制約はそれぞれ_frと_pxサフィックスを使用して分数またはピクセルで指定できます。この例では、1 行 3 列のグリッドを定義し、中央の列が他の列の 2 倍のスペースを取ります。
JUCE のピクセルは物理ピクセルと同等ではありません。内部計算により、画面の DPI 解像度に応じてピクセル密度が変換されます。
この修正版のコードのソースコードはデモプロジェクトのFlexBoxGridTutorial_03.hファイルにあります。
FlexBox と Grid クラスの長所と短所
レスポンシブレイアウトを作成するためにこれらのクラスのいずれかを使用できる多くのケースがあります。ただし、一方がより適切で、特定のレイアウト制約を解決するために必要な場合もあります。
FlexBoxクラスの利点のいくつか:
- メイン軸が望ましいコンポーネントのレイアウトに適しています。
- コンテンツの折り返し、方向、整列が簡単に指定できます。
- クロス軸上の整列されていないコンテンツに対応できます。
Gridクラスの利点のいくつか:
- 行と列が整列された 2D グリッドタイプのレイアウトに適しています。
- コンポーネントの比率を分数またはピクセルで指定できます。
- 複数の行または列にまたがるコンテンツに対応できます。
まとめ
このチュートリアルでは、FlexBoxとGridクラスを使用してレスポンシブレイアウトを設計する方法を学びました。特に以下のことを行いました: