user1725940
user1725940

Reputation: 93

JavaFX TitledPane lookup(.title) returns null

I'm new to Java FX and am creating an application for fun. I'm trying to add a TitledPane dynamically and am getting Null Pointer Exceptions when attempting to lookup the title of the TitledPane about 70% of the time. I tried to create a simple demo of my issue, but was unable to reproduce the issue outside of my application, but I could solve my issue. I'm hoping someone could help me understand why my solution works and maybe point me in the direction of a better solution. I'm using an FXML file with a Controller. I'm attempting to lookup the title inside of Platform.runLater() because I'm manually editing the layout and elements of the title. Inside of the Controller's initialize function, I do the following to get null pointer exceptions:

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        titledpane.lookup(".title"); // will return null about 70% of the time
    }
});
// Do more stuff

However, if I wrap that call in a timer and execute it in 500 ms, it doesn't seem to ever return Null.

new java.util.Timer().schedule(new java.util.TimerTask() {
    @Override
    public void run() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                titledpane.lookup(".title"); // doesn't seem to return null
            }
        });
    }
}, 500);

One forum mentioned that CSS had to be applied to the element prior to calling a lookup on the title, so I tried manually applying CSS to the title, but titledpane.lookup(".title") still returned null. Can anyone help me understand what is happening here? Thanks in advance!

Upvotes: 4

Views: 1976

Answers (2)

Eng.Fouad
Eng.Fouad

Reputation: 117587

I had the same issue. I resolved it by calling applyCss() and layout() on the pane that contains the TitledPane:

Node loadedPane = paneLoader.load();
bodyPane.setCenter(loadedPane);
bodyPane.applyCss();
bodyPane.layout();

Upvotes: 2

Roland
Roland

Reputation: 18415

You should read the article Creating a Custom Control with FXML.

Here's an example about how you can load a TitledPane dynamically. A TitledPane is added each time you click the "Add Task" button.

Task.fxml

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

<?import javafx.scene.text.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<fx:root collapsible="false" prefHeight="72.0" prefWidth="202.0" text="Task" type="TitledPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <content>
      <Pane prefHeight="43.0" prefWidth="200.0">
         <children>
         </children>
      </Pane>
   </content>
</fx:root>

Task.java

import java.io.IOException;

import javafx.fxml.FXMLLoader;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.Region;


public class Task extends Region {

    TitledPane titledPane;

    public Task( String title) {

        final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));

        titledPane = new TitledPane();

        fxmlLoader.setRoot( titledPane);
        fxmlLoader.setController( this);

        try {
          fxmlLoader.load();
        } catch( IOException exception) {
          throw new RuntimeException( exception);
        }

        titledPane.setText(title);

        getChildren().add( titledPane);

    }


}

Demo.java

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class Demo extends Application {

    @Override
    public void start(Stage primaryStage) {

        Group root = new Group();

        Button addTaskButton = new Button( "Add Task");

        addTaskButton.setOnAction(new EventHandler<ActionEvent>() {

            double x=0;
            double y=0;
            int count=0;

            @Override
            public void handle(ActionEvent event) {

                // calculate new position
                x+=50;
                y+=50;

                // task counter
                count++;

                Task task = new Task( "Task " + count);
                task.relocate(x, y);
                root.getChildren().add( task);

            }
        });
        root.getChildren().add( addTaskButton);


        Scene scene = new Scene( root, 1024, 768);

        primaryStage.setScene( scene);
        primaryStage.show();

    }

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

}

Screenshot:

enter image description here

There are various solutions in order to create a custom title. Here's one. Note: You need to provide an icon in the proper path or adapt the path. Alternatively you can just disable the ImageView node and instead use the Rectangle for demonstration purposes. It's just another node that's displayed.

public class Task extends Region {

    TitledPane titledPane;

    public Task( String title) {

        final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));

        titledPane = new TitledPane();

        fxmlLoader.setRoot( titledPane);
        fxmlLoader.setController( this);

        try {
          fxmlLoader.load();
        } catch( IOException exception) {
          throw new RuntimeException( exception);
        }


        // create custom title with text left and icon right
        AnchorPane anchorpane = new AnchorPane();
        double offsetRight = 20; // just an arbitrary value. usually the offset from the left of the title
        anchorpane.prefWidthProperty().bind(titledPane.widthProperty().subtract( offsetRight));

        // create text for title
        Label label = new Label( title);

        // create icon for title
        Image image = new Image( getClass().getResource( "title.png").toExternalForm());
        ImageView icon = new ImageView();
        icon.setImage(image);

//      Rectangle icon = new Rectangle(16, 16);

        // set text and icon positions
        AnchorPane.setLeftAnchor(label, 0.0);
        AnchorPane.setRightAnchor(icon, 0.0);

        // add text and icon to custom title
        anchorpane.getChildren().addAll( label, icon);

        // set custom title
        titledPane.setGraphic( anchorpane);

        // show only our custom title, don't show the text of titlePane
        titledPane.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        getChildren().add( titledPane);

    }

}

enter image description here

Upvotes: 0

Related Questions