简体   繁体   中英

JavaFX: how do you edit existing TableView entries

I have figured out how to create a TableView and how to add content to it but I am confused as to how to edit existing content.

In this app I have set it up so a person can add player names and certain stats to the tableview but I want to now be able to add a random roll and add the dex and other modifiers to it and populate the tableview with it. This would reside in the rollInitBtnClicked() module.

Please help me understand what I need to do.

This is my Main class.

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


public class Main extends Application {

TextField playerName;
TextField dexMod;
TextField otherMod;
TableView<Players> playersTable;
Button addPlayerBtn;
Button rollInitBtn;
Button delPlayerBtn;


public static void main(String[] args){

    launch(args);


}


@Override
public void start(Stage primaryStage) throws Exception {

    primaryStage.setTitle("Initiative");

    // TextField so the user can enter a players Name
    playerName = new TextField();
    playerName.setPrefSize(200, 20);
    playerName.setPromptText("Player Name");
    //playerName.setFocusTraversable(false);

    // TextField so the user can enter the players dex mod
    dexMod = new TextField();
    dexMod.setPrefSize(50, 20);
    dexMod.setPromptText("Dex");
    //dexMod.setFocusTraversable(false);

    // TextField so the user can enter the players other mods
    otherMod = new TextField();
    otherMod.setPrefSize(50, 20);
    otherMod.setPromptText("Other");
    //otherMod.setFocusTraversable(false);


    addPlayerBtn = new Button("Add Player"); 
    addPlayerBtn.setOnAction(e -> addPlayerBtnClicked());

    delPlayerBtn = new Button("Delete Player"); 
    delPlayerBtn.setOnAction(e -> delPlayerBtnClicked());

    rollInitBtn = new Button("Roll Initiative"); 
    rollInitBtn.setOnAction(e -> rollInitBtnClicked());



    //TableView Name Columns
    TableColumn<Players, String> nameColumn = new TableColumn<>("Name");
    nameColumn.setMinWidth(200);
    nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));

    //TableView dexMod Columns
    TableColumn<Players, String> dexColumn = new TableColumn<>("Dex Mod");
    dexColumn.setMinWidth(50);
    dexColumn.setStyle( "-fx-alignment: CENTER;");
    dexColumn.setCellValueFactory(new PropertyValueFactory<>("dexMod"));

    //TableView otherMod Columns
    TableColumn<Players, String> otherColumn = new TableColumn<>("Other Mods");
    otherColumn.setMinWidth(50);
    otherColumn.setStyle( "-fx-alignment: CENTER;");
    otherColumn.setCellValueFactory(new PropertyValueFactory<>("otherMod"));

    //TableView roll Columns
    TableColumn<Players, String> rollColumn = new TableColumn<>("Die Roll");
    rollColumn.setMinWidth(50);
    rollColumn.setStyle( "-fx-alignment: CENTER;");
    rollColumn.setCellValueFactory(new PropertyValueFactory<>("roll"));

    //TableView total Columns
    TableColumn<Players, String> totalColumn = new TableColumn<>("Total");
    totalColumn.setMinWidth(50);
    totalColumn.setStyle( "-fx-alignment: CENTER;");
    totalColumn.setCellValueFactory(new PropertyValueFactory<>("total"));



    playersTable = new TableView<>();
    playersTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    playersTable.setItems(getPlayers());
    playersTable.getColumns().addAll(nameColumn, dexColumn, otherColumn, rollColumn, totalColumn);



    HBox hLayout = new HBox();
    hLayout.setPadding(new Insets(10, 10, 10, 10));         // sets the padding on the top right left and bottom of the layout
    hLayout.setSpacing(10);                                 // sets the spacing between the TextFields and buttons
    hLayout.getChildren().addAll(playerName, dexMod, otherMod, addPlayerBtn, rollInitBtn, delPlayerBtn);


    VBox vLayout = new VBox();
    vLayout.getChildren().addAll(playersTable, hLayout);


    Scene mainScene = new Scene(vLayout, 800, 400);
    primaryStage.setScene(mainScene);
    primaryStage.show();

}


// What happens when the user clicks the Add Player button
public void addPlayerBtnClicked() {

    Players players = new Players();
    players.setName(playerName.getText());
    players.setDexMod(Integer.parseInt(dexMod.getText()));
    players.setOtherMod(Integer.parseInt(otherMod.getText()));
    playersTable.getItems().add(players);
    playerName.clear();
    dexMod.clear();
    otherMod.clear();
}

