Custom Control ClassNotFoundException in Scene Builder
Asked Answered
R

3

7

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*");
    }
}
Rhonda answered 25/12, 2015 at 12:55 Comment(2)
Probably a duplicate of #30064292Linzy
@Linzy That worked, thank you. I'm a bit annoyed though. Why would they remove the scenebuilder-classpath-element tag? This seems to mean that every single time I make a change to my class, I have to re-build the jar and re-import it. Is there no easier way to do this?Rhonda
T
7

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.

Taxpayer answered 27/4, 2017 at 21:5 Comment(1)
I'm sorry to hear that, @SerhiiDikobrazko. At least 5 other people seem to disagree.Taxpayer
D
6

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.

Daviddavida answered 20/7, 2017 at 19:16 Comment(0)
S
1

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.

Stanleystanly answered 3/12, 2019 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.