3

私は現在 JavaFX を学んでいますが、1 つのことを正しく理解できないようです。基本的に私がやろうとしているのは、複数選択の TreeTableView です。これは、リストを並べ替えようとするまでは正常に機能します。

コードは次のとおりです( http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/tree-table-view.htm#CJAEIFDCの例15-1 1列のTreeTableView ):

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)

助けてくれてありがとう。

4

1 に答える 1

9

昨日同じ問題に遭遇したとき、私はあなたの投稿に出くわしました。誰もあなたに返事をくれなかったのは残念です、そして私は自分自身を調査することにしました. このような一般的な機能のバグが何年も気付かれずに修正されない可能性があることは、いまだに信じられないことです...

私には 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()) {

このコードは、その内容に基づいて正しい項目を再選択しようとします (並べ替え中に変更された可能性のある行番号ではありません)。しかし、残念ながら、複数選択のケースを考慮していないため、並べ替え後に 1 つの項目のみが選択されたままになります。

クラッシュ (例外) が発生するのは、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);
            }

before-last 行では、リストに要素があるとonIterableChange.GenericAddRemoveChange仮定してオブジェクトが作成され(上記で説明したように、常に 1 つの要素が含まれます)、そこから要素を取得しようとするとクラッシュします。newStateitemCountnewStateitemCount

さて、あなたはそれについて何ができますか?それをきれいに修正するには、次のいずれかが必要です

  1. TreeTableViewand overrideメソッドをサブクラス化するsort()、または
  2. SelectionModel(おそらく実装に基づいてTreeTableViewArrayListSelectionModel)の独自の実装を提供しますTreeTableView.SetSelectionModel()

コードは非公開でアクセス可能なメンバーを大量に使用するため、どちらの方法も単純ではありません。FXMLLoader最初の解決策では、作成のみが可能なTreeTableView(サブクラスではなく)の問題にも直面しMyTreeTableViewますが、オブジェクトを手動で作成できるはずです。

次の回避策に固執し、テーブルがソートされるたびに選択をクリアすると思います (私の場合はあまり起こりません)。

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

お役に立てれば!

于 2015-11-05T13:02:33.033 に答える