TableView

时间:2018-01-23 18:17:36

标签: java javafx radio-button tableview

我的问题是关于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都会按预期显示为已选中或未选中,就像错误消失一样。

你能帮我理解发生了什么以及如何解决这些错误吗?

非常感谢

2 个答案:

答案 0 :(得分:4)

示例代码

这是一个有效的表实现。我想你可以将它与你的实现进行比较,看看有什么不同之处。可能是主要的&#34;修复&#34;是updateItem处理空值和空值。

pic

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发布时,我几乎完成了它,所以我认为我会继续发布它。它类似但两者之间的差异可能很有用。)

我能看到的代码中的主要问题是:

  1. 每次调用时,您的单元格值工厂都会返回一个新的BooleanProperty。这意味着单元格无法观察到正确的属性,并且在值发生变化时无法自行更新。您应该在模型类中使用JavaFX属性,以便单元格可以观察单个属性。
  2. 您的单元格实现不会调用updateItem(...)的超类实现。这意味着基本功能不会发生,因此在需要时可能会或可能不会发生更新,并且选择不会起作用等。
  3. 您的单元格实现并不处理空单元格,因此这些单元格无法正确显示。
  4. 在这个实现中,我使用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);
        }
    }