• category: swing folder: FileChooserBottomAccessory title: MetalLookAndFeelでJFileChooserの下部にコンポーネントを追加する tags: [JFileChooser, MetalLookAndFeel] author: aterai pubdate: 2017-08-14T15:32:42+09:00 description: MetalLookAndFeelを適用しているJFileChooserのファイルフィルタとボタンパネルの間にJComboBoxのような横長のコンポーネントを追加します。 image: https://drive.google.com/uc?export=view&id=1Nyb4wo_ryaaCsJEgpGBxbIlYazx3FQVFfw

概要

MetalLookAndFeelを適用しているJFileChooserのファイルフィルタとボタンパネルの間にJComboBoxのような横長のコンポーネントを追加します。

サンプルコード

class EncodingFileChooserUI extends MetalFileChooserUI {
  public final JComboBox<String> combo = new JComboBox<>(
      new String[] {"UTF-8", "UTF-16", "Shift_JIS", "EUC-JP"});
  protected EncodingFileChooserUI(JFileChooser filechooser) {
    super(filechooser);
  }
  @Override public void installComponents(JFileChooser fc) {
    super.installComponents(fc);
    JPanel bottomPanel = getBottomPanel();

    JLabel label = new JLabel("Encoding:") {
      @Override public Dimension getPreferredSize() {
        return SwingUtils.stream(bottomPanel)
          .filter(JLabel.class::isInstance).map(JLabel.class::cast)
          .findFirst()
          .map(JLabel::getPreferredSize)
          .orElse(super.getPreferredSize());
      }
    };
    label.setDisplayedMnemonic('E');
    label.setLabelFor(combo);

    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
    panel.add(label);
    panel.add(combo);

    // 0: fileNamePanel
    // 1: RigidArea
    // 2: filesOfTypePanel
    bottomPanel.add(Box.createRigidArea(new Dimension(1, 5)), 3);
    bottomPanel.add(panel, 4);

    SwingUtils.stream(bottomPanel)
      .filter(JLabel.class::isInstance).map(JLabel.class::cast)
      .forEach(l -> {
        l.setHorizontalAlignment(SwingConstants.RIGHT);
        l.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
      });
  }
}
View in GitHub: Java, Kotlin

解説

  • JFileChooser#setAccessory(JComponent)を使用するとJFileChooserの右側にコンポーネントが追加されるので、横長のコンポーネントを追加するとJFileChooser全体の幅が広がってしまう
  • BasicFileChooserUI#getBottomPanel()メソッドでファイル名入力欄、ファイルフィルタ、ボタンなどを格納している下部パネルを取得し、「ファイルのタイプ(T)」パネルとボタンパネルの間に、JComboBoxのような横長のコンポーネントを設定した別パネルを追加
    • MetalFileChooserUIの場合、2番目のfilesOfTypePanelの直後になるようbottomPanel.add(Box.createRigidArea(vstrut5), 3); bottomPanel.add(encodingPanel, 4)で追加
    • BasicFileChooserUI#getBottomPanel()で取得できるJPanel内のレイアウトは、LookAndFeelごとに大きく異なるため同じ方法は使用できない
      • 例えばWindowsLookAndFeelではBoxLayout.LINE_AXISMetalLookAndFeelではBoxLayout.Y_AXIS
    • MetalFileChooserUIでは、JLabelを継承するAlignedLabelで最も長い文字列幅から推奨サイズを揃えているが、privateのため、このサンプルでは使用できない
  • JFileChooser#updateUI()メソッドをオーバーライドして、super.updateUI(); setUI(new EncodingFileChooserUI(this));などを実行した場合、AcceptAllファイルフィルタが重複追加されてしまう
    • setUI(...)の後でJFileChooser#resetChoosableFileFilters()を実行するか、super.updateUI()を実行しないことで回避可能

参考リンク

コメント