JavaFX ListView非常慢

时间:2014-11-24 21:17:00

标签: java listview javafx

我正在使用JavaFX为GUI创建聊天应用程序。我在ListView中显示聊天内容,但我有一个大问题 - 它非常慢。当我向列表中添加新项目时,尤其是当我向上/向下滚动列表时。我想也许这与列表刷新itsellf每次添加新项目(列表中的每个单元格!)以及每次向上/向下滚动时刷新的事实有关。 有人知道我该怎么做才能解决这个问题? TNX

我重写了ListCell的updateItem:

chatListView.setCellFactory(new Callback,ListCell>(){             @覆盖             public ListCell调用(ListView p){                 ListCell cell = new ListCell(){                     @覆盖                     protected void updateItem(UserInfo item,boolean bln){                         super.updateItem(item,bln);

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

3 个答案:

答案 0 :(得分:3)

  1. 从未updateItem()中添加(大)GUI元素,而不检查它是否已存在。

      当您以任何其他方式滚动,调整大小或更改gui时,每次都会为每条行调用
    1. updateItem()
    2. 如果您没有项目或updateItem(item, empty)的第二个布尔值为false,您应该总是将图形重置为null ,因为第二个布尔值是 EMPTY 旗帜。
  2. 我建议您使用VBox代替ListView

答案 1 :(得分:0)

每次更新视图时,都不得构建组件的新实例。

首先对它们进行一次初始化,然后重复使用并更改其属性。

答案 2 :(得分: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());

从可观察列表中自动更新列表(VBox的子节点)。

PS:其他功能如分组在这里=&gt; ObservableListHelper