2012-07-31 19 views
9

Problema Quiero añadir paneles a la medida, construido a través de la escena javafx constructor, a un gridpane en tiempo de ejecución. Mi panel personalizado muestra botones, etiquetas, etc.JavaFX2 - rendimiento muy pobre al agregar paneles (fxml) hechas a medida para gridpane dinámicamente

mi intento Traté de extenderse desde panel ...

public class Celli extends Pane{ 
    public Celli() throws IOException{ 
     Parent root = FXMLLoader.load(getClass().getResource("Cell.fxml")); 
     this.getChildren().add(root);  
    } 
} 

... y luego utilizar este panel en el método de la adición de la conroller

@FXML 
private void textChange(KeyEvent event) { 
    GridPane g = new GridPane(); 
     for (int i=0 : i<100; i++){ 
       g.getChildren().add(new Celli()); 
     } 
    } 
} 

Funciona, pero funciona muy, muy pobre.

Lo que estoy buscando para ¿Hay una manera de diseñar los paneles a través de escena javafx constructor (y como resultado de tener esta paneles en fxml) y luego añadirlo a una gridpane en tiempo de ejecución, sin hacer uso de esta fxmlloader de cada instancia. Creo que funciona mal debido al cargador de fxml. Cuando agrego un botón estándar, p. sin fxml es mucho más rápido.

Respuesta

19

Respuesta corta: No, no lo es (a partir de JavaFX 2.xy 8.0). Puede ser en una versión futura (JFX> 8)

Respuesta larga: El FXMLLoader no está actualmente diseñado para funcionar como un proveedor de plantilla que crea una instancia del mismo tema una y otra vez. Más bien está destinado a ser un cargador de una sola vez para grandes GUI (o para serializarlos).

El rendimiento es pobre porque, según el archivo FXML, en cada llamada al load(), FXMLLoader debe buscar las clases y sus propiedades a través de la reflexión. Eso significa:

  1. Para cada instrucción de importación, intente cargar cada clase hasta que la clase pueda cargarse satisfactoriamente.
  2. Para cada clase, cree un BeanAdapter que busque todas las propiedades de esta clase e intente aplicar los parámetros dados a la propiedad.
  3. La aplicación de los parámetros a las propiedades se realiza mediante reflexión nuevamente.

Actualmente no hay ninguna mejora para las llamadas posteriores a load() al mismo archivo FXML realizado en el código. Esto significa: no hay almacenamiento en caché de las clases encontradas, no hay almacenamiento en caché de BeanAdapters, etc.

hay una solución para la realización de la etapa 1, sin embargo, mediante el establecimiento de un cargador de clases a medida para la instancia FXMLLoader:

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(); 
    } 
} 

Uso:

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

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

Esto acelera significativamente el rendimiento. Sin embargo, no hay ninguna solución para el paso 2, por lo que esto podría ser un problema.

Sin embargo, ya hay solicitudes de funciones en la jira JavaFX oficial para esto. Sería amable de su parte apoyar estas solicitudes.

Enlaces:

http://javafx-jira.kenai.com/browse/RT-23413

http://javafx-jira.kenai.com/browse/RT-23511

+0

Fabulosa respuesta Sebastian – jewelsea

+0

Los enlaces al final ya no son válidos. ¿Alguien sabe lo que solían señalar y puede encontrar el JDK actualizado? ID correspondiente a ellos? – Itai

2

que he tenido un problema similar. También tuve que cargar un componente personalizado basado en fxml varias veces, de forma dinámica, y me tomó demasiado tiempo. La llamada al método FXMLLoader.load fue costosa, en mi caso.

Mi enfoque era paralelizar la instanciación de componentes y resolvió el problema.

Considerando el ejemplo publicado sobre la cuestión, el método controlador con enfoque multi-hilo sería:

private void textChange(KeyEvent event) { 
    GridPane g = new GridPane(); 
    // creates a thread pool with 10 threads 
    ExecutorService threadPool = Executors.newFixedThreadPool(10); 
    final List<Celli> listOfComponents = Collections.synchronizedList(new ArrayList<Celli>(100)); 

    for (int i = 0; i < 100; i++) { 
     // parallelizes component loading 
     threadPool.execute(new Runnable() { 
      @Override 
      public void run() { 
       listOfComponents.add(new Celli()); 
      } 
     }); 
    } 

    // waits until all threads completion 
    try { 
     threadPool.shutdown();  
     threadPool.awaitTermination(3, TimeUnit.SECONDS); 
    } catch (InterruptedException e) { 
     // seems to be a improbable exception, but we have to deal with it 
     e.printStackTrace(); 
    } 

    g.getChildren().addAll(listOfComponents); 
} 
+0

¿Qué versión de JavaFx usaste 2.2 o 8.O? Con JavaFX8.0, su método falla con la excepción: "java.lang.IllegalStateException: No en el hilo de la aplicación FX ..." – Daniel

+0

La versión que usé este código fue 2.2. – Crferreira

0

Simplemente añadiendo código para "el almacenamiento en caché de las clases ya cargados" en el señor @Sebastian determinado código. Está funcionando para mí. Sugiera cambios para un mejor rendimiento.

@Override 
public Class<?> loadClass(String name) throws ClassNotFoundException { 
    System.out.println("In Class loader"); 

    Class result; 
    System.out.println(" >>>>>> Load class : "+name); 
    result = (Class)classes.get(name); 
    if(result != null){ 
     System.out.println(" >>>>>> returning cached class."); 
     return result; 
    }else{ 
    Class<?> c = findClass(name); 
    if (c == null) { 
     throw new ClassNotFoundException(name); 
    } 
    System.out.println(" >>>>>> loading new class for first time only"); 
    return c; 
    } 
} 
Cuestiones relacionadas