2012-07-31 5 views
9

Problem Chcę dodać niestandardowe panele, zbudowane za pomocą konstruktora scen javafx, do gridpania w czasie wykonywania. Moje wykonane na zamówienie panele z przyciskami, etykietami i tak dalej.JavaFX2 - bardzo słaba wydajność podczas dodawania niestandardowych paneli (fxml) do dynamicznej siatki

moja próba Próbowałem rozciągają się od tafli ...

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

..., a następnie użyć tego panelu na dodawanie sposobem conroller

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

działa ale działa bardzo słabo.

Co szukam Czy istnieje sposób, aby zaprojektować panele poprzez javafx sceny budowniczego (iw rezultacie mając to panele w fxml), a następnie dodać go do gridpane przy starcie bez wykorzystania tej fxmlloader dla każda instancja. Myślę, że działa słabo z powodu programu ładującego fxml. Kiedy dodaję standardowy przycisk, np. whitout fxml jest znacznie szybszy.

Odpowiedz

19

Krótka odpowiedź: Nie, nie jest (jak w przypadku JavaFX 2.x i 8.0). Może to być w przyszłej wersji (JFX> 8)

Długa odpowiedź: FXMLLoader nie jest obecnie przeznaczone do wykonywania jako dostawcy szablonów że instancję sam element w kółko. Raczej ma to być jednorazowy moduł ładujący dla dużych GUI (lub serializować je).

Wydajność jest słaba, ponieważ w zależności od pliku FXML, przy każdym wywołaniu load(), FXMLLoader musi sprawdzić klasy i jego właściwości za pomocą odbicia. Oznacza to:

  1. Dla każdej instrukcji importowania, spróbuj załadować każdą klasę, aż klasa może zostać pomyślnie załadowana.
  2. Dla każdej klasy utwórz element BeanAdapter, który wyszukuje wszystkie właściwości tej klasy i próbuje zastosować podane parametry do właściwości.
  3. Zastosowanie parametrów do właściwości odbywa się ponownie poprzez odbicie.

Obecnie nie ma poprawy dla kolejnych wywołań do load() do tego samego pliku FXML wykonanego w kodzie. Oznacza to: brak buforowania znalezionych klas, brak buforowania BeanAdapters i tak dalej.

Istnieje obejście do wykonywania kroku 1, choć przez ustawienie niestandardowej classloader do instancji 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(); 
    } 
} 

Zastosowanie:

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

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

To znacznie przyspiesza wydajność. Jednak nie ma rozwiązania tego problemu dla kroku 2, więc może to nadal stanowić problem.

Istnieją już jednak prośby o dodanie elementów do oficjalnej javy JavaFX. Miło by było, gdybyś poparł te prośby.

Linki:

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

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

+0

Wspaniała odpowiedź Sebastian – jewelsea

+0

Łącza na końcu nie są już ważne. Czy ktoś wie, co kiedyś wskazywali, i może znaleźć zaktualizowany JDK - ??? ID odpowiadający im? – Itai

2

miałem podobny problem. Musiałem też ładować niestandardowy komponent oparty na fxml kilka razy, dynamicznie, i to trwało zbyt długo. Wywołanie metody FXMLLoader.load było w moim przypadku drogie.

Moje podejście polegało na równoległym tworzeniu instancji składnika i rozwiązało problem.

Biorąc pod uwagę przykład zamieszczonych na pytanie, metoda kontroler z wielowątkowego podejścia byłoby:

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

W której wersji JavaFx korzystasz z wersji 2.2 lub 8.O? Z JavaFX8.0 twoja metoda kończy się niepowodzeniem z wyjątkiem: "java.lang.IllegalStateException: Nie w wątku aplikacji FX ..." – Daniel

+0

Wersja, w której użyłem tego kodu, była 2.2. – Crferreira

0

Wystarczy dodać kod „buforowanie już załadowanych klas” w @Sebastian sir danego kodu. To działa dla mnie. Proszę sugerować zmiany w nim dla lepszej wydajności.

@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; 
    } 
}