We have an own control with an own skin class. Within the skin, several buttons are placed, which use an own button skin. The first of these button skins, which uses a graphics in addition to the button text, is instantiated two times:
MyButtonSkin initialized for 1. Button with no graphics
MyButtonSkin initialized for 2. Button with pane
MyButtonSkin initialized for 3. Button with label
MyButtonSkin initialized for 4. Button with pane
MyButtonSkin initialized for 5. Button with label
MyButtonSkin initialized for 2. Button with pane
Run the appliction (sources below) as a minimal example for that behaviour. In our projects, the duplicate intstantiation leads to many problems. For instance, if the graphics of such button itself is a label, a NullPointerExcpetion occurrs:
at com.sun.javafx.scene.control.skin.LabeledSkinBase.lambda$new$260(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase$$Lambda$134/1744049635.invalidated(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
at javafx.scene.Node$LazyBoundsProperty.invalidate(Unknown Source)
at javafx.scene.Node.impl_layoutBoundsChanged(Unknown Source)
at javafx.scene.layout.Region.widthChanged(Unknown Source)
at javafx.scene.layout.Region.access$1700(Unknown Source)
at javafx.scene.layout.Region$6.invalidated(Unknown Source)
at javafx.beans.property.DoublePropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.DoublePropertyBase.set(Unknown Source)
at javafx.scene.layout.Region.setWidth(Unknown Source)
at javafx.scene.layout.Region.resize(Unknown Source)
at javafx.scene.Node.autosize(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutLabelInArea(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutLabelInArea(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutChildren(Unknown Source)
at javafx.scene.control.Control.layoutChildren(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Group.prefWidth(Unknown Source)
at javafx.scene.Scene.getPreferredWidth(Unknown Source)
at javafx.scene.Scene.resizeRootToPreferredSize(Unknown Source)
at javafx.scene.Scene.preferredSize(Unknown Source)
at javafx.scene.Scene.impl_preferredSize(Unknown Source)
at javafx.stage.Window$9.invalidated(Unknown Source)
at javafx.beans.property.BooleanPropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.BooleanPropertyBase.set(Unknown Source)
at javafx.stage.Window.setShowing(Unknown Source)
at javafx.stage.Window.show(Unknown Source)
at javafx.stage.Stage.show(Unknown Source)
at buttonskin.TestApplication.start(TestApplication.java:21)
...
///////////////////// buttonskin/TestApplication.java:
package buttonskin;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestApplication extends Application {
public static void main(final String[] args) {
Application.launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
final Group root = new Group();
final Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
root.getChildren().add(new MyControl());
stage.setScene(scene);
stage.show();
}
}
///////////////////// buttonskin/MyControl.java:
package buttonskin;
import javafx.scene.control.Control;
public class MyControl extends Control {
public MyControl() {
super();
getStyleClass().add("my-control");
}
}
///////////////////// buttonskin/MyControlSkin.java:
package buttonskin;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Pane;
public class MyControlSkin extends SkinBase<MyControl> {
public MyControlSkin(final MyControl control) {
super(control);
// The 1. button with no graphics has no effect:
getChildren().add(new Button("1. Button with no graphics"));
// The 2. button with a pane will be initialized 2 times:
getChildren().add(new Button("2. Button with pane", new Pane()));
// In case the 2. button is commented out and not added,
// the 3. Button will be initialzed 2 times, leading to a NullPointerException
// at com.sun.javafx.scene.control.skin.LabeledSkinBase.lambda$new$260(Unknown Source)
getChildren().add(new Button("3. Button with label", new Label("Label")));
getChildren().add(new Button("4. Button with pane", new Pane()));
getChildren().add(new Button("5. Button with label", new Label("Label")));
}
}
///////////////////// buttonskin/MyButtonSkin.java:
package buttonskin;
import javafx.scene.control.Button;
import com.sun.javafx.scene.control.skin.ButtonSkin;
@SuppressWarnings("restriction")
public class MyButtonSkin extends ButtonSkin {
public MyButtonSkin(final Button button) {
super(button);
System.out.println("MyButtonSkin initialized for " + button.getText());
}
}
///////////////////// buttonskin/style.css:
.button {
-fx-skin: "buttonskin.MyButtonSkin";
}
.my-control {
-fx-skin: "buttonskin.MyControlSkin";
}
MyButtonSkin initialized for 1. Button with no graphics
MyButtonSkin initialized for 2. Button with pane
MyButtonSkin initialized for 3. Button with label
MyButtonSkin initialized for 4. Button with pane
MyButtonSkin initialized for 5. Button with label
MyButtonSkin initialized for 2. Button with pane
Run the appliction (sources below) as a minimal example for that behaviour. In our projects, the duplicate intstantiation leads to many problems. For instance, if the graphics of such button itself is a label, a NullPointerExcpetion occurrs:
at com.sun.javafx.scene.control.skin.LabeledSkinBase.lambda$new$260(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase$$Lambda$134/1744049635.invalidated(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(Unknown Source)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source)
at javafx.scene.Node$LazyBoundsProperty.invalidate(Unknown Source)
at javafx.scene.Node.impl_layoutBoundsChanged(Unknown Source)
at javafx.scene.layout.Region.widthChanged(Unknown Source)
at javafx.scene.layout.Region.access$1700(Unknown Source)
at javafx.scene.layout.Region$6.invalidated(Unknown Source)
at javafx.beans.property.DoublePropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.DoublePropertyBase.set(Unknown Source)
at javafx.scene.layout.Region.setWidth(Unknown Source)
at javafx.scene.layout.Region.resize(Unknown Source)
at javafx.scene.Node.autosize(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutLabelInArea(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutLabelInArea(Unknown Source)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.layoutChildren(Unknown Source)
at javafx.scene.control.Control.layoutChildren(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Parent.layout(Unknown Source)
at javafx.scene.Group.prefWidth(Unknown Source)
at javafx.scene.Scene.getPreferredWidth(Unknown Source)
at javafx.scene.Scene.resizeRootToPreferredSize(Unknown Source)
at javafx.scene.Scene.preferredSize(Unknown Source)
at javafx.scene.Scene.impl_preferredSize(Unknown Source)
at javafx.stage.Window$9.invalidated(Unknown Source)
at javafx.beans.property.BooleanPropertyBase.markInvalid(Unknown Source)
at javafx.beans.property.BooleanPropertyBase.set(Unknown Source)
at javafx.stage.Window.setShowing(Unknown Source)
at javafx.stage.Window.show(Unknown Source)
at javafx.stage.Stage.show(Unknown Source)
at buttonskin.TestApplication.start(TestApplication.java:21)
...
///////////////////// buttonskin/TestApplication.java:
package buttonskin;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestApplication extends Application {
public static void main(final String[] args) {
Application.launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
final Group root = new Group();
final Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
root.getChildren().add(new MyControl());
stage.setScene(scene);
stage.show();
}
}
///////////////////// buttonskin/MyControl.java:
package buttonskin;
import javafx.scene.control.Control;
public class MyControl extends Control {
public MyControl() {
super();
getStyleClass().add("my-control");
}
}
///////////////////// buttonskin/MyControlSkin.java:
package buttonskin;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Pane;
public class MyControlSkin extends SkinBase<MyControl> {
public MyControlSkin(final MyControl control) {
super(control);
// The 1. button with no graphics has no effect:
getChildren().add(new Button("1. Button with no graphics"));
// The 2. button with a pane will be initialized 2 times:
getChildren().add(new Button("2. Button with pane", new Pane()));
// In case the 2. button is commented out and not added,
// the 3. Button will be initialzed 2 times, leading to a NullPointerException
// at com.sun.javafx.scene.control.skin.LabeledSkinBase.lambda$new$260(Unknown Source)
getChildren().add(new Button("3. Button with label", new Label("Label")));
getChildren().add(new Button("4. Button with pane", new Pane()));
getChildren().add(new Button("5. Button with label", new Label("Label")));
}
}
///////////////////// buttonskin/MyButtonSkin.java:
package buttonskin;
import javafx.scene.control.Button;
import com.sun.javafx.scene.control.skin.ButtonSkin;
@SuppressWarnings("restriction")
public class MyButtonSkin extends ButtonSkin {
public MyButtonSkin(final Button button) {
super(button);
System.out.println("MyButtonSkin initialized for " + button.getText());
}
}
///////////////////// buttonskin/style.css:
.button {
-fx-skin: "buttonskin.MyButtonSkin";
}
.my-control {
-fx-skin: "buttonskin.MyControlSkin";
}