• category: swing folder: InputVerifierFocusOrder title: InputVerifierを設定したコンポーネントのフォーカスナビゲーションをテストする tags: [JTextField, InputVerifier, FocusTraversalPolicy, FocusListener] author: aterai pubdate: 2016-08-15T02:09:37+09:00 description: InputVerifierを設定したコンポーネントの入力の検証とフォーカス移動イベントの順番をテストします。 image: http://drive.google.com/uc?export=view&id=19ojUQl6rF4StwHNdQO_xPRlqnkOognwULw

概要

InputVerifierを設定したコンポーネントの入力の検証とフォーカス移動イベントの順番をテストします。

サンプルコード

JTextField textField = new JTextField(24);
textField.setInputVerifier(new InputVerifier() {
  @Override public boolean verify(JComponent c) {
    if (c instanceof JTextComponent) {
      JTextComponent tc = (JTextComponent) c;
      String str = tc.getText().trim();
      return !str.isEmpty() && MAX_LEN - str.length() >= 0;
    }
    return false;
  }
  @Override public boolean shouldYieldFocus(JComponent input) {
    System.out.println("shouldYieldFocus");
    if (isAllValid()) {
      button.setEnabled(true);
      //EventQueue.invokeLater(() -> button.requestFocusInWindow());
    } else {
      button.setEnabled(false);
    }
    return super.shouldYieldFocus(input);
  }
});
setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
  @Override public Component getComponentAfter(
      Container focusCycleRoot, Component aComponent) {
    System.out.println("getComponentAfter");
    button.setEnabled(isAllValid());
    return super.getComponentAfter(focusCycleRoot, aComponent);
  }
  @Override public Component getComponentBefore(
      Container focusCycleRoot, Component aComponent) {
    System.out.println("getComponentAfter");
    button.setEnabled(isAllValid());
    return super.getComponentBefore(focusCycleRoot, aComponent);
  }
});
setFocusCycleRoot(true);
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、パネル内のJTextFieldがすべて入力検証に成功した場合、JButtonをクリック可能に更新するUIのテストを行っています。

  • Tabキーを押してフォーカス移動するとき、すべてのJTextFieldがすべて入力検証に成功する場合は、JButtonをクリック可能に更新し、JButtonにフォーカスを移動し、むすぐにSpaceキーなどでクリックしたい
  • InputVerifier#shouldYieldFocus(...)FocusListener#focusLost(...)で、JButton#setEnabled(true)としても、フォーカスはJButtonに移動しない(初めてJButtonがクリック可能になったときは、先頭のJCheckBoxにフォーカスが移動する)
    • これらの実行より先にLayoutFocusTraversalPolicy#getComponentAfter(...)で、次にフォーカスが移動するコンポーネントが決定されるため、この時点でJButton#setEnabled(true)になっていないJButtonはスキップされる
  • 回避方法:
    • InputVerifier#shouldYieldFocus(...)FocusListener#focusLost(...)内で、EventQueue.invokeLater(() -> button.requestFocusInWindow() );などを実行し、一番後でフォーカスを強制的にJButtonに移動する
    • または、LayoutFocusTraversalPolicy#getComponentAfter(...)をオーバーライドして、事前にJButton#setEnabled(true)を実行する

コメント