TreeView / TreeTableView - KeyEvent F2 вызывает внутренний NPE JavaFX - PullRequest
0 голосов
/ 31 марта 2020

Поместив «стандартный» редактор в столбец 0 TreeTableView, выполнив следующее:

treeTableView.columns.get(0).setCellFactory( TextFieldTreeTableCell.forTreeTableColumn());

... приятно обнаружить, что несколько вещей начинают редактирование TreeItem: один - щелкнуть по ячейке, а другой - нажать F2.

Однако, к моему небольшому разочарованию, если я запускаю приложение и, не выбрав TreeItem с помощью мыши, но выбрав программно первый потомок root TreeItem, если я затем (возможно, после навигации с использованием только клавиш клавиатуры) нажимаю F2, внутренняя часть JavaFX выдает NPE, который выглядит следующим образом:

java.lang.NullPointerException: null
    at com.sun.javafx.scene.control.behavior.TableViewBehaviorBase.activate(TableViewBehaviorBase.java:890)
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$KeyHandler.process(Scene.java:4058)
    at javafx.scene.Scene$KeyHandler.access$1500(Scene.java:4004)
    at javafx.scene.Scene.processKeyEvent(Scene.java:2121)
    at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2595)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:248)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
    at com.sun.glass.ui.View.handleKeyEvent(View.java:547)
    at com.sun.glass.ui.View.notifyKey(View.java:971)
    at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
    at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277)

Может Нельзя сказать, что мышь фокусируется прямо на TreeItem, потому что суперкласс TreeItem - Object. Я поместил слушателя владельца фокуса на Scene, что подтверждает, что ничего не связано с фокусировкой, когда вы нажимаете на TreeTableView. И все же, качественно, что-то меняется.

Может быть, это что-то, связанное с Behavior, InputMap, "скинами" или любым из многих таинственных вещей JavaFX, о которых я не знаю.

При таких обстоятельствах мне кажется, что одним из вариантов может быть как-то перехватить нажатие клавиши F2 и предотвратить функционирование по умолчанию, а также программно инициировать редактирование TreeItem, который выбран, но который не имеет фокуса.

NB. Я включил прослушиватели событий «нажатие клавиши» и «отпускание клавиши» на TreeTableView. Они включали следующую строку:

TreeTablePosition pos = treeTableView.getFocusModel().getFocusedCell();

«Аномальное» событие, которое я описываю, когда внутренние компоненты JavaFX выдают NPE, характеризуется в слушателе «освобождения ключа» pos.col == -1 и pos.getTableColumn() == null. По логированию я также могу сказать, что NPE происходит до того, как обработчик «ключ отпущен» отвечает.

Для информации, слушатель с нажатой клавишей никогда ничего не регистрирует, когда я нажимаю F2 без первого щелчка мышью на TreeItem (без сомнения, из-за внутреннего Exception JavaFX, убивающего обычную "трансляцию") к слушателям), что позволяет предположить, что инфраструктура JavaFX также предшествует любому добавленному пользователем слушателю нажатия клавиш.

Ответы [ 2 ]

1 голос
/ 31 марта 2020

Если вы выберете ячейку, используя TableSelectionModel#select(int,TableColumnBase), то NPE не появится.

Выбирает ячейку на данном пересечении строк / столбцов. Если элемент управления таблицей находится в режиме «выбора ячеек» (где можно выбрать отдельные ячейки, а не целые строки), и если аргумент столбца равен нулю, этот метод должен выбрать все ячейки в данной строке.

Например:

TreeTableColumn col = treeTableView.getColumns().get(0);
treeTableView.getSelectionModel().select(rowIndex, col);

С учетом вышесказанного вам не нужно беспокоиться о модели фокусировки. Причина этого заключается в том, что реализация модели выбора по умолчанию будет фокусироваться на заданной строке + столбце (то есть ячейке) для вас (не уверен, что это гарантированное поведение, но, безусловно, вежливое поведение).

