Issue
I have a TableView
in the center of a BorderPane
with different prefWidth values for the columns. Inside SceneBuilder the columns are resized correctly according to the prefWidth value, but when I run the program the columns all have the same width (75.0). This is the .fxml file:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="1500.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ReportController">
<center>
<TableView fx:id="reportTableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="date" prefWidth="60.0" text="Date" />
<TableColumn fx:id="company" prefWidth="75.0" text="Company" />
<TableColumn fx:id="number" prefWidth="40.0" text="Number" />
...
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</center>
<bottom>
<TitledPane text="Summary" BorderPane.alignment="CENTER">
<content>
<HBox alignment="CENTER">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Details:">
<font>
<Font size="20.0" />
</font>
</Text>
</children>
</HBox>
</content>
</TitledPane>
</bottom>
</BorderPane>
And this is the code that loads the frame:
try {
FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("report.fxml"));
Parent parent = loader.load();
Stage stage = new Stage(StageStyle.DECORATED);
stage.setTitle("Tax Sheet Report");
stage.getIcons().add(new Image("icons/icon.png"));
stage.setScene(new Scene(parent));
stage.setMaximized( true );
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
Solution
It is because you included the CONSTRAINED_RESIZE_POLICY to the table view.
The java doc says::
Simple policy that ensures the width of all visible leaf columns in this table sum up to equal the width of the table itself.
When the user resizes a column width with this policy, the table automatically adjusts the width of the right hand side columns. When the user increases a column width, the table decreases the width of the rightmost column until it reaches its minimum width. Then it decreases the width of the second rightmost column until it reaches minimum width and so on. When all right hand side columns reach minimum size, the user cannot increase the size of resized column any more.
Having said that, if you included the CONSTRAINED_RESIZE_POLICY on purpose, then you may need to add a bit more custom logic to satisfy the custom widths along with the policy.
UPDATE:
If you want to acheive a feature something like "percentWidth" feature in GridPane, you can try the below approach.
The idea is :
- Create a custom TableColumn that has a new property "percentWidth"
- Create a custom TableView that has a listener to its widthProperty and adjust the columns prefWidth based on the percentWidth.
- Import these controls in fxml and update the fxml with the new controls.
CustomTableColumn.java
public class CustomTableColumn<S, T> extends TableColumn<S, T> {
private DoubleProperty percentWidth = new SimpleDoubleProperty();
public CustomTableColumn(String columnName) {
super(columnName);
}
public DoubleProperty percentWidth() {
return percentWidth;
}
public double getPercentWidth() {
return percentWidth.get();
}
public void setPercentWidth(double percentWidth) {
this.percentWidth.set(percentWidth);
}
}
CustomTableView.java
public class CustomTableView<S> extends TableView<S> {
public CustomTableView() {
widthProperty().addListener((obs, old, tableWidth) -> {
// Deduct 2px from the total table width for borders. Otherwise you will see a horizontal scroll bar.
double width = tableWidth.doubleValue() - 2;
getColumns().stream().filter(col -> col instanceof CustomTableColumn)
.map(col -> (CustomTableColumn) col)
.forEach(col -> col.setPrefWidth(width * (col.getPercentWidth() / 100)));
});
}
}
Updated fxml code:
<CustomTableView fx:id="reportTableView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<columns>
<CustomTableColumn fx:id="date" percentWidth="35" text="Date" />
<CustomTableColumn fx:id="company" percentWidth="40" text="Company" />
<CustomTableColumn fx:id="number" percentWidth="25" text="Number" />
...
</columns>
</CustomTableView>
Please note that sum of all columns percentWidth should be equal to 100 for better results :)
A full working demo of this implementation(non fxml) is below: (I updated the code to fix the horizontal scroll bar in the gif)
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PercentageTableColumnDemo extends Application {
@Override
public void start(Stage stage) throws Exception {
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.add(new Person("Harry", "John", "LS"));
persons.add(new Person("Mary", "King", "MS"));
CustomTableColumn<Person, String> fnCol = new CustomTableColumn<>("First Name");
fnCol.setPercentWidth(30);
fnCol.setCellValueFactory(param -> param.getValue().firstNameProperty());
CustomTableColumn<Person, String> lnCol = new CustomTableColumn<>("Last Name");
lnCol.setPercentWidth(25);
lnCol.setCellValueFactory(param -> param.getValue().lastNameProperty());
CustomTableColumn<Person, String> cityCol = new CustomTableColumn<>("City");
cityCol.setPercentWidth(45);
cityCol.setCellValueFactory(param -> param.getValue().cityProperty());
CustomTableView<Person> tableView = new CustomTableView<>();
tableView.getColumns().addAll(fnCol, lnCol, cityCol);
tableView.setItems(persons);
VBox root = new VBox();
root.getChildren().addAll(tableView);
VBox.setVgrow(tableView, Priority.ALWAYS);
Scene scene = new Scene(root, 500, 500);
stage.setScene(scene);
stage.setTitle("Table demo");
stage.show();
}
class CustomTableColumn<S, T> extends TableColumn<S, T> {
private DoubleProperty percentWidth = new SimpleDoubleProperty();
public CustomTableColumn(String columnName) {
super(columnName);
}
public DoubleProperty percentWidth() {
return percentWidth;
}
public double getPercentWidth() {
return percentWidth.get();
}
public void setPercentWidth(double percentWidth) {
this.percentWidth.set(percentWidth);
}
}
class CustomTableView<S> extends TableView<S> {
public CustomTableView() {
widthProperty().addListener((obs, old, tableWidth) -> {
// Deduct 2px from the total table width for borders. Otherwise you will see a horizontal scroll bar.
double width = tableWidth.doubleValue() - 2;
getColumns().stream().filter(col -> col instanceof CustomTableColumn)
.map(col -> (CustomTableColumn) col)
.forEach(col -> col.setPrefWidth(width * (col.getPercentWidth() / 100)));
});
}
}
class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private StringProperty city = new SimpleStringProperty();
public Person(String fn, String ln, String cty) {
setFirstName(fn);
setLastName(ln);
setCity(cty);
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getCity() {
return city.get();
}
public StringProperty cityProperty() {
return city;
}
public void setCity(String city) {
this.city.set(city);
}
}
}
Answered By - Sai Dandem
Answer Checked By - Robin (JavaFixing Admin)