我的问题是关于tableView中的一个奇怪的行为。 目的是在tableView中显示参与比赛的球员列表。显示的信息包括玩家的名字,他的分数,他连续的半身像数以及一个知道轮到他玩的指标。
这个指标是RadioButton,因为它看起来比checkBox好。当转向玩家时,RadioButton将被设置为选择(true),否则,它将被设置为选择(假)。真实或虚假信息由tableView中使用的玩家信息给出。当然,RadioButton处于“只读”模式。
这是我的tableView代码:
TableView<PlayerProgressInformations> playersProgressTable = new TableView<PlayerProgressInformations>();
defineColumns(playersProgressTable);
playersProgressTable.setItems(playersAtThisTable);
用于defineColumns方法:
TableColumn<PlayerProgressInformations, Boolean> colPlaying = new TableColumn<PlayerProgressInformations, Boolean>("Tour");
colPlaying.setPrefWidth(70);
TableCell<PlayerProgressInformations, Boolean>>) new RadioButton());
colPlaying.setCellValueFactory(new Callback<CellDataFeatures<PlayerProgressInformations, Boolean>, ObservableValue<Boolean>>() {
public ObservableValue<Boolean> call(CellDataFeatures<PlayerProgressInformations, Boolean> p) {
return new SimpleBooleanProperty(p.getValue().isPlaying());
}
});
colPlaying.setCellFactory(new Callback<TableColumn<PlayerProgressInformations, Boolean>, TableCell<PlayerProgressInformations, Boolean>>() {
@Override
public TableCell<PlayerProgressInformations, Boolean> call( TableColumn<PlayerProgressInformations, Boolean> param) {
RadioButtonCell<PlayerProgressInformations, Boolean> radioButtonCell =
new RadioButtonCell<PlayerProgressInformations, Boolean>();
return radioButtonCell;
}
});
RadioButtoCell类:
private class RadioButtonCell<S, T> extends TableCell<S,T> {
public RadioButtonCell () {
}
@Override
protected void updateItem (T item, boolean empty) {
System.out.println("Count value : "+count); //Indicator to check how many times is used the method "updateItem"
count+=1;
if (item instanceof Boolean) {
Boolean myBoolean = (Boolean) item;
if (!empty) {
System.out.println("Valeur du boolean : "+item);
RadioButton radioButton = new RadioButton();
radioButton.setDisable(true);
radioButton.setSelected(myBoolean);
radioButton.setStyle("-fx-opacity: 1");
setGraphic(radioButton);
}
}
}
}
奇怪的行为是下面的行为:
问题1:当我将第一个玩家加入游戏桌时,updateItem方法被调用17次。 如果第二个加入,则第一个玩家的这个数字增加到57,或者第二个玩家增加到60和17。 最后,如果第三个加入,第一个玩家为90次,第二个玩家为57或60,第三个玩家为17。 为什么这种方法经常被称为?为什么那些特定的数字? 更重要的是,在这个“初始化”之后,每次转弯后方法被调用的次数超出我的预期:一次取消选择一个RadioButton,一次选择下一个。
问题2:当第一个玩家加入桌子时,他当然是第一个玩的人,在他的屏幕上,选择了RadioButton。 当第二个玩家加入桌子时,第二个玩家看到为第一个玩家选择的RadioButton和为他自己未选择的RadioButton。这是预期的行为。但对于第一个播放器,2个RadioButtons未被选中。 如果第三名玩家加入桌面,他会看到RadioButton被选为第一名玩家并且未被选中为自己和第二名玩家。这也是预期的结果。但是,对于第二个和第一个玩家,所有3个RadioButton都未被选中。 为何这种奇怪的行为?更重要的是,在第一次转弯之后,所有RadioButton都会按预期显示为已选中或未选中,就像错误消失一样。
你能帮我理解发生了什么以及如何解决这些错误吗?
非常感谢
答案 0 :(得分:4)
示例代码
这是一个有效的表实现。我想你可以将它与你的实现进行比较,看看有什么不同之处。可能是主要的&#34;修复&#34;是updateItem
处理空值和空值。
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class PlayerViewer extends Application {
private final ObservableList<Player> data =
FXCollections.observableArrayList(
new Player("Jacob", true),
new Player("Isabella", false),
new Player("Ethan", true)
);
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
TableView<Player> table = new TableView<>(data);
table.setPrefHeight(130);
table.setPrefWidth(150);
TableColumn<Player, String> handleCol = new TableColumn<>("Handle");
handleCol.setCellValueFactory(new PropertyValueFactory<>("handle"));
table.getColumns().add(handleCol);
TableColumn<Player, Boolean> playingCol = new TableColumn<>("Playing");
playingCol.setCellValueFactory(new PropertyValueFactory<>("playing"));
playingCol.setCellFactory(param -> new TableCell<>() {
RadioButton indicator = new RadioButton();
{
indicator.setDisable(true);
indicator.setStyle("-fx-opacity: 1");
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
@Override
protected void updateItem(Boolean isPlaying, boolean empty) {
super.updateItem(isPlaying, empty);
if (empty || isPlaying == null) {
setGraphic(null);
} else {
indicator.setSelected(isPlaying);
setGraphic(indicator);
}
}
});
table.getColumns().add(playingCol);
stage.setScene(new Scene(table));
stage.show();
}
public static class Player {
private final SimpleStringProperty handle;
private final SimpleBooleanProperty playing;
private Player(String handle, boolean playing) {
this.handle = new SimpleStringProperty(handle);
this.playing = new SimpleBooleanProperty(playing);
}
public SimpleStringProperty handleProperty() {
return handle;
}
public String getHandle() {
return handle.get();
}
public void setHandle(String handle) {
this.handle.set(handle);
}
public SimpleBooleanProperty playingProperty() {
return playing;
}
public boolean isPlaying() {
return playing.get();
}
public void setPlaying(boolean playing) {
this.playing.set(playing);
}
}
}
对您提问的其他评论
在问题1&#34;中,调用updateItem的次数,这是一个内部工具包的事情,你的代码不应该真的关心它,它只需要制作确保无论何时调用它,它都做对了。
关于你的问题2&#34;,关于多个视图的多个视图的交互,谁知道?没有进一步的额外代码就不可能说,这可能最终会使这个问题过于宽泛。如果您有关于如何处理多个玩家的显示交互的特定问题,您需要扩展并澄清您的问题(可能是mcve的新问题。)
对于您的实现,我建议重新设置单选按钮(通过CSS),这样它看起来不像标准的用户可选单选按钮(因为您已禁用选择功能,然后删除了默认禁用)不透明度设置)。或者,您可以使用自定义指标控件,例如this answer中的Bulb类,这可能是首选。
答案 1 :(得分:3)
这是另一个有效的样本。 (当@jewelsea发布时,我几乎完成了它,所以我认为我会继续发布它。它类似但两者之间的差异可能很有用。)
我能看到的代码中的主要问题是:
BooleanProperty
。这意味着单元格无法观察到正确的属性,并且在值发生变化时无法自行更新。您应该在模型类中使用JavaFX属性,以便单元格可以观察单个属性。updateItem(...)
的超类实现。这意味着基本功能不会发生,因此在需要时可能会或可能不会发生更新,并且选择不会起作用等。在这个实现中,我使用Game
类来保持&#34;当前播放器&#34;并为每个玩家提供了对(相同)游戏实例的引用。 playing
中的Player
属性作为只读属性公开,其值绑定到游戏的当前播放器。这意味着只有一个玩家可以拥有playing==true
,而没有大量的&#34;布线&#34;球员之间。
按钮允许测试添加新玩家(自动设置为&#34;当前&#34;玩家)或将当前玩家移动到下一个玩家。
import java.util.Iterator;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class PlayerTable extends Application {
@Override
public void start(Stage primaryStage) {
TableView<Player> table = new TableView<>();
TableColumn<Player, String> playerCol = new TableColumn<>("Name");
playerCol.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getName()));
TableColumn<Player, Boolean> playingColumn = new TableColumn<>("Playing");
playingColumn.setCellValueFactory(cellData -> cellData.getValue().playingProperty());
playingColumn.setCellFactory(tc -> new RadioButtonTableCell<>());
table.getColumns().add(playerCol);
table.getColumns().add(playingColumn);
Game game = new Game();
Button newPlayerButton = new Button("New Player");
newPlayerButton.setOnAction(e -> addNewPlayer(game, table.getItems()));
Button nextPlayerButton = new Button("Next player");
nextPlayerButton.setOnAction(e -> selectNextPlayer(game, table.getItems()));
HBox controls = new HBox(5, newPlayerButton, nextPlayerButton);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(5));
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(controls);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private void addNewPlayer(Game game, List<Player> players) {
int playerNumber = players.size() + 1 ;
Player newPlayer = new Player(game, "Player "+playerNumber);
game.setCurrentPlayer(newPlayer);
players.add(newPlayer);
}
private void selectNextPlayer(Game game, List<Player> players) {
if (players.isEmpty()) return ;
for (Iterator<Player> i = players.iterator() ; i.hasNext() ;) {
if (i.next() == game.getCurrentPlayer()) {
if (i.hasNext()) {
game.setCurrentPlayer(i.next());
} else {
game.setCurrentPlayer(players.get(0));
}
return ;
}
}
game.setCurrentPlayer(players.get(0));
}
public static class RadioButtonTableCell<S> extends TableCell<S, Boolean> {
private RadioButton radioButton ;
public RadioButtonTableCell() {
radioButton = new RadioButton();
radioButton.setDisable(true);
}
@Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
radioButton.setSelected(item);
setGraphic(radioButton);
}
}
};
public static class Game {
private final ObjectProperty<Player> currentPlayer = new SimpleObjectProperty<>() ;
public ObjectProperty<Player> currentPlayerProperty() {
return currentPlayer ;
}
public Player getCurrentPlayer() {
return currentPlayerProperty().get();
}
public void setCurrentPlayer(Player player) {
currentPlayerProperty().set(player);
}
}
public static class Player {
private final String name ;
private final ReadOnlyBooleanWrapper playing ;
public Player(Game game, String name) {
this.name = name ;
playing = new ReadOnlyBooleanWrapper() ;
playing.bind(game.currentPlayerProperty().isEqualTo(this));
}
public String getName() {
return name ;
}
public ReadOnlyBooleanProperty playingProperty() {
return playing.getReadOnlyProperty() ;
}
public boolean isPlaying() {
return playingProperty().get();
}
}
public static void main(String[] args) {
launch(args);
}
}