• category: swing folder: FileChooserInitialFocus title: JFileChooserを開いたときの初期フォーカスを設定する tags: [JFileChooser, NimbusLookAndFeel, Focus] author: aterai pubdate: 2016-10-17T00:34:37+09:00 description: NimbusLookAndFeelを使用しているJFileChooserを開いた場合、ファイル名表示用のJTextFieldに初期フォーカスを設定します。 image: https://drive.google.com/uc?id=1uW5FnfU0V3Yi9iBBMFV7uoN8M9IA2sskmg

概要

NimbusLookAndFeelを使用しているJFileChooserを開いた場合、ファイル名表示用のJTextFieldに初期フォーカスを設定します。

サンプルコード

fileChooser.setSelectedFile(new File(field.getText().trim()));
if (r2.isSelected()) {
  EventQueue.invokeLater(() -> {
    findFileNameTextField(fileChooser).ifPresent(c -> {
      ((JTextField) c).selectAll();
      c.requestFocusInWindow();
    });
  });
}
// ...
private static Optional<Component> findFileNameTextField(JFileChooser fileChooser) {
  return Arrays.stream(fileChooser.getComponents())
  .flatMap(new Function<Component, Stream<Component>>() {
    @Override public Stream<Component> apply(Component c) {
      if (c instanceof Container) {
        Component[] sub = ((Container) c).getComponents();
        return sub.length == 0 ? Stream.of(c)
               : Arrays.stream(sub).flatMap(cc -> apply(cc));
      } else {
        return Stream.of(c);
      }
    }
  })
  .filter(c -> c instanceof JTextField)
  .findFirst();
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、NimbusLookAndFeel(SynthLookAndFeel)を使用している場合、JFileChooserを開いたときの初期フォーカスは、フォルダ選択用のJComboBoxになっているので、これをファイル名表示用のJTextFieldに変更するテストを行っています。

  • JFileChooserを開いた後でフォーカスを移動するため、EventQueue.invokeLater(...)を使用
  • JFileChooserから直接ファイル名表示用のJTextFieldを取得できないので、Container#getComponents()で子コンポーネントを検索する必要がある
  • MetalLookAndFeelWindowsLookAndFeelの場合、初期フォーカスはファイル名表示用のJTextField
    • SynthLookAndFeelのバグ?
  • または、PropertyChangeListenerAncestorListenerなどを使用、SynthFileChooserUIImpl#doAncestorChanged()をオーバーライドする方法がある
//TEST: PropertyChangeListener
fileChooser.addPropertyChangeListener(e -> {
  String s = e.getPropertyName();
  if (s.equals("ancestor")) {
    if (e.getOldValue() == null && e.getNewValue() != null) {
      // Ancestor was added, set initial focus
      findFileNameTextField(fileChooser).ifPresent(c -> {
        ((JTextField) c).selectAll();
        c.requestFocusInWindow();
      });
    }
  }
});
//TEST: AncestorListener
fileChooser.addAncestorListener(new AncestorListener() {
  @Override public void ancestorAdded(AncestorEvent e) {
    findFileNameTextField(fileChooser).ifPresent(c -> {
      ((JTextField) c).selectAll();
      c.requestFocusInWindow();
    });
  }
  @Override public void ancestorMoved(AncestorEvent e) {}
  @Override public void ancestorRemoved(AncestorEvent e) {}
});
//TEST: doAncestorChanged
fileChooser = new JFileChooser() {
  @Override public void updateUI() {
    super.updateUI();
    EventQueue.invokeLater(() -> {
      setUI(new sun.swing.plaf.synth.SynthFileChooserUIImpl(fileChooser) {
        @Override protected void doAncestorChanged(PropertyChangeEvent e) {
          findFileNameTextField(fileChooser).ifPresent(c -> {
            ((JTextField) c).selectAll();
            c.requestFocusInWindow();
          });
        }
      });
    });
  }
};

参考リンク

コメント