Asked  7 Months ago    Answers:  5   Viewed   75 times

I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.

Is this possible? I havent found any way to do it.

Static functions could be a way, but they don't have access to the form's controls.

Any ideas?

 Answers

77

You can get the controller from the FXMLLoader

FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();

store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:

getFooController().updatePage(strData);

updatePage() can be something like:

// ...
@FXML private Label lblData;
// ...
public void updatePage(String data){
    lblData.setText(data);
}
// ...

in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.

Also look the https://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.

Tuesday, June 1, 2021
 
drowneath
answered 7 Months ago
82

What you're after is often handled by "dependency injection". It's quite common to pass in an object like your Page object at creation of the Template object, in the Template constructor. The Page object itself may be a property of Template, or only its $id property. So it isn't necessary to pass in the id on every function call.

Template::__construct($Page)

If the Template class is related the Page class, it may also extend the Page class, whereby it would already have access to the Page::$id if that property was protected or public.

Saturday, May 29, 2021
 
Camsoft
answered 7 Months ago
21

nested class - class defined within other class (includes static and non-static classes)
inner class - non-static nested class (instance of inner class need instance of outer class to exist)

non-nested (top level) classes

Based on your question we know that constructor you want to access is not public. So your class may look like this (A class is in some package different than ours)

package package1;

public class A {
    A(){
        System.out.println("this is non-public constructor");
    }
}

To create instance of this class we need to get to constructor we want to invoke, and make it accessible. When it is done we can use Constructor#newInstance(arguments) to create instance.

Class<?> c = Class.forName("package1.A");
//full package name --------^^^^^^^^^^
//or simpler without Class.forName:
//Class<package1.A> c = package1.A.class;

//In our case we need to use
Constructor<?> constructor = c.getDeclaredConstructor();
//note: getConstructor() can return only public constructors
//so we needed to search for any Declared constructor

//now we need to make this constructor accessible
constructor.setAccessible(true);//ABRACADABRA!

Object o = constructor.newInstance();

nested and inner classes

If you want to access nested (static and non-static) Class with Class.forName you need to use syntax:

Class<?> clazz = Class.forName("package1.Outer$Nested");

Outer$Nested says that Nested class is declared within Outer class. Nested classes are very similar to methods, they have access to all members of its outer class (including private ones).

But we need to remember that instance of inner class to exists requires instance of its outer class. Normally we create them via:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

so as you see each instance of Inner class have some information about its outer class (reference to that outer instance is stored in this$0 field, more info: What does it mean if a variable has the name "this$0" in IntelliJ IDEA while debugging Java?)

So while creating instance of Inner class with Constructor#newInstance() you need to pass as first argument reference to instance of Outer class (to simulate outer.new Inner() behavior).

Here is an example.

in package1

package package1;

public class Outer {
    class Inner{
        Inner(){
            System.out.println("non-public constructor of inner class");
        }
    }
}

in package2

package package2;

import package1.Outer;
import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {

        Outer outerObject = new Outer();

        Class<?> innerClazz = Class.forName("package1.Outer$Inner");

        // constructor of inner class as first argument need instance of
        // Outer class, so we need to select such constructor
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(Outer.class);

        //we need to make constructor accessible 
        constructor.setAccessible(true);

        //and pass instance of Outer class as first argument
        Object o = constructor.newInstance(outerObject);

        System.out.println("we created object of class: "+o.getClass().getName());

    }
}

static-nested classes

Instances of static-nested classes don't require instance of Outer class (since they are static). So in their case we don't need to look for constructor with Outer.class as first argument. And we don't need to pass instance of outer class as first argument. In other words code will be same as for non-nested (top-level) class (maybe except fact that you would need to add $Nested syntax in Class.forName()).

Tuesday, July 13, 2021
 
Evernoob
answered 5 Months ago
60

If you set the static methods setTopAnchor( child, value ), setBottomAnchor( ... ), setLeftAnchor( ... ), setRightAnchor( ... ) of class AnchorPane to 0.0, the child Node will get stretched to the full extend of the parent AnchorPane.

Documentation Link: AnchorPane

