概要

高さはデフォルトのまま幅だけを指定して、JButtonJComboBoxJTextFieldなどのサイズを変更します。

サンプルコード

private static JComponent createRightAlignButtonBox2(
  List<JButton> list, final int buttonWidth, int gap) {
  JComponent box = new JPanel() {
    @Override public void updateUI() {
      for (JButton b : list) {
        b.setPreferredSize(null);
      }
      super.updateUI();
      EventQueue.invokeLater(new Runnable() {
        @Override public void run() {
          int maxHeight = list.stream()
            .map(b -> b.getPreferredSize().height)
            .reduce(0, Integer::max);
          Dimension d = new Dimension(buttonWidth, maxHeight);
          for (JButton b : list) {
            b.setPreferredSize(d);
          }
          revalidate();
        }
      });
    }
  };
  box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS));
  box.add(Box.createHorizontalGlue());
  for (JButton b : list) {
    box.add(b);
    box.add(Box.createHorizontalStrut(gap));
  }
  box.setBorder(BorderFactory.createEmptyBorder(gap, 0, gap, 0));
  return box;
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、JButtonの高さはデフォルト、幅をその文字列によらずに一定、配置は右寄せで水平にしたい場合のレイアウト方法をテストしています。

  • Default
    • BoxLayoutBox.createHorizontalGlue()を使用して右寄せ
    • JButtonの幅はその文字列の長さに依存
  • getPreferredSize
    • Boxを使用して右寄せ
    • JButtonの幅はJButton#setPreferredSize(...)で固定幅を設定
    • JButtonの高さはJButton#getPreferredSize()で取得したサイズの高さを使用
    • LookAndFeelを変更するとJButtonの高さが変化するので、その場合はJButton#updateUI()をオーバーライドしてJButton#setPreferredSize(...)を使用する必要がある
  • SpringLayout+Box
    • SpringLayoutの使用
    • SpringLayoutで幅指定、BoxLayoutで右寄せ
    • SpringLayout.ConstraintsJButtonの固定幅を指定
    • 親パネルの幅もSpringLayout.Constraintsで固定し、Boxで入れ子にして右寄せ
    • LookAndFeelを変更するとJButtonの高さが変化するので、その場合はJComponent#getPreferredSize(...)をオーバーライドして親パネルの高さを更新する必要がある
  • SpringLayout
    • SpringLayoutの使用
    • SpringLayoutを使用して右寄せ
    • SpringLayout.ConstraintsJButtonの幅を固定
    • LookAndFeelを変更するとJButtonの高さが変化するので、その場合はJComponent#getPreferredSize(...)をオーバーライドして親パネルの高さを更新する必要がある
  • GridLayout+Box
    • GridLayoutで幅指定、BoxLayoutで右寄せ
    • GridLayoutですべてのJButtonのサイズを同じにする
    • Boxで入れ子にして右寄せ
    • 幅が最大のJButtonのテキストが変更されると、親のBoxの幅が変化してしまう

以下、setPreferredSize()を使用する際の補足、注意点です。

JButtonUIがフォントサイズや文字列の長さから決めたデフォルトサイズをgetPreferredSize()で取得しています。高さはそのまま利用し、幅だけ一定の値を設定して、新たなデフォルトサイズを作成し、setPreferredSize()しています。これで次からgetPreferredSize()で返ってくる値は、どちらのボタンでも全く同じになります。

このsetPreferredSize(...)で設定したサイズは、LookAndFeelを変更しても残ってしまう(DimensionUIResourceを使用しても効果がない)ため、LookAndFeelがデフォルトで使用する高さを取得したい場合は、updateUI()をオーバーライドして、一旦JButton#setPreferredSize(null);を設定して幅を変更した推奨サイズをクリアしてから、super.updateUI();で推奨サイズを更新し、その後で固定幅、LookAndFeelデフォルトの高さをsetPreferredSize(...)で設定し直す必要があります。

getPreferredSize()で得られる値を使用するかどうかは、レイアウトマネージャーによって異なりますが、水平方向にコンポーネントを並べるBoxLayoutの場合は以下のようになるため、パネルをリサイズしても、ボタンのサイズはどちらも同じで変化しません。

  • 幅: 推奨サイズ(getPreferredSizeメソッド)から取得した値
  • 高さ: 各コンポーネントの推奨サイズ(getPreferredSizeメソッド)で得られた中からもっとも大きな値
    • 上記のサンプルでbutton1.setPreferredSize(new Dimension(100, 0));としても結果は同じ

例えば、各ボタンを格納した親フレームをpack()する前にJButton#getPreferredSize()ではなく、JButton#getSize()でサイズを取得すると、[width=0,height=0]が帰ってきます。コンポーネントが表示されている場合、getSize()で得られるサイズは、その実際に表示されているサイズ(レイアウトマネージャーが決定する)になります。

以下はJLabelgetPreferredSize()した場合の例です。初期状態(preferredSizenull)の場合は、JLabelUIがサイズを計算しています。

JLabel l = new JLabel();
l.setText("a"); //preferredSizeがnullの場合、UIがサイズを計算
// l.getPreferredSize() -> Dimension[width=6, height=13]

l.setText("aaaa"); //JLabelの場合、Fontサイズと文字列の長さなどで決まる
// l.getPreferredSize() -> Dimension[width=24, height=13]

l.setText("<html>aa<br>aa");
// l.getPreferredSize() -> Dimension[width=12, height=26]

l.setPreferredSize(new Dimension(10, 10)); //preferredSizeを設定した場合
// l.getPreferredSize() -> Dimension[width=10, height=10]

l.setPreferredSize(null); //preferredSizeをnullに戻した場合
// l.getPreferredSize() -> Dimension[width=12, height=26]

参考リンク

コメント