web-dev-qa-db-ja.com

JavaFX:フォーカストラバーサルポリシーを変更するにはどうすればよいですか?

AWTのように、JavaFXでフォーカストラバーサルポリシーを変更することはできますか?

2つのHBoxesのトラバーサル順序が間違っているためです。

26
Sonja

一般的なケースでは、ナビゲーションはコンテナ順、子順、または矢印キーの押下に従って行われます。ノードの順序を変更することができます。これは、この状況での最適なソリューションです。

JFXには、トラバーサルエンジン戦略の置換に関するバックドアがあります。

内部クラスcom.Sun.javafx.scene.traversal.TraversalEngineをサブクラス化できます

engine = new TraversalEngine(this, false) {
            @Override public void trav(Node owner, Direction dir) {
                // do whatever you want
            }
        };

そして使う

setImpl_traversalEngine(engine); 

そのエンジンを適用するために呼び出します。

OpenJFXのコードを観察して、それがどのように機能し、何ができるかを理解することができます。

十分注意してください。これは内部APIであり、おそらく近いうちに変更される可能性があります。したがって、これに依存しないでください(とにかく、この公式に依存することはできません)。

実装例:

public void start(Stage stage) throws Exception {
    final VBox vb = new VBox();

    final Button button1 = new Button("Button 1");
    final Button button2 = new Button("Button 2");
    final Button button3 = new Button("Button 3");

    TraversalEngine engine = new TraversalEngine(vb, false) {
        @Override
        public void trav(Node node, Direction drctn) {
            int index = vb.getChildren().indexOf(node);

            switch (drctn) {
                case DOWN:
                case RIGHT:
                case NEXT:
                    index++;
                    break;
                case LEFT:
                case PREVIOUS:
                case UP:
                    index--;
            }

            if (index < 0) {
                index = vb.getChildren().size() - 1;
            }
            index %= vb.getChildren().size();

            System.out.println("Select <" + index + ">");

            vb.getChildren().get(index).requestFocus();
        }
    };

    vb.setImpl_traversalEngine(engine);

    vb.getChildren().addAll(button1, button2, button3);
    Scene scene = new Scene(vb);
    stage.setScene(scene);
    stage.show();
}

一般的なケースでは、強力な分析スキルが必要になります;)

12
Alexander Kirov

最も簡単な解決策は、FXMLファイルを編集してコンテナを適切に並べ替えることです。例として、私の現在のアプリケーションには、シリアル番号を入力できる登録ダイアログがあります。この目的のために5つのテキストフィールドがあります。フォーカスを1つのテキストフィールドから別のテキストフィールドに正しく渡すには、次のようにリストする必要があります。

<TextField fx:id="tfSerial1" layoutX="180.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial2" layoutX="257.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial3" layoutX="335.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial4" layoutX="412.0" layoutY="166.0" prefWidth="55.0" />
<TextField fx:id="tfSerial5" layoutX="488.0" layoutY="166.0" prefWidth="55.0" />
18
Bluehair

Bluehairの答えは正しいですが、JavaFX Scene Builderでもこれを行うことができます。

左側の列に階層パネルがあります。シーンのすべてのコンポーネントがあります。それらの順序はフォーカストラバーサルの順序を表し、FXMLファイル内の順序に応答します。

このヒントはこのWebページで見つかりました: www.wobblycogs.co.uk

12
none_

これには JavaFXイベントフィルター を使用しています。例:

_cancelButton.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
    @Override
    public void handle(KeyEvent event) {
        if (event.getCode() == KeyCode.TAB && event.isShiftDown()) {
            event.consume();
            getDetailsPane().requestFocus();
        }
    }
});
_

event.consume()は、デフォルトのフォーカストラバーサルを抑制します。そうしないと、requestFocus()を呼び出すときに問題が発生します。

7
dwagelaar

これは、内部APIの変更に適合した受け入れられた回答です(fx-8のある時点で起こりました。私の現在のバージョンは8u60b5です)。もちろん、元の免責事項が引き続き適用されます。これはinternal apiであり、いつでも予告なく変更することができます!

