英文:
Weird behaviour in JavaFX list view elements
问题
我有两个列表视图,右边的一个有三个“卡片”(名称和值),每个卡片都有一个按钮,点击该按钮将该卡片添加到中心列表。我还可以点击中心列表上该卡片的按钮来移除它。我的问题是,如果我向中心列表添加两个或更多卡片,然后尝试移除它们,界面会变得奇怪,无法删除它们或者只删除一个或两个。
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView<Card> rightList;
@FXML
private ListView<Card> centerList;
@Override
public void initialize(URL location, ResourceBundle resources) {
rightList.setCellFactory(param -> new ListCell<Card>() {
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setText(null);
} else {
VBox container = new VBox();
Button cardButton = new Button("Add");
cardButton.setOnAction(event -> {
if (!card.isMoved()) {
centerList.getItems().add(card);
card.setMoved(true);
}
});
container.getChildren().addAll(new javafx.scene.control.Label(card.toString()), cardButton);
setGraphic(container);
}
}
});
rightList.getItems().addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
centerList.setCellFactory(param -> new ListCell<Card>() {
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setText(null);
} else {
VBox container = new VBox();
Button cardButton = new Button("Remove");
cardButton.setOnAction(event -> {
card.setMoved(false);
centerList.getItems().remove(card);
});
container.getChildren().addAll(new javafx.scene.control.Label(card.toString()), cardButton);
setGraphic(container);
}
}
});
}
}
英文:
I have 2 list views, on the right one I have three "cards" (name and a value), each with a button which when clicked adds that card to the centerlist. I also can click on the same button of that card on the centerlist to remove that. My problem is if I add two or more card to the centerList and I proceed to remove them, the interface goes weird not deleting them or only one or two.
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView<Card> rightList;
@FXML
private ListView<Card> centerList;
@Override
public void initialize(URL location, ResourceBundle resources) {
rightList.setCellFactory(param -> new ListCell<Card>() {
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setText(null);
} else {
VBox container = new VBox();
Button cardButton = new Button("Add");
cardButton.setOnAction(event -> {
if (!card.isMoved()) {
centerList.getItems().add(card);
//rightList.getItems().remove(card);
card.setMoved(true);
}
});
container.getChildren().addAll(new javafx.scene.control.Label(card.toString()), cardButton);
setGraphic(container);
}
}
});
rightList.getItems().addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
centerList.setCellFactory(param -> new ListCell<Card>() {
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setText(null);
} else {
VBox container = new VBox();
Button cardButton = new Button("Remove");
cardButton.setOnAction(event -> {
card.setMoved(false);
centerList.getItems().remove(card);
});
container.getChildren().addAll(new javafx.scene.control.Label(card.toString()), cardButton);
setGraphic(container);
}
}
});
}
}
答案1
得分: 2
以下是您要翻译的内容:
If your list cell becomes non-empty, it's updateItem(...)
method will be called with a non-null Card
and a value of false
for empty
. In this case you set the graphic for that cell to the VBox
.
If, subsequently, the Card
instance displayed in that cell is removed from the ListView
and the cell becomes empty, then updateItem(null, true)
will be called. However your updateItem(...)
implementation in this case does not remove the graphic, so the graphic remains displayed.
Here is an implementation that should work. I refactored this to avoid repeatedly creating new controls (which is extremely inefficient) and also to remove unnecessary repetitive code.
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView
@FXML
private ListView
private static class DeckCell extends ListCell
private final VBox container;
private final Button cardButton ;
private final Label label;
DeckCell(String buttonText) {
label = new Label();
cardButton = new Button(buttonText);
container = new VBox(label, cardButton);
cardButton.setOnAction(e -> {
Card card = getItem();
if (card.isMoved()) {
centerList.getItems().remove(card);
} else {
centerList.getItems().add(card);
// rightList.getItems().remove(card);
}
card.setMoved(! card.isMoved());
});
}
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setGraphic(null);
} else {
label.setText(card.toString());
setGraphic(container);
}
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
rightList.setCellFactory(param -> new DeckCell("Add"));
rightList.getItems().addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
centerList.setCellFactory(param -> new DeckCell("Remove"));
}
}
Note that if you implement your Card
class using a JavaFX property to represent "moved", your action handler can just update the property and the lists can take care of themselves:
public class Card {
private final BooleanProperty moved = new SimpleBooleanProperty(false);
public BooleanProperty movedProperty() {
return moved ;
}
public final boolean isMoved() {
return movedProperty().get();
}
public final void setMoved(boolean moved) {
movedProperty().set(moved);
}
private final String name ;
private final int value ;
public Card(String name, int value) {
this.name = name ;
this.value = value ;
}
public String getName() {
return name ;
}
public int getValue() {
return value;
}
}
then
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView
@FXML
private ListView
private static class DeckCell extends ListCell
private final VBox container;
private final Button cardButton ;
private final Label label;
private final boolean movedValue;
DeckCell(String buttonText, boolean movedValue) {
this.movedValue = movedValue;
label = new Label();
cardButton = new Button(buttonText);
container = new VBox(label, cardButton);
cardButton.setOnAction(e -> {
Card card = getItem();
card.setMoved(movedValue);
});
}
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setGraphic(null);
} else {
label.setText(card.toString());
setGraphic(container);
}
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList
rightList.setItems(allCards);
rightList.setCellFactory(param -> new DeckCell("Add", true));
allCards.addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
// List that fires updates if any moved properties change:
centerList.setItems(new FilteredList<>(allCards, card::isMoved));
centerList.setCellFactory(param -> new DeckCell("Remove", false));
}
}
英文:
If your list cell becomes non-empty, it's updateItem(...)
method will be called with a non-null Card
and a value of false
for empty
. In this case you set the graphic for that cell to the VBox
.
If, subsequently, the Card
instance displayed in that cell is removed from the ListView
and the cell becomes empty, then updateItem(null, true)
will be called. However your updateItem(...)
implementation in this case does not remove the graphic, so the graphic remains displayed.
Here is an implementation that should work. I refactored this to avoid repeatedly creating new controls (which is extremely inefficient) and also to remove unnecessary repetitive code.
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView<Card> rightList;
@FXML
private ListView<Card> centerList;
private static class DeckCell extends ListCell<Card> {
private final VBox container;
private final Button cardButton ;
private final Label label;
DeckCell(String buttonText) {
label = new Label();
cardButton = new Button(buttonText);
container = new VBox(label, cardButton);
cardButton.setOnAction(e -> {
Card card = getItem();
if (card.isMoved()) {
centerList.getItems().remove(card);
} else {
centerList.getItems().add(card);
// rightList.getItems().remove(card);
}
card.setMoved(! card.isMoved());
});
}
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setGraphic(null);
} else {
label.setText(card.toString());
setGraphic(container);
}
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
rightList.setCellFactory(param -> new DeckCell("Add"));
rightList.getItems().addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
centerList.setCellFactory(param -> new DeckCell("Remove"));
}
}
Note that if you implement your Card
class using a JavaFX property to represent "moved", your action handler can just update the property and the lists can take care of themselves:
public class Card {
private final BooleanProperty moved = new SimpleBooleanProperty(false);
public BooleanProperty movedProperty() {
return moved ;
}
public final boolean isMoved() {
return movedProperty().get();
}
public final void setMoved(boolean moved) {
movedProperty().set(moved);
}
private final String name ;
private final int value ;
public Card(String name, int value) {
this.name = name ;
this.value = value ;
}
public String getName() {
return name ;
}
public int getValue() {
return value;
}
}
then
public class DeckManagement implements Initializable {
private final SceneHandler sceneHandler = SceneHandler.getInstance();
@FXML
private ListView<Card> rightList;
@FXML
private ListView<Card> centerList;
private static class DeckCell extends ListCell<Card> {
private final VBox container;
private final Button cardButton ;
private final Label label;
private final boolean movedValue;
DeckCell(String buttonText, boolean movedValue) {
this.movedValue = movedValue;
label = new Label();
cardButton = new Button(buttonText);
container = new VBox(label, cardButton);
cardButton.setOnAction(e -> {
Card card = getItem();
card.setMoved(movedValue);
});
}
@Override
protected void updateItem(Card card, boolean empty) {
super.updateItem(card, empty);
if (empty || card == null) {
setGraphic(null);
} else {
label.setText(card.toString());
setGraphic(container);
}
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<Card> allCards = FXCollections.observableArrayList(card -> new Observable[] { card.movedProperty() });
rightList.setItems(allCards);
rightList.setCellFactory(param -> new DeckCell("Add", true));
allCards.addAll(
new Card("Card 1", 10),
new Card("Card 2", 5),
new Card("Card 3", 8)
);
// List that fires updates if any moved properties change:
centerList.setItems(new FilteredList<>(allCards, card::isMoved));
centerList.setCellFactory(param -> new DeckCell("Remove", false));
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论