edit: in the documentation link you can also see how you can set these values in your java code.

FXML example:

<AnchorPane fx:id="mainContent" ...>
<StackPane fx:id="subPane" AnchorPane.topAnchor="0.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" ></StackPane>
</AnchorPane>
Sunday, August 1, 2021
 
BaajiRao
answered 5 Months ago
44

Short answer: No, it is not (as of JavaFX 2.x and 8.0). It may be in a future version (JFX >8)

Long answer: The FXMLLoader is currently not designed to perform as a template provider that instantiates the same item over and over again. Rather it is meant to be a one-time-loader for large GUIs (or to serialize them).

The performance is poor because depending on the FXML file, on each call to load(), the FXMLLoader has to look up the classes and its properties via reflection. That means:

  1. For each import statement, try to load each class until the class could successfully be loaded.
  2. For each class, create a BeanAdapter that looks up all properties this class has and tries to apply the given parameters to the property.
  3. The application of the parameters to the properties is done via reflection again.

There is also currently no improvement for subsequent calls to load() to the same FXML file done in the code. This means: no caching of found classes, no caching of BeanAdapters and so on.

There is a workaround for the performance of step 1, though, by setting a custom classloader to the FXMLLoader instance:

import java.io.IOException; 
import java.net.URL; 
import java.util.Enumeration; 
import java.util.HashMap; 
import java.util.Map; 

public class MyClassLoader extends ClassLoader{ 
  private final Map<String, Class> classes = new HashMap<String, Class>(); 
  private final ClassLoader parent; 

  public MyClassLoader(ClassLoader parent) { 
    this.parent = parent; 
  } 

  @Override 
  public Class<?> loadClass(String name) throws ClassNotFoundException { 
    Class<?> c = findClass(name); 
    if ( c == null ) { 
      throw new ClassNotFoundException( name ); 
    } 
    return c; 
  } 

  @Override 
  protected Class<?> findClass( String className ) throws ClassNotFoundException { 
// System.out.print("try to load " + className); 
    if (classes.containsKey(className)) { 
      Class<?> result = classes.get(className); 
      return result; 
    } else { 
      try { 
        Class<?> result = parent.loadClass(className); 
// System.out.println(" -> success!"); 
        classes.put(className, result); 
        return result; 
      } catch (ClassNotFoundException ignore) { 
// System.out.println(); 
        classes.put(className, null); 
        return null; 
      } 
    } 
  } 

  // ========= delegating methods ============= 
  @Override 
  public URL getResource( String name ) { 
    return parent.getResource(name); 
  } 

  @Override 
  public Enumeration<URL> getResources( String name ) throws IOException { 
    return parent.getResources(name); 
  } 

  @Override 
  public String toString() { 
    return parent.toString(); 
  } 

  @Override 
  public void setDefaultAssertionStatus(boolean enabled) { 
    parent.setDefaultAssertionStatus(enabled); 
  } 

  @Override 
  public void setPackageAssertionStatus(String packageName, boolean enabled) { 
    parent.setPackageAssertionStatus(packageName, enabled); 
  } 

  @Override 
  public void setClassAssertionStatus(String className, boolean enabled) { 
    parent.setClassAssertionStatus(className, enabled); 
  } 

  @Override 
  public void clearAssertionStatus() { 
    parent.clearAssertionStatus(); 
  } 
}

Usage:

public static ClassLoader cachingClassLoader = new MyClassLoader(FXMLLoader.getDefaultClassLoader()); 

FXMLLoader loader = new FXMLLoader(resource); 
loader.setClassLoader(cachingClassLoader); 

This significantly speeds up the performance. However, there is no workaround for step 2, so this might still be a problem.

However, there are already feature requests in the official JavaFX jira for this. It would be nice of you to support this requests.

Links:

  • FXMLLoader should be able to cache imports and properties between to load() calls:
    https://bugs.openjdk.java.net/browse/JDK-8090848

  • add setAdapterFactory() to the FXMLLoader:
    https://bugs.openjdk.java.net/browse/JDK-8102624

Monday, August 9, 2021
 
HuLu ViCa
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share