首页 文章

JavaFX ListView非常慢

提问于
浏览
1

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

我重写ListCell的updateItem:

chatListView.setCellFactory(new Callback,ListCell>(){@ Overver public ListCell call(ListView p){ListCell cell = new ListCell(){@ Override 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 回答

  • 3

    1.) Never everupdateItem() 中添加(大)GUI元素,而不检查它是否已经存在 .
    1.1)当你以任何其他方式滚动,调整大小或改变gui时,每次为 EVERY SINGLE ROW 调用 updateItem() .
    1.2)如果你没有项目或 updateItem(item, empty) 的第二个布尔值为false,你应该总是 reset the graphic to null ,因为第二个布尔值是 EMPTY 标志 .

    2.)我建议您使用VBox而不是ListView . ;-)

    快乐的编码,
    Kalasch

  • 0

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

    最初对它们进行一次初始化,然后重用并更改其属性 .

  • 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:其他功能如分组在这里=> ObservableListHelper

相关问题