JavaFX:刷新内容后如何记住tabcontent的滚动位置?

时间:2017-04-25 08:41:22

标签: java javafx treeview

我在JavaFX中使用treeview来实现文件浏览器。我将Tab中的树视图设置为conent。当我使用预定线程刷新树视图元素时,我无法滚动到以前选择的树项。这是我的最低版本。

 /**
 *
 * @author nika
 */
public class TabTreeView extends Application implements Runnable {

private TreeItem<String> root;
private TreeView<String> tv = new TreeView<>();
private static ArrayList<NumberPair> rememberExpanded = new ArrayList<>();
private TreeItem<String> previouslySelectedTreeItem;

@Override
public void run() {
    root = new TreeItem<>("Root");
    root.setExpanded(true);
    for (int i = 0; i < 10; i++) {
        TreeItem<String> sublevel = new TreeItem<>("Level : " + i + " TS: " + System.currentTimeMillis());
        final int level = i;
        if (rememberExpanded.contains(new NumberPair(level, -9999))) {
            sublevel.setExpanded(true);
        }
        sublevel.expandedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if (newValue) {
                    rememberExpanded.add(new NumberPair(level, -9999));
                } else {
                    rememberExpanded.remove(new NumberPair(level, -9999));
                }
            }
        });
        for (int j = 0; j < 5; j++) {
            TreeItem<String> subsublevel = new TreeItem<>("SubLevel : " + j + " @ " + i + " TS:" + System.currentTimeMillis());
            final int level2 = j;
            if (rememberExpanded.contains(new NumberPair(level, level2))) {
                sublevel.setExpanded(true);
            }
            subsublevel.expandedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                    if (newValue) {
                        rememberExpanded.add(new NumberPair(level, level2));
                    } else {
                        rememberExpanded.remove(new NumberPair(level, level2));
                    }
                }
            });

            sublevel.getChildren().add(subsublevel);
        }
        root.getChildren().add(sublevel);
    }
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            tv.setRoot(root);
            //scroll to previously selected item
            //i know this one is wrong 1. "as it is not working", 2. "because it will only look into children of root not grandchildren".
            tv.scrollTo(root.getChildren().indexOf(previouslySelectedTreeItem));
        }
    });
}

@Override
public void start(Stage primaryStage) throws Exception {
    TabPane tabpane = new TabPane();
    tv.selectionModelProperty().addListener(new ChangeListener<MultipleSelectionModel<TreeItem<String>>>() {
        @Override
        public void changed(ObservableValue<? extends MultipleSelectionModel<TreeItem<String>>> observable, MultipleSelectionModel<TreeItem<String>> oldValue, MultipleSelectionModel<TreeItem<String>> newValue) {
            previouslySelectedTreeItem = newValue.getSelectedItem();
        }
    });
    Tab t = new Tab("Demo", tv);
    tabpane.getTabs().add(t);
    BorderPane bp = new BorderPane(tabpane);
    primaryStage.setScene(new Scene(bp));
    primaryStage.show();
    ScheduledExecutorService exc = Executors.newScheduledThreadPool(1);
    exc.scheduleAtFixedRate(this, 0, 2, TimeUnit.SECONDS);

}

public static void main(String[] args) throws Exception {
    launch(TabTreeView.class, args);

}

private class NumberPair {

    int first, second;

    public NumberPair(int first, int second) {
        this.first = first;
        this.second = second;
    }

    public int getFirst() {
        return first;
    }

    public void setFirst(int first) {
        this.first = first;
    }

    public int getSecond() {
        return second;
    }

    public void setSecond(int second) {
        this.second = second;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 37 * hash + this.first;
        hash = 37 * hash + this.second;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final NumberPair other = (NumberPair) obj;
        if (this.first != other.first) {
            return false;
        }
        if (this.second != other.second) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "NumberPair{" + "first=" + first + ", second=" + second + '}';
    }

}

}

无论如何要记住滚动tabcontent或treeview的位置?这样我可以在刷新视图后恢复滚动位置。

在这个实现中,我添加了一个NumberPair类来记住扩展项目。

1 个答案:

答案 0 :(得分:1)

你可以通过styleclass找到TreeView的ScrollBar参考。试试这个。

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        double position[] = new double[]{0.0d};
        Optional<ScrollBar> scroll = findScrollBar(tv, Orientation.VERTICAL);
        scroll.ifPresent(s -> position[0] = s.getValue());
        tv.setRoot(root);
        scroll.ifPresent(s -> s.setValue(position[0]));
    }
});

private Optional<ScrollBar> findScrollBar(TreeView<String> from, Orientation orientation) {
    Set<Node> nodes = from.lookupAll(".scroll-bar");
    return nodes.stream()
            .filter(node -> node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == orientation)
            .map(node -> ((ScrollBar)node))
            .findFirst();
}

但请注意,尝试在TreeItem中保留相同的项目可能是一个很好的方法。只要您从唯一的根添加和删除项目,TreeView就会被设置为保留选择扩展和滚动位置。