web-dev-qa-db-ja.com

TableView ScrollBarポリシー

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

15
Roland

TableViewオブジェクトをStackPaneオブジェクトに追加します。スクロールバーがテーブルに自動的に追加されます。また、自動的にサイズ変更されます。

TableViewオブジェクトでは、他のコンテナータイプがうまく機能しないことがわかりました。それらの中には、ScrollPaneオブジェクトとAnchorPaneオブジェクトがあります。したがって、HBoxをそのリストに追加できるかどうか疑問に思います。

2
Alf

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
Steel Rat

CSSソリューションは不適切です。TableMenuButton(列の表示を切り替えることができる「+」ボタン)が表示されていると、ビューがめちゃくちゃになるからです。

スクロールバーへの参照を取得するための私の解決策(まだかなり厄介です):

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();
            }
        });
    }
}

}

その後、新しいTableViewをFXMLまたはコードで使用できます。

スクロールを無効にしたかったので、テーブル全体のスクロールをフィルタリングしてイベントを消費しました。ボタンを非表示にする私の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;
}
1
Jesper Pedersen