JavaFX Preloader issue - java

Hello dear Stackoverflow,
I have several questions regarding the JavaFX class Preloader.
After a long-term research, I haven't found any proper solution to my problem and I think it's not really the issue with my code, but rather the limits of JavaFX itself when it comes to the Preloader class.
Let me get straight up to my point: Is it not possible to use FXML to define the design of a Preloader? It's interesting to see how all the tutorials only guide you throughout creating a preloader by creating new Instances out of the classes. (ProgressBar progressBar = new ProgressBar(); ie)
So, let's take this example code for you to test this with me:
TestPreloader:
import com.sun.javafx.application.LauncherImpl;
import javafx.application.Preloader;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TestPreloader extends Preloader {
#FXML
ProgressBar progressBar;
public static void main(String[] args) {
LauncherImpl.launchApplication(TestApplication.class, TestPreloader.class, args);
}
public void initialize() {
System.out.println("initialize printed");
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.initStyle(StageStyle.TRANSPARENT);
Parent root = FXMLLoader.load(getClass().getResource("preloader.fxml"));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.centerOnScreen();
primaryStage.show();
}
#Override
public void handleProgressNotification(ProgressNotification progressNotification) {
// progressBar.setProgress(progressNotification.getProgress());
}
#Override
public void handleStateChangeNotification(StateChangeNotification notification) {
System.out.println(notification.getType().toString());
switch (notification.getType()) {
case BEFORE_START:
//progressBar.setProgress(1);
break;
case BEFORE_LOAD: //this is where the TestApplication class will be constructed
//progressBar.setProgress(1);
break;
case BEFORE_INIT:
//progressBar.setProgress(1);
break;
}
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestPreloader">
<children>
<ProgressBar fx:id="progressBar" layoutX="174.0" layoutY="141.0" prefWidth="200.0" progress="0.0" />
</children>
</AnchorPane>
TestApplication
import javafx.application.Application;
import javafx.stage.Stage;
public class TestApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
System.out.println("Application started!");
}
}
The output if you start is in the following order:
initialize printed
BEFORE_LOAD
BEFORE_INIT
BEFORE_START
Application started!
now, as you can see the initialize() method is being called firsthand, then the state of the preloader changes and thus upon every change the notification pops up - all good until now.
Yet what bothers me is, if I call progressBar#setProgress IN the initialize method, the progress actually gets set to 1 and no errors are being thrown. Although the States occur AFTER the Preloader is initialized, NullPointers occur.
To understand what I mean, uncomment setProgress under handleStateNotification (under the BEFORE_LOAD statement would make the most sense to me), and you'll see that a NullPointerException pops up.
Now, why does it occur? What can I do to fix this issue? It just doesn't make any sense for me. If the Preloader is initialized and the ProgresssBar is not null, how can it turn null afterwards? Perhaps maybe there is a new instance of the own class being used separately? But that wouldn't make sense as well to me. Why would you have that?
I'm stuck on this problem for a few days now, I mean I could just go for a Timeline and "hardcode" it, but well, I want to learn what causes these issues.
TLDR;
ProgressBar turns null after initializing the application when using the Preloader class, what causes it and what should I do?
EDIT:
It seems like the class is not being reconstructed somewhere, idk i'm really stuck lol

When the FXMLLoader loads your FXML file, it creates a new instance of the specified controller. However, this is not the desired outcome. The object that receives your state callbacks should be the controller of your FXML window.
Replacing your FXMLLoader.load(...) block with
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("preloader.fxml"));
fxmlLoader.setController(this);
Parent root = fxmlLoader.load();
and removing the controller property from your FXML, causes the FXMLLoader to use the given controller instance (being the one that receives the state callbacks) instead of it constructing a new instance of the specified controller class.

Related

Cannot invoke "Object.getClass()" because "obj" is null - JavaFX [duplicate]

