簡體   English   中英

選擇多行排序時的JavaFX TreeTableView異常

[英]JavaFX TreeTableView exception when sorting with multiple rows selected

我現在正在學習JavaFX,我似乎無法做到正確。 基本上我正在嘗試做的是具有多個選擇的TreeTableView,它可以正常工作,直到我嘗試對列表進行排序。

這是代碼(示例15-1帶有一列的TreeTableView來自http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/tree-table-view.htm#CJAEIFDC ):

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;

public class TreeTableViewSample extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        stage.setTitle("Tree Table View Samples");
        final Scene scene = new Scene(new Group(), 200, 400);
        Group sceneRoot = (Group)scene.getRoot();  

        //Creating tree items
        final TreeItem<String> childNode1 = new TreeItem<>("Child Node 1");
        final TreeItem<String> childNode2 = new TreeItem<>("Child Node 2");
        final TreeItem<String> childNode3 = new TreeItem<>("Child Node 3");

        //Creating the root element
        final TreeItem<String> root = new TreeItem<>("Root node");
        root.setExpanded(true);   

        //Adding tree items to the root
        root.getChildren().setAll(childNode1, childNode2, childNode3);        

        //Creating a column
        TreeTableColumn<String,String> column = new TreeTableColumn<>("Column");
        column.setPrefWidth(150);   

        //Defining cell content
        column.setCellValueFactory((CellDataFeatures<String, String> p) -> 
            new ReadOnlyStringWrapper(p.getValue().getValue()));  

        //Creating a tree table view
        final TreeTableView<String> treeTableView = new TreeTableView<>(root);
        TreeTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); //Setting SelectionMode to MULTIPLE
        treeTableView.getColumns().add(column);
        treeTableView.setPrefWidth(152);
        treeTableView.setShowRoot(true);             
        sceneRoot.getChildren().add(treeTableView);
        stage.setScene(scene);
        stage.show();
    }     
}

我添加了這一行:

TreeTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); //Setting SelectionMode to MULTIPLE

一切正常但是當我選擇多行並嘗試對列進行排序時,只有活動行(最后選擇的)保持選中狀態。