変更(受け入れられた回答と比較して)

  • 親には、ParentTraversalEngineタイプのTraversalEngineが必要です
  • navはもはやTraversalEngine(またはParentTE)のメソッドではなく、TopLevelTraversalEngineのメソッドのみです。
  • ナビゲーションの実装は、アルゴリズムと呼ばれる戦略に委任されています
  • 実際のフォーカス転送は(そうであると思われますか?)TopLevelTEによって処理されます。アルゴリズムは新しいターゲットを見つけて返すだけです

サンプルコードの単純な翻訳:

/**
 * Requirement: configure focus traversal
 * old question with old hack (using internal api):
 * http://stackoverflow.com/q/15238928/203657
 * 
 * New question (closed as duplicate by ... me ..)
 * http://stackoverflow.com/q/30094080/203657
 * Old hack doesn't work, change of internal api
 * rewritten to new internal (sic!) api
 * 
 */
public class FocusTraversal extends Application {

    private Parent getContent() {
        final VBox vb = new VBox();

        final Button button1 = new Button("Button 1");
        final Button button2 = new Button("Button 2");
        final Button button3 = new Button("Button 3");

        Algorithm algo = new Algorithm() {

            @Override
            public Node select(Node node, Direction dir,
                    TraversalContext context) {
                Node next = trav(node, dir);
                return next;
            }

            /**
             * Just for fun: implemented to invers reaction
             */
            private Node trav(Node node, Direction drctn) {
                int index = vb.getChildren().indexOf(node);

                switch (drctn) {
                    case DOWN:
                    case RIGHT:
                    case NEXT:
                    case NEXT_IN_LINE:    
                        index--;
                        break;
                    case LEFT:
                    case PREVIOUS:
                    case UP:
                        index++;
                }

                if (index < 0) {
                    index = vb.getChildren().size() - 1;
                }
                index %= vb.getChildren().size();

                System.out.println("Select <" + index + ">");

                return vb.getChildren().get(index);
            }

            @Override
            public Node selectFirst(TraversalContext context) {
                return vb.getChildren().get(0);
            }

            @Override
            public Node selectLast(TraversalContext context) {
                return vb.getChildren().get(vb.getChildren().size() - 1);
            }

        };
        ParentTraversalEngine engine = new ParentTraversalEngine(vb, algo);
        // internal api in fx8
        // vb.setImpl_traversalEngine(engine);
        // internal api since fx9
        ParentHelper.setTraversalEngine(vb, engine);
        vb.getChildren().addAll(button1, button2, button3);
        return vb;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setScene(new Scene(getContent()));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
6
kleopatra

これはSceneBuilderで簡単に実行できます。 scenebuilderでfxmlファイルを開き、Documentタブのhierarchyに移動します。入力コントロールをドラッグして、必要な順序に入力コントロールを配置します。プロパティでFocus Traversalを確認することを忘れないでください。次に、タブバーを押すと、フォーカストラバーサルポリシーが適切に機能します。

chnage focus traversal policy with SB

1
Jamith Nimantha

シーンビルダーで、表示メニューに移動してドキュメントを表示を選択します。左側は、現在のfxmlドキュメント内のすべてのオブジェクトです。リスト内でコントロールを上下にドラッグして、タブインデックスを並べ替えます。ドキュメントペインがスペースを占有するため、他のツールを使用するには、[ドキュメントを非表示]を選択します。

1
Chris

上記のようにNodeName.requestFocus()を使用できます。さらに、ルートレイアウトのすべてのノードをインスタンス化して追加した後で、このフォーカスを要求するようにしてください。そうすると、フォーカスが変更されます。

0
Aaron

私はフィールドIDの参照と組み合わせてソリューションとしてEventfilterを使用したので、フィールドに(field1、field2、field3、field4)のような名前を付けるだけで、uを必要な場所に配置できます。

mainScene.addEventFilter(KeyEvent.KEY_PRESSED, (event) -> {
        if(event.getCode().equals(KeyCode.TAB)){
            event.consume();
            final Node node =  mainScene.lookup("#field"+focusNumber);
            if(node!=null){
                node.requestFocus();
            }
            focusNumber ++;
            if(focusNumber>11){
              focusNumber=1;
            }
        }
    }); 
0
Patrick Eckert