So basically I began a dummy JavaFX project just to achieve a minimalistic example for my actual problem. But now I am not even able to run that minimalistic project anymore and do not receive enough error information to actually google it myself out. So right now, when I run the code, I receive the given error stack, which does not lead me anywhere.
I am using IntelliJ. JavaFX libraries are set correctly and VM Options set to:
--module-path "C:\Program Files\Java\javafx-sdk-11.0.2\lib" --add-modules javafx.controls,javafx.fxml
On top, when I run the code, those errors pop up in console, but the application seems to still be running, because I need to press the Red Stop Button of IntelliJ to actually stop it.
Has anyone some guess, what goes wrong here? I am not experienced enough to follow those errors, since they do not point into my code, but rather into some Deep Java code.
The Exception:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.NullPointerException
at java.base/java.lang.reflect.Method.invoke(Method.java:559)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
... 5 more
Main.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
private Stage rootStage;
public BorderPane mainWindow;
public AnchorPane left;
public AnchorPane bottom;
#Override
public void start(Stage primaryStage) throws Exception {
this.rootStage = primaryStage;
loadMainWindow();
}
public void loadMainWindow() throws IOException {
FXMLLoader loaderMainWindow = new FXMLLoader(Main.class.getResource("MainWindow.fxml"));
mainWindow = loaderMainWindow.load();
FXMLLoader loaderLeft = new FXMLLoader(Main.class.getResource("Left.fxml"));
left = loaderLeft.load();
mainWindow.setLeft(left);
//mainWindow.setBottom(bottom);
Scene scene = new Scene(mainWindow);
rootStage.setScene(scene);
rootStage.show();
}
public void main(String[] args) {
launch(args);
}
}
MainWindow.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainWindowController" />
MainWindowController:
package sample;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class MainWindowController implements Initializable {
private Main main;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
public void setMain(Main main) {
this.main = main;
}
}
Left.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="100.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.LeftController">
<children>
<Button fx:id="button" layoutX="237.0" layoutY="169.0" mnemonicParsing="false" onAction="#buttonClick" text="Button" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
</children>
</AnchorPane>
LeftController.java:
package sample;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import java.awt.event.ActionEvent;
import java.net.URL;
import java.util.ResourceBundle;
public class LeftController implements Initializable {
#FXML
private Button button;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
public void buttonClick(javafx.event.ActionEvent actionEvent) {
System.out.println("Some Stuff");
}
}
Solution
The error you're getting is caused by your main(String[]) method not being static. If you make it static then the error will go away.
Some Explanation
JavaFX offers the ability to launch applications without providing a main method, so long as the main class is a subclass of Application. However, developers can still include a main method which means this special launch functionality has to handle that situation gracefully. In other words, an explicit main method present in the Application subclass must act like the entry point of the application from the developer's point of view. Nonetheless, behind the scenes some deep internal class has become the "real" main class.
To do this, the main method is located—if present at all—via Class#getMethod(String,Class...) which, while only returning public methods, doesn't distinguish between static and non-static methods. If found, Method#invoke(Object,Object...) is used to invoke the main method reflectively. The first argument of invoke is the instance that the method should be invoked on; in the case of static methods the value is null. Unfortunately, the code assumes the method it found is static which causes a NullPointerException to be thrown—you can't call an instance method on a null "instance".
Update: This issue has been submitted on GitHub (#570) and subsequently JBS (JDK-8230119). The current idea is to emit a warning rather than throw the NullPointerException. However, the functionality that allows launching without a main method may be deprecated in a future release, which will affect how this issue is addressed.

How to solve JavaFX problem for using pop-up notifications

I am learning JavaFX and my IDE for doing that is NetBeans IDE 8.2.
I have a problem when I want to run a project.
my sample project is Drag and Drop a picture on ImageView by scene builder.
the error is:
Executing C:\Users\Mohammad Sadeghi\Documents\NetBeansProjects\DragDrop\dist\run1057050476\DragDrop.jar using platform C:\Program Files\Java\jdk1.8.0_212\jre/bin/java
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$159(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassCastException: javafx.scene.image.ImageView cannot be cast to javafx.scene.Parent
at dragdrop.DragDrop.start(DragDrop.java:22)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$166(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$179(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$177(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$178(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$152(WinApplication.java:177)
... 1 more
Exception running application dragdrop.DragDrop
Java Result: 1
my FXMLDocument.fxml is :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.ImageView?>
<ImageView fx:id="ImgID" fitHeight="228.0" fitWidth="248.0" onDragDone="#ondragdone" pickOnBounds="true" preserveRatio="true" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dragdrop.FXMLDocumentController"/>
FXMLDocumentController.java :
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package drag-drop;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.DragEvent;
/**
*
* #author Mohammad Sadeghi
*/
public class FXMLDocumentController implements Initializable {
private Label label;
#FXML
private ImageView ImgID;
public FXMLDocumentController() {
}
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void ondragdone(DragEvent event) {
List<File> fi=event.getDragboard().getFiles();
try {
Image img=new Image(new FileInputStream(fi.get(0)));
ImgID.setImage(img);
} catch (Exception e) {
System.out.println("ERROR");
}
}
}
and my Main app() is :
public class DragDrop extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I don't know what is this error for !!
I have almost the same error on another JavaFX project when I am using of Scenebuilder.
could anyone help me?
If you look at the signature of FXMLLoader#load(URL) you'll see:
public static <T> T load​(URL location) throws IOException
Notice the <T>? That makes the method generic and the type parameter is used as the return type of the method. This is what allows you to assign the result of calling #load(URL) to a variable of any type, such as you're doing.
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
You could literally change the type of root to any other type and the code would still compile. Currently, however, the above code has T being inferred as Parent. Under the hood this uses a cast, same as you would have to do if the #load(URL) method returned Object instead of T.
Parent root = (Parent) FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
The problem is your FXML declares the root element to be an ImageView. This means the actual type of object being returned by the #load(URL) method is ImageView, not Parent, and the ImageView class is not a subtype of Parent—thus the ClassCastException.
One option is to use ImageView root = ...;. However, this is not sufficient because you are attempting to use root as the root of a Scene which must be a Parent. The better solution is to wrap ImageView in a Parent in the FXML file. Here's an example using a StackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.StackPane?>
<StackPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="dragdrop.FXMLDocumentController">
<ImageView fx:id="ImgID" fitHeight="228.0" fitWidth="248.0" onDragDone="#ondragdone"
pickOnBounds="true" preserveRatio="true"/>
</StackPane>

Distinguish between foo(int) and foo(Integer) in java

I haven't been able to find this, though I currently have a usecase where I have a generic type which has a method foo(int) and a method foo(T).
For my usecase said type is instantiated with T = Integer, meaning I have the methods foo(int) and foo(Integer).
Whenever I try to call foo(Integer) it calls foo(int) instead, no matter whether the type is specified, whether I cast or not. The only thing solving it is using a Long instead, which I do not want to do.
Is there any way I can force java to use the foo(Integer) method?
Edit:
For once, to answer the comment, I don't think code was relevant here, as what I described was formable enough to understand what I meant.
Secondly, the error was on my end, I apologise. I didn't have the expected behaviour and thought it'd be because of an issue in that regard, especially since my IDE displayed the usage of the foo(int) method. I'll be closing this now
A MVCE:
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(Main,args);
}
}
Controller.java
package sample;
import javafx.collections.FXCollections;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
public class Controller implements Initializable {
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
ListView<Integer> listView = new ListView<>();
listView.setItems(FXCollections.observableArrayList(1, 5, 8, 13));
Integer t = 5;
listView.getSelectionModel().select(t);
System.out.println(listView.getSelectionModel().getSelectedItems());
}
}
sample.fxml
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>
You will notice this code works as expected, what I figured out now though is that, since I'm not using java but rather groovy - switching the file endings to groovy and compiling with a groovy compiler makes this program have my described behaviour, which means the issue is groovy related not java related.
You question as stated has a simple answer:
class Foo<T> {
void foo(int i) {
System.out.println("foo(int)");
}
void foo(T t) {
System.out.println("foo(T)");
}
}
private void test() {
Foo<Integer> foo = new Foo<>();
foo.foo(1);
foo.foo((Integer)1);
foo.foo(Integer.valueOf("1"));
}
prints:
foo(int)
foo(T)
foo(T)
However, I suspect you've tried this so please post some example code.
If you like, check out the rules for method selection here: https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.3.

JavaFX CheckboxTreeItem: unselectable leaves

I'm trying to set up a checkbox tree in JavaFX where the leaves are bound to their parents:
they are selected when the parent is selected
cannot be changed individually.
I did it using a binding with the selectedProperty.
It works just fine, but it throws an exception each time I select or deselect the parent.
Is there an easy way around this? I hate to abandon an approach that works except for those nasty exceptions.
The relevant code is below. Please forgive any java convention errors - I'm still new to the language.
Controller:
package application;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
public class BindingTestController {
#FXML
private TreeView<String> MainTree;
static List<CheckBoxTreeItem<String>> treeItems = new ArrayList<CheckBoxTreeItem<String>>();
CheckBoxTreeItem<String> root = new CheckBoxTreeItem<String>("Root");
CheckBoxTreeItem<String> parent1 = new CheckBoxTreeItem<String>("Parent1");
CheckBoxTreeItem<String> parent2 = new CheckBoxTreeItem<String>("Parent2");
private void AddColumns(CheckBoxTreeItem<String> item){
for (int i = 1 ; i < 5 ; i++){
CheckBoxTreeItem<String> itemColumn = new CheckBoxTreeItem<String>(item.getValue()+"_Column_"+i);
treeItems.add(itemColumn);
item.getChildren().add(itemColumn);
itemColumn.selectedProperty().bind(item.selectedProperty());
}
}
#FXML // This method is called by the FXMLLoader when initialization is complete
private void initialize() {
treeItems.add(root);
treeItems.add(parent1);
root.getChildren().add(parent1);
treeItems.add(parent2);
root.getChildren().add(parent2);
AddColumns(parent1);
AddColumns(parent2);
MainTree.setRoot(root);
MainTree.setShowRoot(true);
root.setExpanded(true);
MainTree.setCellFactory(p-> new CheckBoxTreeCell());
MainTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
MainTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
CheckBoxTreeItem<String> treeItem = (CheckBoxTreeItem)newValue;
}
});
}
}
Here is the FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BindingTestController">
<center>
<VBox prefHeight="1049.0" prefWidth="714.0"BorderPane.alignment="CENTER">
<children>
<TreeView fx:id="MainTree" prefHeight="624.0" prefWidth="714.0"/>
</children>
</VBox>
</center>
</BorderPane>
and here is the main:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
// BorderPane root = new BorderPane();
BorderPane root = (BorderPane) FXMLLoader.load(Main.class.getResource("BindingTest.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Any advice would be appreciated.
The problem is coming from this line
itemColumn.selectedProperty().bind(item.selectedProperty());
To understand why do you get an exception whatever node you select, you should take a look on the independentProperty of CheckBoxTreeItem:
A CheckBoxTreeItem can be independent or dependent. By default,
CheckBoxTreeItem instances are dependent, which means that any changes
to the selection state of a TreeItem will have an impact on parent and
children CheckBoxTreeItem instances. If a CheckBoxTreeItem is set to
be independent, this means that any changes to that CheckBoxTreeItem
will not directly impact the state of parent and children
CheckBoxTreeItem instances.
This means, when you select/deselect a node in the tree, which has children, it will select/deselect all of it's children by default (and also sets the checked, unchecked, indeterminate state of its parent, but this is not important in your case). This is one of the functionality what you need and you get it out of the box.
Now you can understand why your binding is problematic:
You bind the selectedProperty() of the child nodes to the selectedProperty() of the parent node, then:
if you check one of the leaf nodes, you will get an exception, because the value is bound, and bound values cannot be set.
if you check one of the parent nodes, it will try to select all of its children (because of being dependent), then you will get an exception again.
Solution
You have two requirements:
select all of the children nodes, when the parent is selected, which is already done
do not allow to select child nodes manually
For the second requirement, you can disable the checkbox of these items. For this you can use the setCellFactory method of your TreeView:
MainTree.setCellFactory((item) -> {
return new CheckBoxTreeCell<String>(){
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
this.disableProperty().unbind();
CheckBoxTreeItem<String> value = (CheckBoxTreeItem<String>) treeItemProperty().getValue();
this.disableProperty().bind(value.leafProperty());
}
}
};
});
This snippet will bind the disableProperty of the displayed CheckBoxTreeCell objects to the leafProperty of the corresponding CheckBoxTreeItem.
public final ReadOnlyBooleanProperty leafProperty()
Represents the TreeItem leaf property, which is true if the TreeItem
has no children.
Which results in a tree where all the nodes that has no children are disabled.
To make this working you should remove this line from your code:
itemColumn.selectedProperty().bind(item.selectedProperty());
and you should replace this line
MainTree.setCellFactory(p-> new CheckBoxTreeCell());
with the code posted above.

