JavaFX tableview resize to fit window

bzanchi picture bzanchi · May 15, 2014 · Viewed 40.7k times · Source

I am just trying out JavaFX and am forcing my way into it because it is suppose to be the future. I am trying to create a table with 4 columns. The columns AND THE TABLE should resize to fill the parent pane. I cannot for the life of me get this to work. I have been trying for over 4 hours now and the tableview does not resize.

Here is my FXML file.

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.collections.*?>
<?import t.cubed.fxml.*?>


<BorderPane styleClass="root" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="t.cubed.fxml.FXMLDocumentController">
    <top>
        <MenuBar fx:id="menuBar" styleClass="menu-bar">
        <menus>
            <Menu text="File">
                <items>
                    <MenuItem onAction="#handleOpenAction" text="Open" />
                    <MenuItem onAction="#handleExitAction" text="Exit" />
                </items>
            </Menu>
            <Menu text="Edit">
                <items>
                    <MenuItem onAction="#handleWeightAction" text="Edit Weights" />  
                    <MenuItem onAction="#handleFilterAction" text="Edit Filters" />
                    <MenuItem onAction="#handleOptionsAction" text="Options" />
                </items>
            </Menu>
        </menus>
    </MenuBar>
    </top>
    <center>
        <GridPane>
               <!--
               <padding>
                   <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/> 
               </padding>
               -->
               <TableView fx:id="testTable" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="1">
                   <columnResizePolicy>
                       <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
                   </columnResizePolicy>
                   <columns>
                       <TableColumn text="TEST NUMBER">
                       <cellValueFactory>
                           <PropertyValueFactory property="testNumber" />
                       </cellValueFactory>
                       </TableColumn>
                       <TableColumn text="TEST NAME">
                           <cellValueFactory>
                           <PropertyValueFactory property="testName" />
                       </cellValueFactory>
                       </TableColumn>
                       <TableColumn text="TEST TIME(ms)">
                           <cellValueFactory>
                           <PropertyValueFactory property="testTime" />
                       </cellValueFactory>
                       </TableColumn>
                       <TableColumn text="BEST MATCH">
                           <cellValueFactory>
                           <PropertyValueFactory property="bestMatch" />
                       </cellValueFactory>
                       </TableColumn>
                   </columns>
                   <items>
                    <!--
                    <FXCollections fx:factory="observableArrayList">
                        <TestTableModel testNumber="100" testName="Test1234" testTime="0.34" bestMatch="99"/>
                    </FXCollections>
                    -->
                </items>
               </TableView>
        </GridPane>
    </center>
<stylesheets>
    <URL value="@t-cubed.css" />
</stylesheets>
</BorderPane>

Answer

jewelsea picture jewelsea · May 15, 2014

How Resizable Layout Works

The size of resizable items in JavaFX is managed by the layout managers in which the items are placed.

What you need to do

For your particular issue, you need to set the GridPane sizing constraints to manage the TableView that you placed in the grid.

See the GridPane.hgrow and GridPane.vgrow constraints I added on the TableView:

<TableView fx:id="testTable" 
           GridPane.columnIndex="0" 
           GridPane.columnSpan="1" 
           GridPane.hgrow="ALWAYS" 
           GridPane.vgrow="ALWAYS"
           GridPane.rowIndex="0">

FXML just reflects the Java API, so you could also do the same in Java source as well; i.e. GridPane.setHGrow(node, priority). Though, if you are using FXML for your layout, defining the layout constraints in FXML is recommended.

grid

Refactored Sample

I loaded your FXML up in Scene Builder 2 and set the appropriate constraints and it appears to resize automatically fine when I use the preview functionality of Scene Builder.

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.collections.*?>
<?import t.cubed.fxml.*?>

<BorderPane styleClass="root" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="t.cubed.fxml.FXMLDocumentController">
  <top>
    <MenuBar fx:id="menuBar" styleClass="menu-bar">
      <menus>
        <Menu text="File">
          <items>
            <MenuItem onAction="#handleOpenAction" text="Open" />
            <MenuItem onAction="#handleExitAction" text="Exit" />
          </items>
        </Menu>
        <Menu text="Edit">
          <items>
            <MenuItem onAction="#handleWeightAction" text="Edit Weights" />
            <MenuItem onAction="#handleFilterAction" text="Edit Filters" />
            <MenuItem onAction="#handleOptionsAction" text="Options" />
          </items>
        </Menu>
      </menus>
    </MenuBar>
  </top>
  <center>
    <GridPane>
       <children>
         <!--
         <padding>
             <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
         </padding>
         -->
         <TableView fx:id="testTable" GridPane.columnIndex="0" GridPane.columnSpan="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="0" GridPane.vgrow="ALWAYS">
           <columnResizePolicy>
             <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
           </columnResizePolicy>
           <columns>
             <TableColumn text="TEST NUMBER">
               <cellValueFactory>
                 <PropertyValueFactory property="testNumber" />
               </cellValueFactory>
             </TableColumn>
             <TableColumn text="TEST NAME">
               <cellValueFactory>
                 <PropertyValueFactory property="testName" />
               </cellValueFactory>
             </TableColumn>
             <TableColumn text="TEST TIME(ms)">
               <cellValueFactory>
                 <PropertyValueFactory property="testTime" />
               </cellValueFactory>
             </TableColumn>
             <TableColumn text="BEST MATCH">
               <cellValueFactory>
                 <PropertyValueFactory property="bestMatch" />
               </cellValueFactory>
             </TableColumn>
           </columns>
           <items>
          <!--
          <FXCollections fx:factory="observableArrayList">
              <TestTableModel testNumber="100" testName="Test1234" testTime="0.34" bestMatch="99"/>
          </FXCollections>
          -->
        </items>
         </TableView>
       </children>
         <columnConstraints>
            <ColumnConstraints />
         </columnConstraints>
         <rowConstraints>
            <RowConstraints />
         </rowConstraints>
    </GridPane>
  </center>
  <stylesheets>
    <URL value="@t-cubed.css" />
  </stylesheets>
</BorderPane>

Some Advice

You are placing the TableView inside a GridPane. Using a GridPane in your case is a little strange as the GridPane you only place one node in the GridPane. Just placing the TableView directly in the center of your BorderPane or using a simpler layout parent such as a StackPane would have worked just fine. But perhaps this is just some cut down version of a more complex UI, so perhaps that is why you have used a GridPane.

Further Reading

If you have time, read up on layout in JavaFX in the Node, Pane, Group and GridPane javadoc and try this little layout bounds demo too.

Additional Questions for Comments

mention a StackPane would work, but StackPanes don't seem to have Hgrow and Vgrow?

StackPanes don't need a Hgrow or Vgrow constraints. Those constraints set Priorities, which are defined as:

Enumeration used to determine the grow (or shrink) priority of a given node's layout area when its region has more (or less) space available and multiple nodes are competing for that space.

With a StackPane, all children are layered on top of each other, they don't compete for space, so such growth priority settings for child nodes are not applicable.