Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Control ClassNotFoundException in Scene Builder

I have created a new control by extending an existing one, and I would like to use this new control in my JavaFX scenes. I would like to be able to edit my scenes using Scene Builder, but after adding the new control to the FXML file, I am encountering a ClassNotFoundException when opening Scene Builder.

For example, here is a class I made which extends TextField:

RegexLimitingTextField.java

public class RegexLimitingTextField extends TextField {

    private String regexLimiter = ".*";

    public void setRegexLimiter(String regex) {
        this.regexLimiter = regex;
    }

    @Override
    public void replaceText(int start, int end, String text) {
        if (text.matches(regexLimiter))
            super.replaceText(start, end, text);
    }

    @Override
    public void replaceSelection(String replacement) {
        if (replacement.matches(regexLimiter))
            super.replaceSelection(replacement);
    }
}

After adding this control to my FXML file...

sample.fxml

<?import javafx.scene.layout.GridPane?>
<?import sample.RegexLimitingTextField?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
    <RegexLimitingTextField fx:id="textField" text="Test" />
</GridPane>

... I get this error when loading Scene Builder 2.0:

Caused by: java.lang.ClassNotFoundException: sample.RegexLimitingTextField
    at java.lang.ClassLoader.findClass(ClassLoader.java:530)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at javafx.fxml.FXMLLoader.loadTypeForPackage(FXMLLoader.java:2920)
    at javafx.fxml.FXMLLoader.loadType(FXMLLoader.java:2909)
    at javafx.fxml.FXMLLoader.importClass(FXMLLoader.java:2850)
    ... 23 more

Why can't Scene Builder find my new control? What do I need to do in order for it to find and be able to use my new control?

Here are the other files if needed:

Main.java

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setScene(new Scene(root, 200, 200));
        primaryStage.show();
    }
}

Controller.java

public class Controller implements Initializable {
    public RegexLimitingTextField textField;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        textField.setRegexLimiter("\\w*");
    }
}
like image 468
Mitch Talmadge Avatar asked Dec 25 '15 12:12

Mitch Talmadge


3 Answers

If anyone is still having troubles with SceneBuilder not loading their custom components, simply passing on a ClassLoader solved the issue for me.

try {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("CustomComponent.fxml"));
    loader.setRoot(this);
    loader.setController(this);
    loader.setClassLoader(getClass().getClassLoader());
    loader.load();
} catch (IOException e ){
    throw new RuntimeException(e);
}

Credit goes to the miracle worker, thatjavaguy.

like image 106
Brad Turek Avatar answered Nov 15 '22 00:11

Brad Turek


So ... after a lot of struggle I think I found the fix for this. The problem is so stupid that you won't believe.

This solution is only for thowse who allready tried everything, and did everything correctly, like:

  • passing on a ClassLoader when you create the controller for your custom component as Brad Turek said.
  • use fx:root construct for your .fxml

So, it seems that you get ClassNotFoundException in Scene Builder also, if the custom component (.fxml and it's java controller) are in a package which is named starting with uppercase letter. It seems that all package names down to your custom component MUST start with lowercase letter. To be sure...just use lowercase to name all the packages. Another thing I noticed is that also the class name of the controller, MUST start with uppercase letter.

like image 39
Florin Virtej Avatar answered Nov 15 '22 00:11

Florin Virtej


I also have had a lot of fun with a custom component extending TextField and causing Scene Builder to freeze when opening.

The jar file I added to Scene Builder only contained this one class. But my custom component has a reference to the controller — and that's the point. When I export both classes to the jar, then everything is fine.

So if you are in a similar situation and looking for an answer, look closely at the dependencies. The world without Scene Builder is not the same.

like image 32
Martin Avatar answered Nov 15 '22 00:11

Martin