Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
483 views
in Technique[技术] by (71.8m points)

javafx - Custom ListCell implements InvalidationListener, repaint components

I have a custom ListCell implemented that contains a BorderPane layout with some components.

The cell registers itself to the item. So when the duration of the item changes the invalidated method is called.

In this method I set the text of the duration label. My problem is now the method is called but the label is not repainted.

I think if setText is called the cell should repaint. It is possible to manually repaint the cell or the Label.?

public static class ListItemCell extends ListCell<MusicListItem> implements InvalidationListener{

    private AnchorPane listItem;
    private Label artist;
    private Label title;
    private Label duration;
    private BorderPane borderPane;
    private FlowPane flowPane;

    public ListItemCell() {
        initCellLayout();
    }

    public ListItemCell(final LogicInterfaceFX logic) {
        ...
    }

    public void initCellLayout() {
        try {
            this.listItem = (AnchorPane) FXMLLoader.load(getClass().getResource("/de/roth/jsona/view/themes/" + Config.getInstance().THEME + "/" + "layout_list_item.fxml"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        this.borderPane = (BorderPane) listItem.getChildren().get(0);
        this.flowPane = (FlowPane) borderPane.getLeft();
        this.artist = (Label) flowPane.getChildren().get(0);
        this.artist.getStyleClass().add(defaultTextClass);
        this.title = (Label) flowPane.getChildren().get(1);
        this.title.getStyleClass().add(defaultTextClass);
        this.duration = (Label) borderPane.getRight();
        this.duration.getStyleClass().add(defaultTextClass);

        this.setGraphic(listItem);
    }

    @Override
    public void updateItem(MusicListItem item, boolean empty) {
        super.updateItem(item, empty);

        if (!empty && item != null) {
            item.addListener(this);
            item.durationProperty().addListener(this);

            // Duration
            this.duration.setText(item.getDuration());

            // Artist / Title
            if (item.getArtist() != null) {
                this.artist.setText(item.getArtist());
                this.title.setText(" - " + item.getTitle());
            } else {
                this.artist.setText("");
                this.title.setText(item.getFile().getName());
            }
        } else {
            this.artist.setText("");
            this.title.setText("");
            this.duration.setText("");
        }

    }

    @Override
    public void invalidated(Observable observable) {
        System.out.println("INVALIDATE!!!" + getItem().getFile().getAbsolutePath());
        this.duration.setText(getItem().getDuration());
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have a bug in there: you need to make sure you remove listeners from old items when the item is updated. Remember that ListCells are reused, so updateItem(...) is called multiple times during the lifespan of your ListView.

I don't know if that's what is causing it to fail to update. This works for me:

import java.util.Random;

import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class ListViewUpdatableProperties extends Application {

    @Override
    public void start(Stage primaryStage) {
        final ListView<Item> listView = new ListView<>();
        listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        final Random rng = new Random();
        for (int i=1; i<=20; i++) {
            listView.getItems().add(new Item("Item "+i, rng.nextInt(100)));
        }
        BorderPane root = new BorderPane();
        root.setCenter(listView);

        listView.setCellFactory(new Callback<ListView<Item>, ListCell<Item>>() {

            @Override
            public ListCell<Item> call(ListView<Item> param) {
                return new ItemListCell();
            }

        });

        HBox controls = new HBox();
        controls.setPadding(new Insets(5));
        Button incButton = new Button("Increment selected");
        incButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                for (Item item : listView.getSelectionModel().getSelectedItems()) {
                    item.increment();
                }
            }

        });
        controls.getChildren().add(incButton);

        root.setBottom(controls);
        Scene scene = new Scene(root, 250, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class ItemListCell extends ListCell<Item> implements InvalidationListener {

        private final HBox hbox ;
        private final Label nameLabel ;
        private final Label valueLabel ;

        public ItemListCell() {
            hbox = new HBox(5);
            nameLabel = new Label();
            valueLabel = new Label();
            hbox.getChildren().addAll(nameLabel, valueLabel);
            setGraphic(hbox);
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        }

        @Override
        public void updateItem(Item item, boolean empty) {
            Item oldItem = getItem();
            if (oldItem != null) {
                oldItem.valueProperty().removeListener(this);
            }
            super.updateItem(item, empty);
            if (item != null) {
                nameLabel.setText(item.getName());
                valueLabel.setText(String.valueOf(item.getValue()));
                item.valueProperty().addListener(this);
            } else {
                nameLabel.setText("");
                valueLabel.setText("");
            }

        }

        @Override
        public void invalidated(Observable observable) {
            final int value = getItem().getValue();
            System.out.println("Invalidated: item is "+getItem().getName() + " with value "+value);
            valueLabel.setText(String.valueOf(value));
        }

    }

    public static class Item {
        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

        private final StringProperty name = new SimpleStringProperty(this, "name");
        public StringProperty nameProperty() {
            return name ;
        }
        public String getName() {
            return name.get();
        }
        public void setName(String name) {
            this.name.set(name);
        }

        private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
        public IntegerProperty valueProperty() {
            return value ;
        }
        public int getValue() {
            return value.get();
        }
        public void setValue(int value) {
            this.value.set(value);
        }

        public void increment() {
            value.set(value.get()+1);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

As stated in the other answer, there is no repaint() method in JavaFX. If you wire things up correctly, when the properties are invalidated, it will know to repaint.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...