Подтвердить что ты не робот

JavaFX: Как изменить политику обхода фокуса?

Возможно ли в JavaFX изменить политику обхода фокуса, например, в AWT?

Потому что порядок обхода для двух моих HBox es неверен.

4b9b3361

Ответ 1

В обычном случае навигация выполняется в порядке контейнера, в порядке расположения детей или в соответствии с нажатием клавиш со стрелками. Вы можете изменить порядок узлов - это будет оптимальное решение для вас в этой ситуации.

В 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();
}

Для обычного случая потребуются сильные аналитические навыки;)

Ответ 2

Самое простое решение - отредактировать файл 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" />

Ответ 3

Ответ Bluehair прав, но вы можете сделать это даже в JavaFX Scene Builder.

В левой колонке есть панель иерархии. С вашего сайта есть все ваши компоненты. Их порядок представляет собой порядок обхода фокуса и отвечает на их порядок в файле FXML.

Я нашел этот совет на этой веб-странице: www.wobblycogs.co.uk

Ответ 4

Это принятый ответ, адаптированный к изменению внутреннего API (произошло в какой-то момент fx-8, моя текущая версия 8u60b5). Очевидно, что оригинальный отказ от ответственности по-прежнему применяется: это внутренний API, открытый для изменения без предварительного уведомления в любое время!

Изменения (по сравнению с принятым ответом)

  • Родителю нужен TraversalEngine типа ParentTraversalEngine
  • 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);
    }
}

Ответ 5

Мы используем фильтры событий 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().

Ответ 6

В построителе сцен перейдите в меню просмотра и выберите документ. слева будут все объекты в вашем текущем документе fxml. перетащите элементы управления вверх или вниз в списке, чтобы изменить порядок индексов вкладок. Выберите скрыть документ, чтобы использовать другие инструменты, так как панель документов запускает пространство.

Ответ 7

Я использовал 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;
            }
        }
    }); 

Ответ 8

Вы можете использовать NodeName.requestFocus() как сказано выше; Кроме того, убедитесь, что вы запрашиваете этот фокус после создания экземпляра и добавления всех своих узлов для корневого макета, потому что при этом фокус будет меняться.

Ответ 9

Вы можете сделать это легко с SceneBuilder. Откройте файл fxml с помощью scenebuilder и перейдите в иерархию на вкладке " Документ ". Расположите элементы управления вводом в нужном вам порядке, перетаскивая элементы управления вводом. Не забудьте проверить Фокус Traversal в свойствах. Тогда политика обхода фокуса будет хорошо работать при нажатии на панель вкладок.

chnage focus traversal policy with SB