JavaFX TitledPane lookup (.title) returns null

I am new to Java FX and am building an application for fun. I am trying to add TitledPane

dynamically and get Null Pointer Exceptions when trying to find the title TitledPane

about 70% of the time. I tried to create a simple demo of my problem, but was unable to reproduce the problem outside of my application, but I could solve my problem. I hope someone can help me understand why my solution works and maybe point me towards a better solution. I am using an FXML file with a controller. I am trying to find the title inside Platform.runLater () because I am manually editing the layout and title elements. Inside the controller initialization 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 move this call to a timer and execute it in 500ms, it never seems to 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);

      

It was mentioned in one of the forums that the CSS had to be applied to the element before calling the title search, so I tried to manually apply the CSS to the title but titledpane.lookup(".title")

still returned null. Can anyone help me understand what's going on here? Thanks in advance!

+3


source to share


2 answers


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

Here is an example of how you can dynamically load a TitledPane. The TitledPane is added every 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 for creating a custom header. Here's one. Note. You must specify an icon on the corresponding path or change the path. Alternatively, you can just turn off the ImageView node and use Rectangle instead for demo. It's just another node that's showing up.

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

0


source


I had the same problem. I resolved it by calling applyCss()

and layout()

on the panel containing TitledPane

:



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

      

0


source







All Articles