javafx 8 compatibility issues - FXML static fields
It sounds like you are trying to inject a TextField
into a static field. Something like
@FXML
private static TextField myTextField ;
This apparently worked in JavaFX 2.2. It doesn't work in JavaFX 8. Since no official documentation ever supported this use, it's doesn't really violate backward compatibility, though in fairness the documentation on exactly what the FXMLLoader
does is pretty woeful.
It doesn't really make much sense to make @FXML
-injected fields static. When you load an FXML file, it creates new objects for each of the elements in the FXML file. A new controller instance is associated with each call to FXMLLoader.load(...)
and the fields in that controller instance are injected with the corresponding objects created for the FXML elements. So the injected fields are necessarily specific to the controller instance. If you had a static injected fields in the controller, and you loaded the same FXML file twice and displayed it twice in the UI, then you would have no way of referencing both sets of controls.
Update: Response to question in comments
In particular, don't use static fields just to enable them to be accessible from outside the class. A static field has a single value belonging to the class, instead of a value for each instance of the class, and the decision to make fields static should only be made if that makes sense. In other words, static
defines scope, not accessibility. To allow access to instance data, you just have to have a reference to the instance. The FXMLLoader
has a getController()
method that allows you to retrieve a reference to the controller.
A related point: it's also not a good idea to expose the UI controls from the controller. You should instead expose the data. For example, instead of defining a getTextField()
method in the controller, instead define a textProperty()
method that returns a StringProperty
representing the contents of the TextField
. The reason for this is that when your boss comes to the office and tells you he wants the TextField
to be replaced by a TextArea
, or a ComboBox<String>
, or some other control, then it's going to be a lot harder if classes outside of the controller are using your TextField
. The structure of the data represented by your controller is much less likely to change than the implementation of how that data is presented to the user.
For some examples
- Passing Parameters JavaFX FXML
- JavaFX Data Mangement
- Access fields from another Controller in JavaFX
- https://github.com/james-d/LeastCommonMultipleCalculator
- https://github.com/james-d/Nested-Controller-Example
- https://github.com/james-d/Dialog-FXML-Example
- https://github.com/james-d/Shared-Data-Controller
(JavaFx) Proper way represent static FXML fields (and why this is a bad idea)
This question is probably too opinion-based for this forum, but I think the opinions I'm expressing here are held widely enough for this to be acceptable.
Why are static fields a bad idea
Suppose you did what you are trying to do here, and it was allowed to work. Imagine your boss comes into your office in 6 months, and says something like "The customers love the application, but they want to be able to open multiple node editors at the same time. Can you make the application so they can open new windows, each with a node editor in?"
This kind of thing happens in real life all the time.
So in theory this should be easy. You have the FXML that defines the layout, and a controller than defines the logic associated with it. So all you need to do is have an event handler (say on a menu) that creates a new Stage
and a Scene
, loads the FXML again, and sets the root of the scene to the root of the FXML.
Except that it doesn't work. The problem is, you make the root pane static
. So when you loaded the FMXL again, it replaced the current value of canvas
with the new one from the newly-loaded FXML. If you now try to add new elements to the first canvas, you can't.
So now you have to refactor the code, so that canvas
is an instance variable. Worse still, you publicly exposed it via the static getCanvas()
method, which might have been called anywhere in your application. So you have to track down all those invocations. And you end up at 3am trying to grep your entire codebase to figure out what you have to change.
The problem (one problem) with making things static is that you commit to only ever having one copy of that variable, and essentially you commit to that for all time (or commit to rewriting much of the application if you change your mind). That decision leaks upwards: by only ever having one canvas, you can only ever have one controller, so you can only ever have one node editor. You have essentially prevented the code you have written from being reusable.
Your underlying assumption here is wrong. The static
keyword really has nothing to do with accessibility: it is about scope. A static
field is scoped to the class: a non-static field is scoped to each instance of that class. You made the field accessible by defining a public getCanvas()
method and making the scope of that method - the class - available (by making the class public). If canvas
is an instance variable, you can make it accessible by similarly defining a public getCanvas()
method and making the scope of that method -- now the CanvasController
instance -- available. So you load the FXML, retrieve the controller by calling getController()
on the FXMLLoader
, and pass the controller instance to whoever needs access to it. Notice now you have control in the code over who has received a reference to the controller, and if you need any refactoring you at least have a path to follow to find where the controller is used.
You mention "JavaFX doesn't have a NodeProperty
or an AnchorPaneProperty
: it has an ObjectProperty
, so you can use an ObjectProperty<Node>
or an ObjectProperty<AnchorPane>
if you want/need.
In general, though, I think it's also a bad idea to expose view elements (like your canvas
) outside of the controller, and even to share controllers with other parts of the application. You should instead consider using a MVC-type approach. What is missing from your question is the "M": the model. So your model would use an object model with classes like Image
, Scale
, Composite
, etc. The classes should use JavaFX properties to make them observable and use ObservableList
(and similar) to store collections of these objects and relationships among them. Create a single model instance and pass it to your controller. The controller can observe the data in the model and update the view if it changes, and update the model according to user input. Then share the model instance with the other parts of the application that need access to it. This keeps things more maintainable: if you decide, for example, canvas
needs to be a Pane
instead of an AnchorPane
, that change is now confined to your FXML and controller, and doesn't leak out over the rest of your application. A (much simpler) example of MVC (really more MVP) in JavaFX is in this question.
Can't set the value of a static TextField in javafx
JavaFX does not inject components into static fields. If possible, you should pass an instance of this class around instead of using static fields. This can be accomplished by allowing methods and class using this class to take this class as a parameter. You are also able to get the controller using FXMLLoader#getController
.
However, if necessary, you can have a non-static field and set a static field on initialize
:
@FXML
private TextField ReferenceKey;
public static TextField referenceKeyPublic;
...
@Override
public void initialize(URL url, ResourceBundle rb) {
ClassName.referenceKeyPublic = ReferenceKey;
}
javafx 8 compatibility issues - FXML static fields
It sounds like you are trying to inject a TextField
into a static field. Something like
@FXML
private static TextField myTextField ;
This apparently worked in JavaFX 2.2. It doesn't work in JavaFX 8. Since no official documentation ever supported this use, it's doesn't really violate backward compatibility, though in fairness the documentation on exactly what the FXMLLoader
does is pretty woeful.
It doesn't really make much sense to make @FXML
-injected fields static. When you load an FXML file, it creates new objects for each of the elements in the FXML file. A new controller instance is associated with each call to FXMLLoader.load(...)
and the fields in that controller instance are injected with the corresponding objects created for the FXML elements. So the injected fields are necessarily specific to the controller instance. If you had a static injected fields in the controller, and you loaded the same FXML file twice and displayed it twice in the UI, then you would have no way of referencing both sets of controls.
Update: Response to question in comments
In particular, don't use static fields just to enable them to be accessible from outside the class. A static field has a single value belonging to the class, instead of a value for each instance of the class, and the decision to make fields static should only be made if that makes sense. In other words, static
defines scope, not accessibility. To allow access to instance data, you just have to have a reference to the instance. The FXMLLoader
has a getController()
method that allows you to retrieve a reference to the controller.
A related point: it's also not a good idea to expose the UI controls from the controller. You should instead expose the data. For example, instead of defining a getTextField()
method in the controller, instead define a textProperty()
method that returns a StringProperty
representing the contents of the TextField
. The reason for this is that when your boss comes to the office and tells you he wants the TextField
to be replaced by a TextArea
, or a ComboBox<String>
, or some other control, then it's going to be a lot harder if classes outside of the controller are using your TextField
. The structure of the data represented by your controller is much less likely to change than the implementation of how that data is presented to the user.
For some examples
- Passing Parameters JavaFX FXML
- JavaFX Data Mangement
- Access fields from another Controller in JavaFX
- https://github.com/james-d/LeastCommonMultipleCalculator
- https://github.com/james-d/Nested-Controller-Example
- https://github.com/james-d/Dialog-FXML-Example
- https://github.com/james-d/Shared-Data-Controller
Issues with upgrading from JavaFX 2.2 to JavaFX 8 (Possible bug?)
I believe support for static access via @FXML
disappeared in JavaFX 8. Remove the static modifier.
Static @FXML variables in FXML Controller
Anyway I refactored everything and it wasn't as hard as I thought. I spent about half the time refactoring as I did searching for a solution.
I'll leave the question in case any other poor souls have the same problem.
JavaFX-8 setRoot on embedded fxml causes cursor flickering and memory / cpu resource issues
Alright, so the problem was accidental recursion/loop. The code I executed to load a Node from a FXML was in the initialize(URL, ResourceBundle)
-method. Now, setting the controller of the new loaded fxml to the same controller-object from where it is created from (loader.setController(this);
) causes the initialize(URL, ResourceBundle)
-method to run again and therefore load the same fxml again. This never stops.
My solution was to remove the loader.setController(this);
call and (more importantly) set the controller in the fxml itself to some other controller. To keep connection between the controllers I created an interface called SubController
. All controllers which represent a step/task and not the wizard itself implement it. The interface specifies the four onButtonClick-methods I want to forward to the SubController. Actual handling of the event now happens in the SubController.
@FXML
void onCancel(ActionEvent event)
{
this.currentSub.onCancel(event);
}
@FXML
void onFinish(ActionEvent event)
{
this.currentSub.onFinish(event);
}
@FXML
void onNext(ActionEvent event)
{
this.currentSub.onNext(event);
}
@FXML
void onPrevious(ActionEvent event)
{
this.currentSub.onPrevious(event);
}
Root cannot be null exception with multiple FXML/Controller in JavaFX
You're creating a new WorkoutsController
instance in your WorkoutsMainController
, instead of using the one created for you by the FXMLLoader
. The @FXML
-annotated fields in the instance you create will all be null, so you are probably getting a null pointer exception somewhere. (You should be seeing the "Error when trying to load WorkoutsMain.fxml" message in the console.) Thus anchorPane
in your start()
method is never initialized and you get an exception when you pass the null anchorPane
reference to the Scene
constructor.
Note also you have (for some unknown reason) made splitPane
and anchorPane
static
: it makes no sense to do that, and the FXMLLoader
will not initialize static fields. So those fields are also null in the controller.
If you don't squash exceptions thrown by loading the FXML, you will be able to see the actual stack trace for the underlying exception.
The fixes are:
don't make
@FXML
-annotated fieldsstatic
. See javafx 8 compatibility issues - FXML static fieldsTo inject the actual controller instance for the included FXML that is created by the
FXMLLoader
, see Nested Controllers in the documentation. In short, since yourfx:include
hasfx:id="workoutsPane"
, you should replaceprivate final WorkoutsController workoutsController = new WorkoutsController();
with
@FXML private WorkoutsController workoutsPaneController ;
(and replace all occurrences of
workoutsController
withworkoutsPaneController
in the remainder ofWorkoutsMainController
).
Running my app returns me : Javafx.fxml.LoadExeption
You get a NullPointerException
in your initialize()
method. That is because your ComboBox
methode is declared static.
The FXML injection does not work with static variables in JavaFX8.
Refer to the answer by James_D to this question:
javafx 8 compatibility issues - FXML static fields
public class HexaController implements Initializable {
private org.jdom2.Document document;
private JFrame fileDialog;
private Element racine;
private String Nom_dar;
private java.util.List<Element> listeDAS;
private ArrayList<String> Nom_das = new ArrayList();
private ArrayList<String> Nom_task = new ArrayList();
private ArrayList<String> Nom_Variable1 = new ArrayList();
private ArrayList<String> Resultat_Variable1 = new ArrayList();
private ArrayList<Double> Resultat_Variabledouble1 = new ArrayList<Double>();
private ArrayList<String> ResultatHexa_Variable1 = new ArrayList<String>();
private ArrayList<String> Lien = new ArrayList();
private java.util.List<Element> ListeTRIAL;
private java.util.List<Element> listeTASK;
private java.util.List<Element> listeOperation;
private List listeOperation_delta;
private java.util.List<Element> listeArgument;
private List listeArgument_delta;
@FXML
private ComboBox<String> hexa;
ObservableList<String> list = FXCollections.observableArrayList();
@FXML
private TextField entree;
@FXML
private TextField nomfichier;
@FXML
private TextField excel;
@FXML
private TextField sortie;
@FXML
private void dar(ActionEvent event) {
FileDialog fd1 = new FileDialog(fileDialog, "Choisissez un fichier .dar", FileDialog.LOAD);
fd1.setDirectory("C:\\");
fd1.setVisible(true);
String filename1 = fd1.getFile();
String Directory1 = fd1.getDirectory();
String path1 = Directory1 + filename1;
entree.setText(path1);
nomfichier.setText(filename1);
}
@FXML
private void modele(ActionEvent event) {
JFrame parentFrame = new JFrame();
FileDialog filechooser = new FileDialog(parentFrame, "Choisir un modèle Excel à copier", FileDialog.LOAD);
filechooser.setDirectory("C:\\");
filechooser.setVisible(true);
String directory_copy = filechooser.getDirectory();
String name_copy = filechooser.getFile();
String path_copy = (directory_copy + name_copy);
excel.setText(path_copy);
}
@FXML
private void sortie(ActionEvent event) {
JFrame parentFrame2 = new JFrame();
FileDialog filechooser2 = new FileDialog(parentFrame2, "Choisir une destination d'enregistrement", FileDialog.SAVE);
filechooser2.setDirectory("C:\\");
filechooser2.setVisible(true);
String directory_save = filechooser2.getDirectory();
String name_save = filechooser2.getFile();
String path_save = (directory_save + name_save + ".xls");
sortie.setText(path_save);
}
@FXML
private void annuler(ActionEvent event) {
System.exit(0);
}
@FXML
private ComboBox<Integer> methode;
ObservableList<Integer> nombre = FXCollections.observableArrayList();
@FXML
private void exe(ActionEvent event) throws Throwable {
SAXBuilder sxb = new SAXBuilder();
String filename1 = nomfichier.getText();
if (filename1 == null) {
System.out.println("Annulation");
System.exit(0);
} else {
try {
//On crée un nouveau document JDOM avec en argument le fichier XML
//Le parsing est terminé
String path1 = entree.getText();
document = sxb.build(new File(path1));
//On initialise un nouvel élément racine avec l'élément racine du document.
racine = document.getRootElement();
System.out.println("Lecture du premier .dar");
} catch (Exception e) {
System.out.println("FATAL ERROR !");
System.exit(0);
}
}
afficheALL();
Xl();
}
//RECUPERATION DES DONNEES DU FICHIER DAR
private void afficheALL() {
//Variables Locales
String Str;
String Str_bis;
Nom_dar = racine.getAttributeValue("label");
System.out.println("Nom du fichier dar : " + Nom_dar + "\n");
listeDAS = racine.getChildren("DAS");
//On crée un Iterator sur notre liste
Iterator<Element> i1 = listeDAS.iterator();
////TRAITEMENT POUR CHAQUE DAS
String m[] = new String[50];
m[0] = "Marqueur de Sortie";
m[1] = "Nb Occurences";
m[2] = "Valeur Physique";
m[3] = "Résultat";
m[4] = "Valeur de Sortie";
int s = methode.getValue();
int n;
for (n = 5; n < 5 + s; n++) {
//System.out.println("Entrez le nom de la methode");
//Scanner methode = new Scanner(System.in);
//String name=methode.nextLine();
//m[n]=name;
}
int j;
for (j = 0; j < 5 + s; j++) {
System.out.println("Le nom de la methode " + j + " est :" + m[j]);
}
while (i1.hasNext()) {
Element courant = (Element) i1.next();
//On affiche le nom de l’élément courant
Str = courant.getAttributeValue("dasFullFileName").toString();
System.out.println("Nom du DAS: " + Str);
Nom_das.add(Str);
ListeTRIAL = courant.getChildren("TRIAL");
@SuppressWarnings("rawtypes")
Iterator T1 = ListeTRIAL.iterator();
while (T1.hasNext()) {
Element courant2 = (Element) T1.next();
Lien.add(courant2.getAttributeValue("xconvFullFileName").toString());
}
listeTASK = courant.getChildren("TASK");
@SuppressWarnings("rawtypes")
Iterator k1 = listeTASK.iterator();
while (k1.hasNext()) {
Element courant2 = (Element) k1.next();
Str_bis = courant2.getAttributeValue("label").toString();
System.out.println(Str_bis);
listeOperation = courant2.getChildren("Operation");
@SuppressWarnings("rawtypes")
Iterator l1 = listeOperation.iterator();
while (l1.hasNext()) {
Element courant3 = (Element) l1.next();
listeArgument = courant3.getChildren("Argument");
@SuppressWarnings("rawtypes")
Iterator m1 = listeArgument.iterator();
while (m1.hasNext()) {
Element courant4 = (Element) m1.next();
int l;
for (l = 0; l < 5 + s; l++) {
if (courant4.getAttributeValue("parameterName").compareTo(m[l]) == 0) {
Nom_Variable1.add(courant4.getAttributeValue("argumentName"));
double d = Double.parseDouble(courant4.getAttributeValue("instantValue"));
DecimalFormat df = new DecimalFormat(".#");
df.setMaximumFractionDigits(20);
String sortie = df.format(d);
long entier = (long) d;
String hex = Long.toHexString(entier).toUpperCase();
//hex.toUpperCase();
Resultat_Variable1.add(sortie);
Resultat_Variabledouble1.add(d);
ResultatHexa_Variable1.add(hex);
}
}
}
}
}
System.out.println("Fin lecture XML File");
}
}
private void Xl() throws Throwable {
try {
System.out.println("Saisi utilisateur ok");
//CREATION DES FEUILLES NECESSAIRES A LA RECUPERATION DES DONNEES
String path_copy = excel.getText();
String path_save = sortie.getText();
Workbook copie = Workbook.getWorkbook(new File(path_copy));
WritableWorkbook sortie = Workbook.createWorkbook(new File(path_save), copie);
WritableFont bord = new WritableFont(WritableFont.ARIAL, 11);
WritableCellFormat border = new WritableCellFormat(bord);
//REMPLISSAGE DE LA PREMIERE FEUILLE AVEC LES DONNEES MESUREES
System.out.println("---" + Nom_Variable1.size() + "---");
for (int x = 0; x <= Nom_Variable1.size() - 1; x++) {
Label Col0 = new Label(0, x, Nom_Variable1.get(x), border);
sortie.getSheet(1).addCell(Col0);
}
for (int y = 0; y <= Resultat_Variabledouble1.size() - 1; y++) {
Number Col1 = new Number(1, y, Resultat_Variabledouble1.get(y), border);
sortie.getSheet(1).addCell((WritableCell) Col1);
}
for (int y = 0; y <= ResultatHexa_Variable1.size() - 1; y++) {
Label Col2 = new Label(2, y, ResultatHexa_Variable1.get(y), border);
sortie.getSheet(1).addCell(Col2);
}
System.out.println("Fin Copie Excel");
sortie.write();
sortie.close();
} catch (RowsExceededException e1) {
} catch (BiffException ex) {
} catch (WriteException e1) {
} catch (IOException e) {
} finally {
System.out.println("Le fichier à été généré correctement.");
System.out.println("Appuyez sur 'Entrée' pour terminer le programme");
System.exit(0);
}
System.exit(0);
}
public HexaController() {
}
@Override
public void initialize(URL url, ResourceBundle rb) {
list.add(new String("OUI"));
list.add(new String("NON"));
hexa.setItems(list);
nombre.add(new Integer(0));
nombre.add(new Integer(1));
nombre.add(new Integer(2));
nombre.add(new Integer(3));
nombre.add(new Integer(4));
nombre.add(new Integer(5));
methode.setItems(nombre);
}
}
Related Topics
Unfortunately Myapp Has Stopped. How to Solve This
How to Make a .Jar Out from an Android Studio Project
Why Does My Function That Calls an API or Launches a Coroutine Return an Empty or Null Value
Nullpointerexception Accessing Views in Oncreate()
Android - Get Real Path of a .Txt File Selected from the File Explorer
How to Get Url from Firebase Storage Getdownloadurl
What's the Best Way to Share Data Between Activities
Why Doesn't Recyclerview Have Onitemclicklistener()
Android, Getting Resource Id from String
What Is the Java String Pool and How Is "S" Different from New String("S")
Java Using Much More Memory Than Heap Size (Or Size Correctly Docker Memory Limit)
Element Myelement Is Not Clickable At Point (X, Y)... Other Element Would Receive the Click