2
votes

Let's say I have following layout:

<VBox xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <TextField fx:id="textField" />
      <ListView fx:id="list" visible="true" />
   </children>
</VBox>

As soon as I call listView.setManaged(false) the ListView is invisible no matter which layout properties I set. Maybe I missed some.

Which properties do I have to set in order to see the ListView with these sample values:
Left = 100
Top = 200
Width = 300
Height = 400

1
But first, why do you want to set the listview as not managed? - Uluk Biy
This is part of a custom component and I don't want the ListView to take up space. It is shown as a kind of overlay serving as an autocomplete input. I know this can be achieved with other ways. Still I want to understand why I can't get this to work and I want to understand JavaFX better. - T3rm1

1 Answers

1
votes

Solution

Wrap the ListView in an unmanaged container. Any container will do, e.g., a Group or any Pane or Pane subclass. Do not set the ListView itself to be unmanaged.

Background

Unmanaged in this context, means an explicit call to setManaged(false) on the container node. This means that automatic layout is disabled for the node and the user application code must explicitly manage layout properties (such as node size and width and requesting automated layout of children of the unmanaged node). So the paradoxical thing is that your application code must explicitly manage the container layout.

If you make the ListView itself as unmanaged, my guess (and it is just a guess) is that this breaks the internal layout algorithm for the ListView and prevents the ListView being correctly displayed as the components inside the ListView are probably expecting the ListView itself to be managed.

When you put the ListView inside another container which is unmanaged, then the ListView itself is still managed so internally its layout does not break, but the layout is disjoint from the regular layout of the scene and the unmanaged container becomes the root for layout of the ListView. That way you can set the location and preferred sizing of the unmanaged container (and its children) manually, effectively achieving your goal of having application managed layout for the ListView.

Applicability

I noted the same behavior for other controls like TextField, so this is probably a general pattern that can be used for unmanaged display of any control.

Also note, that this request is somewhat unusual. For most applications, unmanaged controls are not required. The in-built layout managers can handle a large variety of layout needs. When custom layout is required, the method is to create a custom layout manager (e.g. a new Pane subclass) and override layoutChildren() and the computePrefHeight/computePrefWidth/computeMinHeight/computeMinWidth methods. This allows your application or component to interact in a friendly manner with the automatic JavaFX layout system. For examples of such functional style, review the code for in-built layout managers such as StackPane.

Sample

Here is a sample application to demonstrate these principles and achieve the sizing and placement goals stated for the ListView in your question. I sized the screenshot to half size so it doesn't take up too much space. The measurements are a bit deceptive as visually to me it doesn't look like the list is placed at 100, 200 with size 300, 400 but I measured it in a graphics app and it is placed and sized as expected.

unmanaged

import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Unmanageable extends Application {
    @Override
    public void start(final Stage stage) throws Exception {
        TextField textField = new TextField();

        ListView<String> listView = new ListView<>();
        listView.getItems().addAll("Sally", "Sells", "Seashells", "At", "The", "Seashore");
        listView.setStyle("-fx-base: lightcoral;");

        Group unmanagedContainer = new Group(listView);
        unmanagedContainer.setManaged(false);
        unmanagedContainer.relocate(100, 200);

        listView.setMinSize(ListView.USE_PREF_SIZE, ListView.USE_PREF_SIZE);
        listView.setPrefSize(300, 400);
        listView.setMaxSize(ListView.USE_PREF_SIZE, ListView.USE_PREF_SIZE);

        VBox layout = new VBox(textField, unmanagedContainer);
        layout.setMinSize(600, 800);
        layout.setStyle("-fx-background-color: azure;");

        stage.setScene(new Scene(layout));
        stage.show();

        System.out.println(unmanagedContainer.getBoundsInParent());
    }

    public static void main(String[] args) throws Exception {
        launch(args);
    }
}

Output:

BoundingBox [minX:100.0, minY:200.0, minZ:0.0, width:300.0, height:400.0, depth:0.0, maxX:400.0, maxY:600.0, maxZ:0.0]