概要

JPanelに配置した子コンポーネントを水平方向で折り返し、またそれらの水平間隔を動的に均等になるよう拡大します。

サンプルコード

class ScrollableWrapLayout extends FlowLayout {
  private final int fixedHorizontalGap;

  protected ScrollableWrapLayout(int align, int hgap, int vgap) {
    super(align, hgap, vgap);
    fixedHorizontalGap = hgap;
  }

  private int getPreferredHorizontalGap(Container target) {
    Insets insets = target.getInsets();
    int columns = 0;
    int width = target.getWidth();
    if (target.getParent() instanceof JViewport) {
      width = target.getParent().getBounds().width;
    }
    width -= insets.left + insets.right + fixedHorizontalGap * 2;
    for (int i = 0; i < target.getComponentCount(); i++) {
      Component m = target.getComponent(i);
      if (m.isVisible()) {
        Dimension d = m.getPreferredSize();
        if (width - d.width - fixedHorizontalGap < 0) {
          columns = i;
          break;
        }
        width -= d.width + fixedHorizontalGap;
      }
    }
    return fixedHorizontalGap + width / columns;
  }

  @Override public void layoutContainer(Container target) {
    setHgap(getPreferredHorizontalGap(target));
    super.layoutContainer(target);
  }

  @Override public Dimension preferredLayoutSize(Container target) {
    Dimension dim = super.preferredLayoutSize(target);
    synchronized (target.getTreeLock()) {
      if (target.getParent() instanceof JViewport) {
        dim.width = target.getParent().getBounds().width;
        for (int i = 0; i < target.getComponentCount(); i++) {
          Component m = target.getComponent(i);
          if (m.isVisible()) {
            Dimension d = m.getPreferredSize();
            dim.height = Math.max(dim.height, d.height + m.getY());
          }
        }
      }
      return dim;
    }
  }
}
View in GitHub: Java, Kotlin

解説

  • 上: JPanel + FlowLayout
    • デフォルトのJPanelFlowLayoutを設定してJScrollPaneに配置
    • 水平方向にJPanelが拡張されて水平ScrollBarが表示される
  • 下: ScrollableWrapPanel + ScrollableWrapLayout
    • JPanelScrollableを実装するScrollableWrapPanelを作成し、これにFlowLayout#preferredLayoutSize(...)などをオーバーライドするScrollableWrapLayoutを設定してJScrollPaneに配置
    • ScrollableWrapPanelの子コンポーネント間の水平間隔を親のリサイズに応じて動的・均等に拡大するようFlowLayout#layoutContainer(...)メソッドをオーバーライド
      • 最小値は初期水平間隔
      • 子コンポーネントはすべて同じ推奨サイズと想定

参考リンク

コメント