// What happens when the user clicks the Delete Player button
public void delPlayerBtnClicked() {

    ObservableList<Players> playerSelected, allPlayers;
    allPlayers = playersTable.getItems();
    playerSelected = playersTable.getSelectionModel().getSelectedItems();

    playerSelected.forEach(allPlayers::remove);



}

public void rollInitBtnClicked() {

    Players players = new Players();
    ObservableList<Players> playerSelected, allPlayers;
    allPlayers = playersTable.getItems();
    playerSelected = playersTable.getSelectionModel().getSelectedItems();

    players.setRoll((int)Math.ceil(Math.random() * 20));

}



public ObservableList<Players> getPlayers(){
    ObservableList<Players> players = FXCollections.observableArrayList();
    return players;
}

}

The following lives in a class named Players .

public class Players {

private String name;
private int dexMod;
private int otherMod;
private int roll;
private int total;


// default method to enter default data
public Players(){

}

// overloaded method to enter data that is handed to it
public Players(String name, int dexMod, int otherMod, int roll, int total){

    this.name = name;
    this.dexMod = dexMod;
    this.otherMod = otherMod;
    this.roll = roll;
    this.total = total;

}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getDexMod() {
    return dexMod;
}

public void setDexMod(int dexMod) {
    this.dexMod = dexMod;
}

public int getOtherMod() {
    return otherMod;
}

public void setOtherMod(int otherMod) {
    this.otherMod = otherMod;
}

public int getRoll() {
    return roll;
}

public void setRoll(int roll) {
    this.roll = roll;
}

public int getTotal() {
    return total;
}

public void setTotal(int total) {
    this.total = total;
}

}

You firstly need to mark the table as editable

playersTable.setEditable(true);

Also you need to change the cell factory for any editable columns.

nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());

for the integer type columns, you'll also need to specify a converter.

otherColumn.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
rollColumn.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
totalColumn.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));

and fix the declaration from <Players, String> to <Players, Integer> , ie

TableColumn<Players, Integer> totalColumn = new TableColumn<>("Total");

You should now be able to edit the table by either doing a "long click", or pressing F2, ie just like standard applications...

More information can be found in the official tutorial

For the TableView to be notified of modifications, you need to make a ObservableValue wrapping the property value available that is notified of modifications. PropertyValueFactory uses a method <propertyName>Property() to retrieve this ObservableValue . If no such method exists, it uses the getter; However in this case the TableView not notified of any modification. Furthermore the total property should not be modifiable on it's own.

Starting with JavaFX 8u60 there is a refresh method in TableView , which would allow you to manually trigger a refresh:

private final Random random = new Random();

public void rollInitBtnClicked() {
    List<Players> targets = playersTable.getItems();

    for (Players p : targets) {
        p.setRoll(random.nextInt(20));
        // set total ect...
    }

    // update table
    playersTable.refresh();
}

However the "standard approach that is also compatible with older versions of JavaFX would be modifying the properties:

private final IntegerProperty roll = new SimpleIntegerProperty();

public int getRoll() {
    return roll.get();
}

public void setRoll(int roll) {
    this.roll.set(roll);
}

public IntegerProperty rollProperty() {
    return this.roll;
}

// same modifications for other int properties
...

private final IntegerBinding total = Bindings.createIntegerBinding(() -> getRoll() + getOtherMod() + getDexMod(), roll, otherMod, dexMod);

public int getTotal() {
    return this.total.get();
}

public IntegerBinding totalProperty() {
    return this.total;
}

This would allow you to remove the refresh call from the rollInitBtnClicked method posted above.


Additional notes

  1. If you're using a class to store the data for a single player, it should not be named Players , but Player .
  2. You're using incorrect type parameters for your TableColumn s. This is not important here, since PropertyValueFactory works regardless of type parameters, but it could get you into problems, if you use other features of TableColumn . The second type parameter should always match the type parameter of the ObservableValue returned by the cellValueFactory :

     TableColumn<Players, String> nameColumn TableColumn<Players, Number> dexColumn TableColumn<Players, Number> otherColumn TableColumn<Players, Number> rollColumn TableColumn<Players, Number> totalColumn 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM