it-swarm.com.ru

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

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

Потому что порядок обхода для двух моих HBoxes неправильный. 

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

Это потребует сильных аналитических навыков для общего случая;)

11
Alexander Kirov

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

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

У вас есть панель иерархии в левом столбце. Есть все ваши компоненты со сцены. Их порядок представляет порядок обхода фокуса, и он отвечает на их порядок в файле FXML.

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

11
none_

Это принятый ответ, адаптированный к изменению внутреннего 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);
    }
}
6
kleopatra

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

5
dwagelaar

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

1
Chris

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

0
Aaron

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