Возможно ли в JavaFX изменить политику обхода фокуса, например, в AWT?
Потому что порядок обхода для двух моих HBox
es неверен.
Возможно ли в JavaFX изменить политику обхода фокуса, например, в AWT?
Потому что порядок обхода для двух моих 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 текстовых полей. Чтобы сфокусироваться на правильном переходе из одного текстового поля в другое, мне пришлось перечислить их следующим образом:
<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.
Я нашел этот совет на этой веб-странице: www.wobblycogs.co.uk
Это принятый ответ, адаптированный к изменению внутреннего 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);
}
}
Мы используем фильтры событий 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()
.
В построителе сцен перейдите в меню просмотра и выберите документ. слева будут все объекты в вашем текущем документе fxml. перетащите элементы управления вверх или вниз в списке, чтобы изменить порядок индексов вкладок. Выберите скрыть документ, чтобы использовать другие инструменты, так как панель документов запускает пространство.
Я использовал 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;
}
}
});
Вы можете использовать NodeName.requestFocus()
как сказано выше; Кроме того, убедитесь, что вы запрашиваете этот фокус после создания экземпляра и добавления всех своих узлов для корневого макета, потому что при этом фокус будет меняться.
Вы можете сделать это легко с SceneBuilder. Откройте файл fxml с помощью scenebuilder и перейдите в иерархию на вкладке " Документ ". Расположите элементы управления вводом в нужном вам порядке, перетаскивая элементы управления вводом. Не забудьте проверить Фокус Traversal в свойствах. Тогда политика обхода фокуса будет хорошо работать при нажатии на панель вкладок.