2014-11-24 193 views
1

我正在使用JavaFX作爲GUI的聊天應用程序。我在ListView中顯示聊天內容,但是我遇到了一個很大的問題 - 它非常慢。當我將新項目添加到列表中時,特別是當我向上/向下滾動列表時。我認爲也許它與這樣一個事實有關,即每次添加新項目(列表中的每個單元格)時,列表都會刷新它,並且每次向上/向下滾動時都會刷新。 有人知道我能做些什麼來解決這個問題嗎? TNXJavaFX ListView非常慢

我重寫的ListCell的的updateItem:

chatListView.setCellFactory(新的回調,的ListCell>(){ @覆蓋 公衆的ListCell調用(ListView控件P){ 的ListCell電池=新的ListCell(){ @Override 保護無效的updateItem(的UserInfo項,布爾億){ super.updateItem(項目,億);

    if (item != null) { 
         BorderPane borderPane = new BorderPane(); 
         ImageView profileImage = new ImageView(new Image(item.getImageURL())); 
         profileImage.setFitHeight(32); 
         profileImage.setFitWidth(32); 

         Rectangle clip = new Rectangle(
           profileImage.getFitWidth(), profileImage.getFitHeight() 
         ); 
         clip.setArcWidth(30); 
         clip.setArcHeight(30); 
         profileImage.setClip(clip); 
         SnapshotParameters parameters = new SnapshotParameters(); 
         parameters.setFill(Color.TRANSPARENT); 
         WritableImage image = profileImage.snapshot(parameters, null); 
         profileImage.setClip(null); 
         profileImage.setImage(image); 

         ImageView arrowImage = new ImageView(new Image("arrow1.png")); 
         ImageView arrowImage2 = new ImageView(new Image("arrow1.png")); 
         Label nameLabel = new Label(item.getUserName()); 
         nameLabel.setStyle(" -fx-text-alignment: center; -fx-padding: 2;"); 

         HBox hbox = null; 
         Label textLabel = new Label(); 
         String messageText = splitTolines(item.getMessage()); 
         textLabel.setText(messageText); 
         textLabel.setStyle("-fx-background-color: #a1f2cd; " 
           + "-fx-padding: 10;\n" 
           + "-fx-spacing: 5;"); 
         hbox = new HBox(arrowImage, textLabel); 

         VBox vbox = new VBox(profileImage, nameLabel); 
         BorderPane.setMargin(vbox, new Insets(0, 10, 10, 10)); 
         BorderPane.setMargin(hbox, new Insets(10, 0, 0, 0)); 

         //Time 
         Date dNow = new Date(); 
         SimpleDateFormat ft = new SimpleDateFormat("hh:mm a"); 
         Label timeLabel = new Label(ft.format(dNow)); 
         timeLabel.setStyle("-fx-font: 8px Tahoma; -fx-width: 100%"); 

         HBox hbox2 = new HBox(arrowImage2, timeLabel); 
         arrowImage2.setVisible(false); 
         VBox vbox2 = new VBox(hbox, hbox2); 

         borderPane.setCenter(vbox2); 
         borderPane.setLeft(vbox); 
         setGraphic(borderPane); 
        } 
       } 
      }; 

      return cell; 
     } 
    }); 
+0

你應該包括[MCVE](http://stackoverflow.com/help/mcve),否則你的問題是有效地無法回答。 – jewelsea 2014-11-24 22:45:03

+0

是的,請提供示例代碼。你重寫ListCell的'updateItem'方法嗎?你在那裏做什麼昂貴的東西? [Flowless](https://github.com/TomasMikula/Flowless)可能會或可能不會幫助你的情況。 – 2014-11-25 01:01:18

+0

我添加了一些代碼^ – Yael 2014-11-26 14:01:49

回答

2

1)永遠不要updateItem()中添加(大)GUI元素,而不檢查它是否已經存在。
1.1)updateItem()每次調用EVERY SINGLE ROW當您以任何其他方式滾動,調整大小或更改gui時。
1.2)你應該送花兒給人重置圖形爲空,如果你沒有一個項目或updateItem(item, empty)第二布爾是假的,因爲第二個布爾是EMPTY標誌。

2.)我建議你使用VBox而不是ListView。 ;-)

編碼快樂,
Kalasch

+0

我接受了您的建議並將其更改爲VBox - 非常棒!謝謝! – Yael 2014-12-03 22:27:51

0

每次視圖被更新你不能建立你的組件的新實例。

Instanciate他們一次initialy,然後你重用和改變他們的屬性。

0

我剛剛也注意到了。即使僅包含5-10個項目(縮放圖像和文本)的列表也很慢。因爲我不需要選擇功能,所以我也重寫了代碼來使用VBox,並且速度立刻消失了!

爲了模擬setItems,我有一個輔助函數,你可能會覺得得心應手:

public static <S, T> void mapByValue(
     ObservableList<S> sourceList, 
     ObservableList<T> targetList, 
     Function<S, T> mapper) 
{ 
    Objects.requireNonNull(sourceList); 
    Objects.requireNonNull(targetList); 
    Objects.requireNonNull(mapper); 
    targetList.clear(); 
    Map<S, T> sourceToTargetMap = new HashMap<>(); 
    // Populate targetList by sourceList and mapper 
    for (S s : sourceList) 
    { 
     T t = mapper.apply(s); 
     targetList.add(t); 
     sourceToTargetMap.put(s, t); 
    } 
    // Listen to changes in sourceList and update targetList accordingly 
    ListChangeListener<S> sourceListener = new ListChangeListener<S>() 
     { 
      @Override 
      public void onChanged(ListChangeListener.Change<? extends S> c) 
      { 
       while (c.next()) 
       { 
        if (c.wasPermutated()) 
        { 
         for (int i = c.getFrom(); i < c.getTo(); i++) 
         { 
          int j = c.getPermutation(i); 
          S s = sourceList.get(j); 
          T t = sourceToTargetMap.get2(s); 
          targetList.set(i, t); 
         } 
        } 
        else 
        { 
         for (S s : c.getRemoved()) 
         { 
          T t = sourceToTargetMap.get2(s); 
          targetList.remove2(t); 
          sourceToTargetMap.remove2(s); 
         } 
         int i = c.getFrom(); 
         for (S s : c.getAddedSubList()) 
         { 
          T t = mapper.apply(s); 
          targetList.add(i, t); 
          sourceToTargetMap.put(s, t); 
          i += 1; 
         } 
        } 
       } 
      } 
     }; 
    sourceList.addListener(new WeakListChangeListener<>(sourceListener)); 
    // Store the listener in targetList to prevent GC 
    // The listener should be active as long as targetList exists 
    targetList.addListener((InvalidationListener) iv -> 
     { 
      Object[] refs = { sourceListener, }; 
      Objects.requireNonNull(refs); 
     }); 
} 

然後它可以用於像:

ObservableList<Bookmark> bookmarkList; 
VBox bookmarkListVBox; 
mapByValue(bookmarkList, bookmarkListVBox.getChildren(), bmk -> new Label(bmk.getName()); 

要自動更新列表(垂直框的孩子)從可觀察的名單。

PS:其它功能,例如分組的位置=>ObservableListHelper