排序時控制台給我這個輸出:

Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException
    at com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList.subList(Unknown Source)
    at javafx.collections.ListChangeListener$Change.getAddedSubList(Unknown Source)
    at javafx.scene.control.TreeTableView$TreeTableViewArrayListSelectionModel.handleSelectedCellsListChangeEvent(Unknown Source)
    at javafx.scene.control.TreeTableView$TreeTableViewArrayListSelectionModel.access$2100(Unknown Source)
    at javafx.scene.control.TreeTableView.sort(Unknown Source)
    at javafx.scene.control.TreeTableView.doSort(Unknown Source)
    at javafx.scene.control.TreeTableView.lambda$new$115(Unknown Source)
    at javafx.scene.control.TreeTableView$$Lambda$99/1473718685.onChanged(Unknown Source)
    at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(Unknown Source)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(Unknown Source)
    at javafx.collections.ObservableListBase.fireChange(Unknown Source)
    at javafx.collections.ListChangeBuilder.commit(Unknown Source)
    at javafx.collections.ListChangeBuilder.endChange(Unknown Source)
    at javafx.collections.ObservableListBase.endChange(Unknown Source)
    at javafx.collections.ModifiableObservableListBase.setAll(Unknown Source)
    at javafx.collections.ObservableListBase.setAll(Unknown Source)
    at com.sun.javafx.scene.control.skin.TableColumnHeader.sortColumn(Unknown Source)
    at com.sun.javafx.scene.control.skin.TableColumnHeader.lambda$static$55(Unknown Source)
    at com.sun.javafx.scene.control.skin.TableColumnHeader$$Lambda$152/863692449.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.Scene$MouseHandler.process(Unknown Source)
    at javafx.scene.Scene$MouseHandler.access$1500(Unknown Source)
    at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$224/2145564822.get(Unknown Source)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.notifyMouse(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(Unknown Source)
    at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

在此先感謝您的幫助。

昨天遇到同樣的問題時,我偶然發現了你的帖子。 遺憾的是沒有人回復你,我決定調查自己。 令人難以置信的是,這種常見功能中的錯誤可能會被忽視並且多年未被發現......

因為它看起來像JavaFX中的一個錯誤,在TreeTableView.TreeTableViewArrayListSelectionModel 有一個處理程序可以更新選擇以響應數據模型中的更改:

        private EventHandler<TreeItem.TreeModificationEvent<S>> treeItemListener = new EventHandler<TreeItem.TreeModificationEvent<S>>() {
        @Override public void handle(TreeItem.TreeModificationEvent<S> e) {

            if (getSelectedIndex() == -1 && getSelectedItem() == null) return;
            <...>

在某些時候(第2421行),它處理排序情況(即排列):

               } else if (e.wasPermutated()) {
                // This handles the sorting case where nothing was added or
                // removed, but the location of the selected index / item
                // has likely changed. This was added to fix RT-30156 and
                // unit tests exist to prevent it from regressing.
                quietClearSelection();
                select(oldSelectedItem);
            } else if (e.wasAdded()) {

此代碼嘗試根據其內容(而不是排序期間可能已更改的行號)重新選擇正確的項目。 但不幸的是,它沒有考慮多選案例,在排序后只留下一個項目。

發生崩潰(異常)是因為TreeTableViewsort()方法似乎解決了同樣的問題(一旦完成排序就恢復正確的選擇),通過在排序之前和之后保存選擇索引,並發出一個排列事件:

    final List<TreeTablePosition<S,?>> prevState = new ArrayList<>(getSelectionModel().getSelectedCells());
    final int itemCount = prevState.size();

    <...>

            final TreeTableViewArrayListSelectionModel<S> sm = (TreeTableViewArrayListSelectionModel<S>) getSelectionModel();
            final ObservableList<TreeTablePosition<S, ?>> newState = sm.getSelectedCells();

            List<TreeTablePosition<S, ?>> removed = new ArrayList<>();
            for (int i = 0; i < itemCount; i++) {
                TreeTablePosition<S, ?> prevItem = prevState.get(i);
                if (!newState.contains(prevItem)) {
                    removed.add(prevItem);
                }
            }

            if (!removed.isEmpty()) {
                // the sort operation effectively permutates the selectedCells list,
                // but we cannot fire a permutation event as we are talking about
                // TreeTablePosition's changing (which may reside in the same list
                // position before and after the sort). Therefore, we need to fire
                // a single add/remove event to cover the added and removed positions.
                ListChangeListener.Change<TreeTablePosition<S, ?>> c = new NonIterableChange.GenericAddRemoveChange<>(0, itemCount, removed, newState);
                sm.handleSelectedCellsListChangeEvent(c);
            }

在前一行中,創建onIterableChange.GenericAddRemoveChange對象,同時假設newState列表具有itemCount元素(而newState將始終包含1個元素,如上所述),並且在嘗試從中獲取itemCount元素時崩潰。

現在,你能做些什么呢? 要徹底修復它,你需要

  1. TreeTableView並覆蓋sort()方法,或
  2. 使用TreeTableView.SetSelectionModel()提供您自己的SelectionModel實現(可能基於TreeTableViewArrayListSelectionModel實現TreeTableView.SetSelectionModel()

這兩種方法都不簡單,因為代碼大量使用私有可訪問的成員。 使用第一個解決方案,您還將遇到FXMLLoader的問題,它只能創建TreeTableView (而不是您的子類MyTreeTableView ),但您應該能夠手動創建對象。

我想我會堅持以下解決方法,每當表被排序時清除選擇(在我的情況下不會發生很多):

 myTreeTable.setOnSort(event -> { 
     if(myTreeTable.getSelectionModel().getSelectedIndices().size() > 1) 
         myTreeTable.getSelectionModel().clearSelection(); 
 });

希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM