刷新javafx中的各个ListView项

时间:2017-01-18 10:04:42

标签: listview javafx refresh listener

HIHO,

我有一个让ListView更新“很好”的问题。这是一个包含一堆播放列表项的播放列表。基本上,当项目的样式或内容发生变化时,我希望它在ListView中进行更改。目前,我刷新了整个列表,我认为这可行,但对我来说它似乎是一个非常糟糕(不清楚)的解决方案(并且它闪烁)。有没有办法刷新/重绘特定项目?我一直都找不到。

作为参考,当发生以下情况时,需要更新每个项目:

  • 正在读取与该项目相关的文件,例如它失败;或者检索元数据。
  • 或;来自用户输入,例如更改当前歌曲时。

我能以某种方式使用听众吗?我看过绑定等,但我似乎找不到我想要的东西。任何帮助非常感谢

再次编辑:下面的工作代码

初始化清单:

protected Playlist(List<PlaylistItem> list){
    ...

    // initialise the items
    backup = FXCollections.observableArrayList(list);
    setItems(backup);

    // Use a custom CellFactory
    setCellFactory(new Callback<ListView<PlaylistItem>, ListCell<PlaylistItem>>() {
        @Override
        public ListCell<PlaylistItem> call(ListView<PlaylistItem> list) {
            return new PlaylistCell();
        }
    });

    ...
}

工厂创建的单元格:

private class PlaylistCell extends ListCell<PlaylistItem> {
    private PlaylistItem lastItem = null;
    private final BooleanProperty booleanProperty = new SimpleBooleanProperty(false);

    /** Add a listener to the boolean property upon construction */
    private PlaylistCell() {
        booleanProperty.addListener( new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if (newValue) { updateItem(lastItem, lastItem == null); };
            }
        });
    }

    @Override
    public void updateItem(PlaylistItem item, boolean empty) {
        super.updateItem(item, empty);
        booleanProperty.set(false);

        if (lastItem != item){
            // remove the pointer if we change item
            if (lastItem != null && booleanProperty == lastItem.getBooleanProperty())
                lastItem.setBooleanProperty(null);
            // and attach it to the new item
            if (item != null)
                item.setBooleanProperty(booleanProperty);
        }

        // redraw the cell
        if (!empty && item != null) {
            lastItem = item;

            // current song in bold
            if (item.equals(current)) {
                setId("current-item");
            } else{
                setId(null);
            }

            // mark queued songs & update text
            if (queue.contains(item)) {
                int i = queue.indexOf(item);
                super.setText(item.toString() + "\t (" + (i + 1) + (i != queue.lastIndexOf(item) ? ", ...)": ')'));
            } else {
                super.setText(item.toString());
            }
        }

        // draw an empty cell
        else {
            lastItem = null;
            setText(null);
            setId(null);
        }

    }

}

然后,当我双击某个项目时,或者当我排队项目时,我将BooleanProperty设置为true(即已更改)以触发更新调用。

1 个答案:

答案 0 :(得分:0)

假设queue是要播放的队列中的ObservableList<PlaylistItem>个项目,current是表示当前正在播放的项目的ObjectProperty<PlaylistItem>(或类似内容),我认为你只需要像

这样的东西
private class PlaylistCell extends ListCell<PlaylistItem> {

    public PlaylistCell() {

        textProperty().bind(Bindings.createStringBinding(this::calculateText,
            itemProperty(), emptyProperty(), current, queue);
        idProperty().bind(Bindings
            .when(current.isEqualTo(itemProperty())
            .then("current-item")
            .otherwise(""));
    }

    private String calculateText() {
        if (isEmpty() || getItem() == null) return null ;

        PlaylistItem item = getItem();

        if (queue.contains(item)) {
            int index = queue.indexOf(item) ;
            boolean unique = index == queue.lastIndexOf(item);
            return String.format("%s (%d%s)", item ,index + 1, (unique ? "" : ", ..."));
        } else {
            return item.toString();
        }
    }
}

我建议使用CSS PseudoClass而不是操纵id:

private class PlaylistCell extends ListCell<PlaylistItem> {

    private final BooleanBinding isCurrent = current.isEqualTo(itemProperty());
    private final PseudoClass currentPC = PseudoClass.getPseudoClass("current");

    public PlaylistCell() {

        textProperty().bind(Bindings.createStringBinding(this::calculateText,
            itemProperty(), emptyProperty(), current, queue);
        isCurrent.addListener((obs, wasCurrent, isNowCurrent) -> 
            pseudoClassStateChanged(currentPC, isNowCurrent));
    }

    // ...
}

然后更改CSS以使用选择器

.list-cell:current { /* ... */ }

而不是

#current-item { /* ... *. }

作为创建绑定的替代方法,您还可以使用extractor创建数据列表,这将导致在当前正在播放的项目或队列时调用通常的updateItem()方法,改变:

ObservableList<PlaylistItem> items = FXCollections.observableArrayList(
    item -> new Observable[] { current, queue /* , other properties the toString() depends on...*/ });

ListView<PlaylistItem> listView = new ListView<>();
listView.setItems(items);

然后

public class PlaylistCell extends ListCell<PlayListItem> {

    private final PseudoClass currentPC = PseudoClass.getPseudoClass("current");


    @Override
    protected void updateItem(PlaylistItem item, boolean empty) {

        pseudoClassStateChanged(currentPC, current.isEqualTo(item));

        if (empty) {
            setText(null);
        } else {
            if (queue.contains(item)) {
                int index = queue.indexOf(item) ;
                boolean unique = index == queue.lastIndexOf(item);
                return String.format("%s (%d%s)", item ,index + 1, (unique ? "" : ", ..."));
            } else {
                return item.toString();
            }
        }
    }
}