AWTのように、JavaFXでフォーカストラバーサルポリシーを変更することはできますか?
2つのHBox
esのトラバーサル順序が間違っているためです。
一般的なケースでは、ナビゲーションはコンテナ順、子順、または矢印キーの押下に従って行われます。ノードの順序を変更することができます。これは、この状況での最適なソリューションです。
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();
}
一般的なケースでは、強力な分析スキルが必要になります;)
最も簡単な解決策は、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" />
Bluehairの答えは正しいですが、JavaFX Scene Builderでもこれを行うことができます。
左側の列に階層パネルがあります。シーンのすべてのコンポーネントがあります。それらの順序はフォーカストラバーサルの順序を表し、FXMLファイル内の順序に応答します。
このヒントはこのWebページで見つかりました: www.wobblycogs.co.uk
これには 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()
を呼び出すときに問題が発生します。
これは、内部APIの変更に適合した受け入れられた回答です(fx-8のある時点で起こりました。私の現在のバージョンは8u60b5です)。もちろん、元の免責事項が引き続き適用されます。これはinternal apiであり、いつでも予告なく変更することができます!
変更(受け入れられた回答と比較して)
サンプルコードの単純な翻訳:
/**
* 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);
}
}
シーンビルダーで、表示メニューに移動してドキュメントを表示を選択します。左側は、現在のfxmlドキュメント内のすべてのオブジェクトです。リスト内でコントロールを上下にドラッグして、タブインデックスを並べ替えます。ドキュメントペインがスペースを占有するため、他のツールを使用するには、[ドキュメントを非表示]を選択します。
上記のようにNodeName.requestFocus()
を使用できます。さらに、ルートレイアウトのすべてのノードをインスタンス化して追加した後で、このフォーカスを要求するようにしてください。そうすると、フォーカスが変更されます。
私はフィールド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;
}
}
});