AWT のように、JavaFX でフォーカス トラバーサル ポリシーを変更することは可能ですか?
HBox
私の 2 つのesの走査順序が間違っているためです。
最も簡単な解決策は、FXML ファイルを編集してコンテナを適切に並べ替えることです。例として、私の現在のアプリケーションには、シリアル番号を入力できる登録ダイアログがあります。この目的のために 5 つのテキスト フィールドがあります。あるテキスト フィールドから別のテキスト フィールドにフォーカスを正しく渡すには、次のようにリストする必要がありました。
<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" />
通常、ナビゲーションはコンテナ順、子順、または矢印キーの押下に従って行われます。ノードの順序を変更できます。この状況では、これが最適なソリューションになります。
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();
}
一般的なケースでは、強力な分析スキルが必要になります;)
これは、内部 API の変更に適応した受け入れられた回答です (fx-8 のある時点で発生し、現在のバージョンは 8u60b5 です)。明らかに、元の免責事項が引き続き適用されます。これは内部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);
}
}
フィールド ID の参照と組み合わせて Eventfilter をソリューションとして使用したので、フィールドに (field1,field2,field3,field4) のような名前を付けるだけで、必要な場所にフィールドを配置できます。
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;
}
}
});