Javafx Donut Chart in FXML

I want to do a Doughnut/Donut chart on JavaFX and searching I came to this example: Can PieChart from JavaFX be displayed as a doughnut?
I Works really nice, but since I'm using FXML to make my GUI, I can't use this example. First, I tried to add the DoughtnutChart.java class as a #FXML var in the controller class of the panel where I want to insert it, but launched errors.
Then, searched in Google to make the DoughnutChart a custom component, but all the examples are based on Panes. Also, If I try to import my donu.jar to SceneBuilder, the window to select a component is empty.
So, my question is: How do I implement this Doughnut Chart on JavaFX when my GUI is made on FXML?
Thanks a lot.
It's hard to tell what the cause of your error is without seeing the FXML and the error message.
I got this to work pretty easily: the one thing to be aware of is that the FXMLLoader instantiates classes by invoking the no-argument constructor. If it can't find one, it tries to use a builder class as a back-up plan. So the one modification you need to make to #jewelsea's DoughnutChart implementation is to add a no-argument constructor. (You could also define a DoughnutClassBuilder, but that's a lot more work, and doesn't get you any extra benefit.) So I did this:
package doughnut ;
// imports as before...
public class DoughnutChart extends PieChart {
private final Circle innerCircle;
public DoughnutChart() {
this(FXCollections.observableArrayList());
}
// everything else as before...
}
Then the following FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import doughnut.DoughnutChart?>
<StackPane xmlns:fx="http://javafx.com/fxml" fx:controller="doughnut.SampleController">
<DoughnutChart fx:id="doughnutChart" />
</StackPane>
with the controller SampleController.java:
package doughnut;
import javafx.fxml.FXML;
import javafx.scene.chart.PieChart;
public class SampleController {
#FXML
private PieChart doughnutChart ;
public void initialize() {
doughnutChart.getData().addAll(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30));
}
}
and the application class
package doughnut;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = (StackPane)FXMLLoader.load(getClass().getResource("DoughnutChartDemo.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
work exactly as expected.
I had to replace this with super in James_D answer to make it work.
For me the constructor looked like:
public DoughnutChart()
{
super(FXCollections.observableArrayList());
innerCircle = new Circle();
// just styled in code for demo purposes,
// use a style class instead to style via css.
innerCircle.setFill(Color.WHITESMOKE);
innerCircle.setStroke(Color.WHITE);
innerCircle.setStrokeWidth(3);
}

Categories