The moment I started working with TableView in JavaFX 2.x, the first question raised in my mind is “Why there is no feature of setting the column widths in percentage as we do in HTML?” I am not sure what could be the reasons for not setting this feature. But if my application demands, I have to implement this by somehow ;). Ofcourse there are a couple of issues logged in JIRA related to this functionality, but till it get resolved here is the way (workaround) how I tried to get it.
And another thing is with the display of “Extra Column” in TableView. I still remember, in the discussion forum, one guy has referred this term as “Ghost Column” :). Below is the screen shot of a simple TableView, with some setting of prefWidth (here 150px) to TableColumns and resize the screen.
Ok, let me get into some action... The target is, on resizing the window the table columns should keep their relative sizes with respect to container/window.
For achieving this I am taking the help of GridPane for which percentage widths can be set to its columns through ColumnConstraints.
As a first step, a CustomTableColumn class is created that extends TableColumn, to hold the custom percentWidth property for the column.
The code for the CustomTableColumn is as below:
/**
* CustomTableColumn to hold the custom percentWidth property.
*/
public class CustomTableColumn<s> extends TableColumn<s>{ private SimpleDoubleProperty percentWidth = new SimpleDoubleProperty(); public CustomTableColumn(String columnName){ super(columnName); } public SimpleDoubleProperty percentWidth() { return percentWidth; } public double getPercentWidth() { return percentWidth.get(); } public void setPercentWidth(double percentWidth) { this.percentWidth.set(percentWidth); } } </s></s>
The idea is that, we will create a container, probably a StackPane (CustomTableView) which holds a GridPane and a TableView. The number of columns in the GridPane will be synchronized with the number of columns in the TableView.
StackPane is used as the content in each column of the grid. (StackPane because, it fits automatically to its parent size)
Now the core logic is,
- Step 1: We will be creating ColumnConstraints with the percentWidth that is specified for each column(CustomTableColumn) of the TableView. And set these ColumnConstraints to the grid.
- Step 2: The next step is binding each StackPane’s widthProperty to its corresponding table column’s prefWidthProperty.
Considering the above logic, the CustomTableView code is as below:
/**
* CustomTableView to hold the table and grid.
*/
public class CustomTableView<s> extends StackPane{ private TableView<s> table; @SuppressWarnings("rawtypes") public CustomTableView(){ this.table = new TableView<s>(); final GridPane grid = new GridPane(); this.table.getColumns().addListener(new ListChangeListener(){ @Override public void onChanged(javafx.collections.ListChangeListener.Change arg0) { grid.getColumnConstraints().clear(); ColumnConstraints[] arr1 = new ColumnConstraints[CustomTableView.this.table.getColumns().size()]; StackPane[] arr2 = new StackPane[CustomTableView.this.table.getColumns().size()]; int i=0; for(TableColumn column : CustomTableView.this.table.getColumns()){ CustomTableColumn col = (CustomTableColumn)column; ColumnConstraints consta = new ColumnConstraints(); consta.setPercentWidth(col.getPercentWidth()); StackPane sp = new StackPane(); if(i==0){ // Quick fix for not showing the horizantal scroll bar. NumberBinding diff = sp.widthProperty().subtract(3.75); column.prefWidthProperty().bind(diff); }else{ column.prefWidthProperty().bind(sp.widthProperty()); } arr1[i] = consta; arr2[i] = sp; i++; } grid.getColumnConstraints().addAll(arr1); grid.addRow(0, arr2); } }); getChildren().addAll(grid,table); } public TableView<s> getTableView(){ return this.table; } } </s></s></s></s>
Combining all the above code, the below is the final SSCCE(Short, Self Contained, Correct Example) code:
package com.ezest.javafx.demogallery.tableviews;
import javafx.application.Application;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TableViewAutoSizeDemo extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
root.autosize();
Scene scene = new Scene(root);
stage.setTitle("TableView Auto Size Demo");
stage.setWidth(700);
stage.setHeight(400);
stage.setScene(scene);
stage.show();
configureTable(root);
}
@SuppressWarnings("unchecked")
private void configureTable(StackPane root) {
final ObservableList data = FXCollections.observableArrayList(
new MyDomain("Apple","This is a fruit.","Red"),
new MyDomain("Orange","This is also a fruit.","Orange"),
new MyDomain("Potato","This is a vegetable.","Brown")
);
CustomTableView table = new CustomTableView();
CustomTableColumn titleColumn = new CustomTableColumn("Title");
titleColumn.setPercentWidth(25);
titleColumn.setCellValueFactory(new PropertyValueFactory("name"));
CustomTableColumn descCol = new CustomTableColumn("Description");
descCol.setPercentWidth(55);
descCol.setCellValueFactory(new PropertyValueFactory("description"));
CustomTableColumn colorCol = new CustomTableColumn("Color");
colorCol.setPercentWidth(20);
colorCol.setCellValueFactory(new PropertyValueFactory("color"));
table.getTableView().getColumns().addAll(titleColumn,descCol,colorCol);
table.getTableView().setItems(data);
root.getChildren().add(table);
}
/**
* CustomTableView to hold the table and grid.
*/
public class CustomTableView<s> extends StackPane{ private TableView<s> table; @SuppressWarnings("rawtypes") public CustomTableView(){ this.table = new TableView<s>(); final GridPane grid = new GridPane(); this.table.getColumns().addListener(new ListChangeListener<TableColumn<s>>(){ public void onChanged(javafx.collections.ListChangeListener.Change arg0) { grid.getColumnConstraints().clear(); ColumnConstraints[] arr1 = new ColumnConstraints[CustomTableView.this.table.getColumns().size()]; StackPane[] arr2 = new StackPane[CustomTableView.this.table.getColumns().size()]; int i=0; for(TableColumn column : CustomTableView.this.table.getColumns()){ CustomTableColumn col = (CustomTableColumn)column; ColumnConstraints consta = new ColumnConstraints(); consta.setPercentWidth(col.getPercentWidth()); StackPane sp = new StackPane(); if(i==0){ // Quick fix for not showing the horizantal scroll bar. NumberBinding diff = sp.widthProperty().subtract(3.75); column.prefWidthProperty().bind(diff); }else{ column.prefWidthProperty().bind(sp.widthProperty()); } arr1[i] = consta; arr2[i] = sp; i++; } grid.getColumnConstraints().addAll(arr1); grid.addRow(0, arr2); } }); getChildren().addAll(grid,table); } public TableView<s> getTableView(){ return this.table; } } /** * CustomTableColumn to hold the custom percentWidth property. */ public class CustomTableColumn<s> extends TableColumn<s>{ private SimpleDoubleProperty percentWidth = new SimpleDoubleProperty(); public CustomTableColumn(String columnName){ super(columnName); } public SimpleDoubleProperty percentWidth() { return percentWidth; } public double getPercentWidth() { return percentWidth.get(); } public void setPercentWidth(double percentWidth) { this.percentWidth.set(percentWidth); } } /** * Domain Object. */ public class MyDomain{ private SimpleStringProperty name = new SimpleStringProperty(); private SimpleStringProperty description = new SimpleStringProperty(); private SimpleStringProperty color = new SimpleStringProperty(); public MyDomain(String name, String desc,String color){ this.name.set(name); this.description.set(desc); this.color.set(color); } public String getDescription() { return description.get(); } public SimpleStringProperty descriptionProperty(){ return description; } public String getName() { return name.get(); } public SimpleStringProperty nameProperty(){ return name; } public String getColor() { return color.get(); } public SimpleStringProperty colorProperty(){ return color; } } } </s></s></s></s></s></s></s>
If you find any compilation issues in the above code( due to formating), the above code can be found here.
On resizing the window, the output is as below. Not only the columns have maintained their relative sizes , but the extra column is no more visible. :)
I hope the above logic will serve the need till the auto sizing feature of TableView is implemented and released.
Happy Coding!! :)
Note: The above logic/code may be or can be improvised. Let me also know the changes for better implementation. :)