TableView ScrollBar策略

时间:2015-01-11 11:44:18

标签: javafx scrollbar tableview

有没有办法在类似于ScrollPane的TableView中更改ScrollBar的策略?我只看到TableView的VirtualFlow计算可见性,但不会出现手动干扰。

我需要垂直滚动条始终可见而水平从不。更改条形的可见状态不起作用。

示例:

import java.time.LocalDate;
import java.time.Month;
import java.util.Set;

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import com.sun.javafx.scene.control.skin.VirtualFlow;

public class ScrollBarInTableViewDemo extends Application {

    private TableView<Data> table1 = new TableView<>(); // table with scrollbars
    private TableView<Data> table2 = new TableView<>(); // table without scrollbars

    private final ObservableList<Data> data =
            FXCollections.observableArrayList( 
                    new Data( LocalDate.of(2015, Month.JANUARY, 10), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 11), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 12), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 13), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 14), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 15), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 16), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 17), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 18), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 19), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 20), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 21), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 22), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 23), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 24), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 25), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 26), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 27), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 28), 10.0, 20.0, 30.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 29), 40.0, 50.0, 60.0),
                    new Data( LocalDate.of(2015, Month.JANUARY, 30), 10.0, 20.0, 30.0)

            );

    final HBox hb = new HBox();

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

    @Override
    public void start(Stage stage) {

        Scene scene = new Scene(new Group());

        stage.setTitle("Table View Sample");
        stage.setWidth(800);
        stage.setHeight(800);

        // setup table columns
        setupTableColumns( table1);
        setupTableColumns( table2);

        // fill tables with data
        table1.setItems(data);
        table1.setTableMenuButtonVisible(true);

        // create container
        HBox hBox = new HBox();
        hBox.getChildren().addAll( table1, table2);

        ((Group) scene.getRoot()).getChildren().addAll( hBox);

        stage.setScene(scene);
        stage.show();

        ScrollBar table1HorizontalScrollBar = findScrollBar( table1, Orientation.HORIZONTAL);
        ScrollBar table1VerticalScrollBar = findScrollBar( table1, Orientation.VERTICAL);

        // this doesn't work:
        table1HorizontalScrollBar.setVisible(false);
        table1VerticalScrollBar.setVisible(false);

        ScrollBar table2HorizontalScrollBar = findScrollBar( table2, Orientation.HORIZONTAL);
        ScrollBar table2VerticalScrollBar = findScrollBar( table2, Orientation.VERTICAL);

        // this doesn't work:
        table2HorizontalScrollBar.setVisible(true);
        table2VerticalScrollBar.setVisible(true);

        // enforce layout to see if anything has an effect
        VirtualFlow flow1 = (VirtualFlow) table1.lookup(".virtual-flow");
        flow1.requestLayout();

        VirtualFlow flow2 = (VirtualFlow) table2.lookup(".virtual-flow");
        flow2.requestLayout();

    }

    /**
     * Primary table column mapping.
     */
    private void setupTableColumns( TableView table) {


        TableColumn<Data, LocalDate> dateCol = new TableColumn<>("Date");
        dateCol.setPrefWidth(120);
        dateCol.setCellValueFactory(new PropertyValueFactory<>("date"));

        TableColumn<Data, Double> value1Col = new TableColumn<>("Value 1");
        value1Col.setPrefWidth(90);
        value1Col.setCellValueFactory(new PropertyValueFactory<>("value1"));

        TableColumn<Data, Double> value2Col = new TableColumn<>("Value 2");
        value2Col.setPrefWidth(90);
        value2Col.setCellValueFactory(new PropertyValueFactory<>("value2"));

        TableColumn<Data, Double> value3Col = new TableColumn<>("Value 3");
        value3Col.setPrefWidth(90);
        value3Col.setCellValueFactory(new PropertyValueFactory<>("value3"));

        table.getColumns().addAll( dateCol, value1Col, value2Col, value3Col);


    }

    /**
     * Find the horizontal scrollbar of the given table.
     * @param table
     * @return
     */
    private ScrollBar findScrollBar(TableView<?> table, Orientation orientation) {

        // this would be the preferred solution, but it doesn't work. it always gives back the vertical scrollbar
        //      return (ScrollBar) table.lookup(".scroll-bar:horizontal");
        //      
        // => we have to search all scrollbars and return the one with the proper orientation

        Set<Node> set = table.lookupAll(".scroll-bar");
        for( Node node: set) {
            ScrollBar bar = (ScrollBar) node;
            if( bar.getOrientation() == orientation) {
                return bar;
            }
        }

        return null;

    }

    /**
     * Data for primary table rows.
     */
    public static class Data {

        private final ObjectProperty<LocalDate> date;
        private final SimpleDoubleProperty value1;
        private final SimpleDoubleProperty value2;
        private final SimpleDoubleProperty value3;

        public Data( LocalDate date, double value1, double value2, double value3) {

            this.date = new SimpleObjectProperty<LocalDate>( date);

            this.value1 = new SimpleDoubleProperty( value1);
            this.value2 = new SimpleDoubleProperty( value2);
            this.value3 = new SimpleDoubleProperty( value3);
        }

        public final ObjectProperty<LocalDate> dateProperty() {
            return this.date;
        }
        public final LocalDate getDate() {
            return this.dateProperty().get();
        }
        public final void setDate(final LocalDate date) {
            this.dateProperty().set(date);
        }
        public final SimpleDoubleProperty value1Property() {
            return this.value1;
        }
        public final double getValue1() {
            return this.value1Property().get();
        }
        public final void setValue1(final double value1) {
            this.value1Property().set(value1);
        }
        public final SimpleDoubleProperty value2Property() {
            return this.value2;
        }
        public final double getValue2() {
            return this.value2Property().get();
        }
        public final void setValue2(final double value2) {
            this.value2Property().set(value2);
        }
        public final SimpleDoubleProperty value3Property() {
            return this.value3;
        }
        public final double getValue3() {
            return this.value3Property().get();
        }
        public final void setValue3(final double value3) {
            this.value3Property().set(value3);
        }


    }
} 

enter image description here

5 个答案:

答案 0 :(得分:2)

将TableView对象添加到StackPane对象。滚动条将自动添加到表格中。它们也会自动调整大小。

我发现其他容器类型对TableView对象不起作用。其中包括ScrollPane对象和AnchorPane对象。所以我想知道是否可以将HBox添加到该列表中。

答案 1 :(得分:2)

如果TableView没有显示,它也可以工作。由于

,如果有一些数据,它会工作
private boolean computeBarVisiblity() {
    if (cells.isEmpty()) {
        // In case no cells are set yet, we assume no bars are needed
        needLengthBar = false;
        needBreadthBar = false;
        return true;
    }

    // ... other method code
}
在VirtualFlow代码中

。但你可以扩展这个例子。

        public static void alwaysShowVerticalScroll(final TableView view) {
    new Thread(() -> {
        while (true) {
            Set<Node> nodes = view.lookupAll(".scroll-bar");
            if (nodes.isEmpty()) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignore) {
                }
                continue;
            }
            Node node = view.lookup(".virtual-flow");
            if (node == null) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignore) {
                }
                continue;
            }
            final VirtualFlow flow = (VirtualFlow) node;
            for (Node n : nodes) {
                if (n instanceof ScrollBar) {
                    final ScrollBar bar = (ScrollBar) n;
                    if (bar.getOrientation().equals(Orientation.VERTICAL)) {
                        bar.visibleProperty().addListener(l -> {
                            if (bar.isVisible()) {
                                return;
                            }
                            Field f = getVirtualFlowField("needLengthBar");
                            Method m = getVirtualFlowMethod("updateViewportDimensions");
                            try {
                                f.setBoolean(flow, true);
                                m.invoke(flow);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            bar.setVisible(true);
                        });
                        Platform.runLater(() -> {
                            bar.setVisible(true);
                        });
                        break;
                    }
                }
            }
            break;
        }
    }).start();
}

private static Field getVirtualFlowField(String name) {
    Field field = null;
    try {
        field = VirtualFlow.class.getDeclaredField(name);
        field.setAccessible(true);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
    return field;
}

private static Method getVirtualFlowMethod(String name) {
    Method m = null;
    try {
        m = VirtualFlow.class.getDeclaredMethod(name);
        m.setAccessible(true);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return m;
}

答案 2 :(得分:1)

CSS解决方案很糟糕,因为当TableMenuButton可见时(&#34; +&#34;按钮可以切换列的可见性),它会弄乱视图。

我的解决方案(仍然非常讨厌)获得对滚动条的引用:

TableView类创建一个包含VirtualFlow的TableViewSkin。然后,VirtualFlow再次保存水平和垂直VirtualScrollBar。我重写了TableView和TableViewSkin。根据您的问题,您可能需要覆盖并更改som。 (检查文件的来源..)。

public class OneRowTableView<S> extends TableView<S>{

@Override 
protected Skin<?> createDefaultSkin() {
    return new OneRowTableViewSkin<S>(this);
}
public class OneRowTableViewSkin<T> extends TableViewSkin<T> {
    public OneRowTableViewSkin(final TableView<T> tableView) {
        super(tableView);
        VirtualScrollBar vBar = null;
        for(Node child : flow.getChildrenUnmodifiable()){
            if(child instanceof VirtualScrollBar){
                if(((VirtualScrollBar)child).getOrientation() == Orientation.VERTICAL){
                    Log.d("Found the vertical scroll bar!");
                    vBar = (VirtualScrollBar) child;
                }
            }
        }
        if(vBar == null) return;
        vBar.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler() {

            @Override
            public void handle(Event event) {
                Log.d("Suppressing mouse pressing on the Vertical Virtual Scroll Bar");
                event.consume();
            }
        });
    }
}

}

然后,您可以在FXML或代码中使用新的TableView。

我只是想禁用滚动,所以我也过滤了整个表格的滚动并消耗了事件。我的CSS隐藏按钮:

.table-view *.scroll-bar:vertical *.increment-button,
.table-view *.scroll-bar:vertical *.decrement-button,
.table-view *.scroll-bar:vertical *.increment-arrow, 
.table-view *.scroll-bar:vertical *.decrement-arrow {
    -fx-background-color: null;
}

答案 3 :(得分:-1)

CSS

对我来说这是唯一可行的解​​决方案,只是隐藏它

.scroll-bar:vertical {
    -fx-opacity: 0;
    -fx-padding:-7;
}

答案 4 :(得分:-2)

我在这种情况下将我的对象放入ScrollPane并使用hbarPolicy/vbarPolicy

在Fxml中,它看起来像:

<ScrollPane fx:id="yourScrollPane" hbarPolicy="NEVER">
<ScrollPane fx:id="yourScrollPane" vbarPolicy="ALWAYS">

在控制器中,例如:

yourScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
yourScrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);