По сути, ваш текущий код вызвал строка, которая будет выделена / выделена, но не указанная ячейка c в этой строке. Таким образом, когда вы пытались войти в «режим редактирования», не было никакого способа определить, на какую ячейку нацеливаться. Я бы посчитал NPE ошибкой. Не уверен, что означало , когда ни один столбец не выделен / не сфокусирован, но поведение элемента управления должно корректно обрабатывать этот случай.

Если интересно, ниже был мой процесс отладки.


Вот код, ответственный за NPE:

protected void activate(KeyEvent e) {
    TableSelectionModel sm = getSelectionModel();
    if (sm == null) return;

    TableFocusModel fm = getFocusModel();
    if (fm == null) return;

    TablePositionBase<TC> cell = getFocusedCell();
    sm.select(cell.getRow(), cell.getTableColumn());
    setAnchor(cell);

    // check if we are editable
    boolean isEditable = isControlEditable() && cell.getTableColumn().isEditable();

    // edit this row also
    if (isEditable && cell.getRow() >= 0) {
        editCell(cell.getRow(), cell.getTableColumn());
        e.consume();
    }
}

В частности, это проблемная строка c:

boolean isEditable = isControlEditable() && cell.getTableColumn().isEditable();

Это означает, что cell равно нулю или getTableColumn() возвращает ноль. После некоторой отладки с помощью JEP 358: Helpful NullPointerExceptions мы знаем, что это последнее:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.control.TableColumnBase.isEditable()" because the return value of "javafx.scene.control.TablePositionBase.getTableColumn()" is null
    at javafx.controls/com.sun.javafx.scene.control.behavior.TableViewBehaviorBase.activate(TableViewBehaviorBase.java:898)

Если getTableColumn() возвращает ноль, то это означает, что, насколько фокус Что касается модели, то здесь нет фокусировки на столбцах и, следовательно, нет специфической ячейки c Исправление состоит в том, чтобы выбрать / сфокусировать указанную c строку + столбец (т.е. ячейку), а не только всю строку.

0 голосов
/ 31 марта 2020

Нашел ответ (или ответ). На самом деле система фокусировки TreeTableView ... см. getFocusModel(). Похоже, что изменения в этом не воспринимаются механизмом владельца фокуса Stage:

stage.getScene().focusOwnerProperty().addListener( new ChangeListener(){
...

Итак, вам нужно изменить внутреннюю модель фокуса при каждом изменении выбора:

treeTableView.getSelectionModel().getSelectedItems().addListener(new ListChangeListener() {
    void onChanged(ListChangeListener.Change c) {
        TreeItem newSelectedItem = c.list.get(0);
        int row = treeTableView.getRow( newSelectedItem );
        treeTableView.getFocusModel().focus( row, treeTableView.getColumns().get(0) );
    }
}

Необходимо сделать одно или два замечания по этому поводу:

  1. Я не совсем уверен, что c.list.get( 0 ) всегда можно гарантировать доставку вновь выбранного предмета ... и это особенно верно, если режим выбора является множественным выбором
  2. Я немного удивлен, обнаружив, что даже если вы нажмете на ячейку в одном из столбцов таблицы (т.е. столбцы 1, 2, 3 ...), в отличие от столбца дерева (столбец 0), вы, кажется, всегда возвращаете TreeItem из столбца 0. В обычной таблице вы, конечно, можете выбрать блок ячеек наугад. Теперь мне интересно, можете ли вы сделать это с помощью этого элемента управления или все выделение действительно является «выделением строки».
  3. Я бы подумал, что вышеуказанная функциональность должна быть включена в реализацию класса по умолчанию: конечно, это не очень хорошо, если простая операция на клавиатуре того, кто ненавидит или не использует мышь, вызывает исключение JavaFX (а также означает, что операция редактирования не происходит).
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...