Skip to content

Instantly share code, notes, and snippets.

@Roland09
Created February 11, 2015 18:08
Show Gist options
  • Save Roland09/5833b61419b5ddb2b440 to your computer and use it in GitHub Desktop.
Save Roland09/5833b61419b5ddb2b440 to your computer and use it in GitHub Desktop.
SummaryTableDemo.java
/*
* hide tableview header:
* hide-tableview-header: our custom selector, we don't want to apply the hidden header on all tableviews
* column-header-background: tableview internal header
*/
.tableview-header-hidden .column-header-background {
visibility: hidden;
-fx-padding: -1.0em;
}
/* Hide the horizontal scrollbar of the summary table
* derived from http://blog.ngopal.com.np/2012/09/25/how-to-bind-vertical-scroll-in-multi-tableview/
*/
.sumtable .scroll-bar:horizontal .track{
-fx-padding:0px;
-fx-background-color:transparent;
-fx-border-color:transparent;
-fx-background-radius: 0em;
-fx-border-radius:2em;
}
.sumtable .scroll-bar:horizontal .increment-button ,
.sumtable .scroll-bar:horizontal .decrement-button {
-fx-background-color:transparent;
-fx-background-radius: 0em;
-fx-padding:0 0 0 0;
}
.sumtable .scroll-bar:horizontal .increment-arrow,
.sumtable .scroll-bar:horizontal .decrement-arrow
{
-fx-shape: " ";
-fx-padding:0;
}
.sumtable .scroll-bar:horizontal .thumb {
-fx-background-color:transparent;
-fx-background-insets: 0, 0, 0;
-fx-background-radius: 2em;
-fx-padding:0px;
}
import java.text.Format;
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.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
* Table with a summary table. The summary table is a 2nd table which is synchronized with the primary table.
*
* TODO:
* + always show vertical bars for both the primary and the summary table, otherweise the width of both tables wouldn't be the same
* + hide the horizontal scrollbar of the summary table
*
*/
public class SummaryTableDemo extends Application {
private TableView<Data> mainTable = new TableView<>();
private TableView<SumData> sumTable = new TableView<>();
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)
);
// TODO: calculate values
private final ObservableList<SumData> sumData =
FXCollections.observableArrayList(
new SumData( "Sum", 0.0, 0.0, 0.0),
new SumData( "Min", 0.0, 0.0, 0.0),
new SumData( "Max", 0.0, 0.0, 0.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());
// load css
scene.getStylesheets().addAll(getClass().getResource("application.css").toExternalForm());
stage.setTitle("Table View Sample");
stage.setWidth(400);
stage.setHeight(550);
// setup table columns
setupMainTableColumns();
setupSumTableColumns();
// fill tables with data
mainTable.setItems(data);
sumTable.setItems(sumData);
// set dimensions
sumTable.setPrefHeight(90);
// bind/sync tables
for( int i=0; i < mainTable.getColumns().size(); i++) {
TableColumn<Data,?> mainColumn = mainTable.getColumns().get(i);
TableColumn<SumData,?> sumColumn = sumTable.getColumns().get(i);
// sync column widths
sumColumn.prefWidthProperty().bind( mainColumn.widthProperty());
// sync visibility
sumColumn.visibleProperty().bind( mainColumn.visibleProperty());
}
// allow changing of column visibility
mainTable.setTableMenuButtonVisible(true);
// hide header (variation of jewelsea's solution: http://stackoverflow.com/questions/12324464/how-to-javafx-hide-background-header-of-a-tableview)
sumTable.getStyleClass().add("tableview-header-hidden");
// hide horizontal scrollbar via styles
sumTable.getStyleClass().add( "sumtable");
// create container
BorderPane bp = new BorderPane();
bp.setCenter(mainTable);
bp.setBottom(sumTable);
// fit content
bp.prefWidthProperty().bind(scene.widthProperty());
bp.prefHeightProperty().bind(scene.heightProperty());
((Group) scene.getRoot()).getChildren().addAll( bp);
stage.setScene(scene);
stage.show();
// synchronize scrollbars (must happen after table was made visible)
ScrollBar mainTableHorizontalScrollBar = findScrollBar( mainTable, Orientation.HORIZONTAL);
ScrollBar sumTableHorizontalScrollBar = findScrollBar( sumTable, Orientation.HORIZONTAL);
mainTableHorizontalScrollBar.valueProperty().bindBidirectional( sumTableHorizontalScrollBar.valueProperty());
}
/**
* Primary table column mapping.
*/
private void setupMainTableColumns() {
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"));
value1Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
TableColumn<Data, Double> value2Col = new TableColumn<>("Value 2");
value2Col.setPrefWidth(90);
value2Col.setCellValueFactory(new PropertyValueFactory<>("value2"));
value2Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
TableColumn<Data, Double> value3Col = new TableColumn<>("Value 3");
value3Col.setPrefWidth(90);
value3Col.setCellValueFactory(new PropertyValueFactory<>("value3"));
value3Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
mainTable.getColumns().addAll( dateCol, value1Col, value2Col, value3Col);
}
/**
* Summary table column mapping.
*/
private void setupSumTableColumns() {
TableColumn<SumData, String> textCol = new TableColumn<>("Text");
textCol.setCellValueFactory(new PropertyValueFactory<>("text"));
TableColumn<SumData, Double> value1Col = new TableColumn<>("Value 1");
value1Col.setCellValueFactory(new PropertyValueFactory<>("value1"));
value1Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
TableColumn<SumData, Double> value2Col = new TableColumn<>("Value 2");
value2Col.setCellValueFactory(new PropertyValueFactory<>("value2"));
value2Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
TableColumn<SumData, Double> value3Col = new TableColumn<>("Value 3");
value3Col.setCellValueFactory(new PropertyValueFactory<>("value3"));
value3Col.setCellFactory( new FormattedTableCellFactory<>( TextAlignment.RIGHT));
sumTable.getColumns().addAll( textCol, 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);
}
}
/**
* Data for summary table rows.
*/
public static class SumData {
private final SimpleStringProperty text;
private final SimpleDoubleProperty value1;
private final SimpleDoubleProperty value2;
private final SimpleDoubleProperty value3;
public SumData( String text, double value1, double value2, double value3) {
this.text = new SimpleStringProperty( text);
this.value1 = new SimpleDoubleProperty( value1);
this.value2 = new SimpleDoubleProperty( value2);
this.value3 = new SimpleDoubleProperty( value3);
}
public final SimpleStringProperty textProperty() {
return this.text;
}
public final java.lang.String getText() {
return this.textProperty().get();
}
public final void setText(final java.lang.String text) {
this.textProperty().set(text);
}
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);
}
}
/**
* Formatter for table cells: allows you to align table cell values left/right/center
*
* Example for alignment form http://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm
*
* @param <S>
* @param <T>
*/
public static class FormattedTableCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private TextAlignment alignment = TextAlignment.LEFT;
private Format format;
public FormattedTableCellFactory() {
}
public FormattedTableCellFactory( TextAlignment alignment) {
this.alignment = alignment;
}
public TextAlignment getAlignment() {
return alignment;
}
public void setAlignment(TextAlignment alignment) {
this.alignment = alignment;
}
public Format getFormat() {
return format;
}
public void setFormat(Format format) {
this.format = format;
}
@Override
@SuppressWarnings("unchecked")
public TableCell<S, T> call(TableColumn<S, T> p) {
TableCell<S, T> cell = new TableCell<S, T>() {
@Override
public void updateItem(Object item, boolean empty) {
if (item == getItem()) {
return;
}
super.updateItem((T) item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (format != null) {
super.setText(format.format(item));
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node) item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
};
cell.setTextAlignment(alignment);
switch (alignment) {
case CENTER:
cell.setAlignment(Pos.CENTER);
break;
case RIGHT:
cell.setAlignment(Pos.CENTER_RIGHT);
break;
default:
cell.setAlignment(Pos.CENTER_LEFT);
break;
}
return cell;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment