Mr. ATC
Mr. ATC

Reputation: 25

JavaFX table column won't resize to prefWidth

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

Upvotes: 1

Views: 947

Answers (2)

Sai Dandem
Sai Dandem

Reputation: 9949

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)

enter image description here

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

Upvotes: 3

Iurii Volobuev
Iurii Volobuev

Reputation: 41

Use UNCONSTRAINED_RESIZE_POLICY instead of CONSTRAINED_RESIZE_POLICY as a column policy of TableView.

Upvotes: 2

Related Questions