2012-07-31 31 views
9

問題 我想在運行時將通過javafx場景構建器構建的自定義面板添加到網格面板。我定製的面板有按鈕,標籤等等。JavaFX2 - 動態添加自定義(fxml)面板到gridpane時性能非常差

我嘗試 我試圖從面板延伸......

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

...然後使用該面板在電腦板

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

它的工作原理的添加方法,但表現非常差。

什麼我找 是否有設計通過JavaFX的場景生成器面板的方式(並因此具有這種面板在FXML),然後在運行時添加到gridpane不化妝使用這種fxmlloader爲每個實例。我認爲它執行差,因爲fxml加載器。當我添加一個標準按鈕,例如沒有fxml它非常快。

回答

19

簡短回答:不,它不是(從JavaFX 2.x和8.0開始)。這可能是在未來的版本(JFX> 8)

龍答: 的FXMLLoader目前沒有設計爲一遍遍實例相同的項目模板提供商執行。相反,它意味着成爲大型GUI的一次性裝載器(或者將其序列化)。

性能很差,因爲取決於FXML文件,在每次調用load()時,FXMLLoader必須通過反射來查找類及其屬性。這意味着:

  1. 對於每個導入語句,嘗試加載每個類,直到類可以成功加載。
  2. 對於每個類,創建一個BeanAdapter,查找此類所有的屬性並嘗試將給定的參數應用於屬性。
  3. 將參數應用到屬性是通過反射再次完成的。

目前對代碼中完成的同一個FXML文件的後續調用load()也沒有任何改進。這意味着:沒有找到類的緩存,沒有緩存BeanAdapter等等。

沒有爲步驟1中的表現要解決此問題,不過,通過自定義類加載器設置爲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(); 
    } 
} 

用法:

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

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

這顯著加速性能。但是,步驟2沒有解決方法,所以這可能仍然是一個問題。

但是,官方的JavaFX jira已經有這方面的功能請求。你會很高興支持這些請求。

鏈接:

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

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

+0

很棒的回答Sebastian – jewelsea 2012-07-31 07:36:22

+0

到最後的鏈接不再有效。有誰知道他們以前指向什麼,並可以找到更新的JDK - ? ID對應他們? – Itai 2016-02-04 21:40:43

2

我也有過類似的問題。我也不得不動態地加載一個自定義的基於fxml的組件,它需要花費很長時間。就我而言,FXMLLoader.load方法的調用非常昂貴。

我的方法是並行化組件實例並解決問題。

考慮貼在問題的例子,用多線程的方式控制方法是:

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

您使用2.2或8.O的哪個版本的JavaFx?對於JavaFX8.0,您的方法失敗,併發生異常:「java.lang.IllegalStateException:不在FX應用程序線程上......」 – Daniel 2014-11-18 22:12:21

+0

我使用此代碼的版本爲2.2。 – Crferreira 2014-11-19 15:23:06

0

只是在給定的代碼@Sebastian先生增加對「裝載的類的高速緩存」的代碼。它爲我工作。請提出更改以獲得更好的性能。

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