TableViewを利用して、基本的なデータの表示・編集・追加・削除を行なってみたいと思います。
TableViewへのデータの表示は、itemsPropertyに表示したいモデルのObservableListを設定し、データの追加・削除についてはそのObservableListにモデルを追加・削除することによって行います。
では、実際に以下のようなものを作成してみたいと思います。
Name, Gender, Email, Ageを持つUserを表示しています。[+]ボタンで新規にUserを追加し、[-]ボタンで選択されているUserを削除します。
まずは、表示するUserのモデルを以下のように定義します。
public interface UserModel { StringProperty nameProperty(); ObjectProperty<GenderModel> genderProperty(); StringProperty emailProperty(); IntegerProperty ageProperty(); }
GenderModelは以下のようになります。
public enum GenderModel { MALE, FEMALE; }
次に、このUserModelを表示するViewに対するモデルを以下のように定義します。
public interface UserListModel { ObjectProperty<ObservableList<UserModel>> usersProperty(); ObjectProperty<TableView.TableViewSelectionModel<UserModel>> userSelectionModelProperty(); void loadUsers(); void addNewUser(); void removeSelectedUser(); }
UserModelの読み込み・追加・削除を行うメソッドを定義しています。また、TableViewのselectionModelプロパティからTableViewで選択されている状態を取得したいので、それをバインドして取得できるようにuserSelectionModelプロパティを定義しています。
UserModelを表示するViewを以下のように定義します。
<fx:root xmlns:fx="http://javafx.com/fxml" type="javafx.scene.layout.HBox"> <TableView fx:id="userTableView" editable="true"> <columns> <TableColumn fx:id="nameTableColumn" text="Name" prefWidth="100"/> <TableColumn fx:id="genderTableColumn" text="Gender" prefWidth="80"/> <TableColumn fx:id="emailTableColumn" text="Email" prefWidth="160"/> <TableColumn fx:id="ageTableColumn" text="Age" prefWidth="60"/> </columns> </TableView> <VBox id="actionButtonRegion"> <Button fx:id="addUserButton" text="+" prefWidth="35"/> <Button fx:id="removeUserButton" text="-" prefWidth="35"/> </VBox> </fx:root>
UserModelを表示するTableViewと、UserModelの追加・削除のためのボタンを定義しています。
このViewのコントローラを以下のようにします。
public class UserListView extends HBox { public ObjectProperty<UserListModel> modelProperty() { return model; } private final ObjectProperty<UserListModel> model = new SimpleObjectProperty<UserListModel>(this, "model") { private UserListModel currentModel; @Override protected void invalidated() { UserListView.this.unbind(currentModel); currentModel = get(); UserListView.this.bind(currentModel); } }; public final UserListModel getModel() { return model.get(); } public final void setModel(UserListModel model) { this.model.set(model); } @FXML private TableView<UserModel> userTableView; @FXML private TableColumn<UserModel, String> nameTableColumn; @FXML private TableColumn<UserModel, GenderModel> genderTableColumn; @FXML private TableColumn<UserModel, String> emailTableColumn; @FXML private TableColumn<UserModel, Integer> ageTableColumn; @FXML private Button addUserButton; @FXML private Button removeUserButton; public UserListView() { getStyleClass().add("user-list-view"); initializeComponent(); } protected void initializeComponent() { FXController.of(this).fromDefaultLocation().load(); } protected void bind(UserListModel model) { if (model == null) { return; } userTableView.itemsProperty().bindBidirectional(model.usersProperty()); model.userSelectionModelProperty().bind(userTableView.selectionModelProperty()); } protected void unbind(UserListModel model) { if (model == null) { return; } userTableView.itemsProperty().unbindBidirectional(model.usersProperty()); model.userSelectionModelProperty().unbind(); } protected void addUser() { if (getModel() == null) { return; } getModel().addNewUser(); userTableView.requestFocus(); } protected void removeUser() { if (getModel() == null) { return; } getModel().removeSelectedUser(); userTableView.requestFocus(); } @FXML protected void initialize() { nameTableColumn.setCellValueFactory(new PropertyValueFactory<UserModel, String>("name")); nameTableColumn.setCellFactory(TextFieldTableCell.<UserModel>forTableColumn()); genderTableColumn.setCellValueFactory(new PropertyValueFactory<UserModel, GenderModel>("gender")); genderTableColumn.setCellFactory(ChoiceBoxTableCell.<UserModel, GenderModel>forTableColumn(GenderModel.values())); emailTableColumn.setCellValueFactory(new PropertyValueFactory<UserModel, String>("email")); emailTableColumn.setCellFactory(TextFieldTableCell.<UserModel>forTableColumn()); ageTableColumn.setCellValueFactory(new PropertyValueFactory<UserModel, Integer>("age")); ageTableColumn.setCellFactory(TextFieldTableCell.<UserModel, Integer>forTableColumn(new IntegerStringConverter())); addUserButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { addUser(); } }); removeUserButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { removeUser(); } }); } }
UserListModelを設定したときに、UserListModelで定義されているプロパティとTableViewのプロパティをバインドしています。[+]ボタンが押下されたときに、UserListModelのaddNewUserメソッドを呼び出してUserModelを追加し、[-]ボタンが押下されたときに、UserListModelのremoveSelectedUserメソッドを呼び出して選択されているUserModelを削除するようにしています。
以上で準備ができましたので、このUserListViewを表示していきます。
まずは、モデルを以下のように定義します。
public interface TableViewSimpleCRUDDemoModel { ObjectProperty<UserListModel> userListModelProperty(); void initialize(); }
次に、Viewを以下のように定義します。
<Scene xmlns:fx="http://javafx.com/fxml" fx:id="scene" width="640" height="480"> <stylesheets> <URL value="@TableViewSimpleCRUDDemoSceneStyle.css"/> </stylesheets> <StackPane> <UserListView fx:id="userListView"/> </StackPane> </Scene>
このViewのコントローラを以下のようにします。
public class TableViewSimpleCRUDDemoSceneController { public ObjectProperty<TableViewSimpleCRUDDemoModel> modelProperty() { return model; } private final ObjectProperty<TableViewSimpleCRUDDemoModel> model = new SimpleObjectProperty<TableViewSimpleCRUDDemoModel>(this, "model") { private TableViewSimpleCRUDDemoModel currentModel; @Override protected void invalidated() { TableViewSimpleCRUDDemoSceneController.this.unbind(currentModel); currentModel = get(); TableViewSimpleCRUDDemoSceneController.this.bind(currentModel); } }; public final TableViewSimpleCRUDDemoModel getModel() { return model.get(); } public final void setModel(TableViewSimpleCRUDDemoModel model) { this.model.set(model); } @FXML private Scene scene; @FXML private UserListView userListView; public void performOn(Stage stage) { stage.setScene(scene); stage.setTitle("Simple CRUD for Table View Demo"); stage.sizeToScene(); stage.centerOnScreen(); stage.show(); } public TableViewSimpleCRUDDemoSceneController with(TableViewSimpleCRUDDemoModel model) { setModel(model); if (model != null) { model.initialize(); } return this; } protected void bind(TableViewSimpleCRUDDemoModel model) { if (model == null) { return; } userListView.modelProperty().bindBidirectional(model.userListModelProperty()); } protected void unbind(TableViewSimpleCRUDDemoModel model) { if (model == null) { return; } userListView.modelProperty().unbindBidirectional(model.userListModelProperty()); } }
あとは、各モデルのデフォルトの実装を行っておきます。
まずは、UserModelのデフォルトの実装を以下のようにします。
public class DefaultUserModel implements UserModel { @Override public StringProperty nameProperty() { return name; } private final StringProperty name = new SimpleStringProperty(this, "name"); public final String getName() { return name.get(); } public final void setName(String name) { this.name.set(name); } @Override public ObjectProperty<GenderModel> genderProperty() { return gender; } private final ObjectProperty<GenderModel> gender = new SimpleObjectProperty<>(this, "gender"); public final GenderModel getGender() { return gender.get(); } public final void setGender(GenderModel gender) { this.gender.set(gender); } @Override public StringProperty emailProperty() { return email; } private final StringProperty email = new SimpleStringProperty(this, "email"); public final String getEmail() { return email.get(); } public final void setEmail(String email) { this.email.set(email); } @Override public IntegerProperty ageProperty() { return ageProperty; } private final IntegerProperty ageProperty = new SimpleIntegerProperty(this, "ageProperty"); public final int getAge() { return ageProperty.get(); } public final void setAge(int ageProperty) { this.ageProperty.set(ageProperty); } protected DefaultUserModel(String name, GenderModel gender, String email, int age) { setName(name); setGender(gender); setEmail(email); setAge(age); } public static DefaultUserModel asMale(String name, String email, int age) { return new DefaultUserModel(name, GenderModel.MALE, email, age); } public static DefaultUserModel asFemale(String name, String email, int age) { return new DefaultUserModel(name, GenderModel.FEMALE, email, age); } public static DefaultUserModel newUser() { return new DefaultUserModel("Unknown", GenderModel.MALE, "", 20); } }
次に、UserListModelのデフォルトの実装を以下のようにします。
public class DefaultUserListModel implements UserListModel { @Override public ObjectProperty<ObservableList<UserModel>> usersProperty() { return users; } private final ObjectProperty<ObservableList<UserModel>> users = new SimpleObjectProperty<>(this, "users"); public final ObservableList<UserModel> getUsers() { return users.get(); } public final void setUsers(ObservableList<UserModel> users) { this.users.set(users); } @Override public ObjectProperty<TableView.TableViewSelectionModel<UserModel>> userSelectionModelProperty() { return userSelectionModel; } private final ObjectProperty<TableView.TableViewSelectionModel<UserModel>> userSelectionModel = new SimpleObjectProperty<>(this, "userSelectionModel"); public final TableView.TableViewSelectionModel<UserModel> getUserSelectionModel() { return userSelectionModel.get(); } public final void setUserSelectionModel(TableView.TableViewSelectionModel<UserModel> userSelectionModel) { this.userSelectionModel.set(userSelectionModel); } @Override public void loadUsers() { setUsers( FXCollections.<UserModel>observableArrayList( DefaultUserModel.asMale("User 1", "user1@email.com", 25), DefaultUserModel.asFemale("User 2", "user2@email.com", 28), DefaultUserModel.asFemale("User 3", "user3@email.com", 24), DefaultUserModel.asMale("User 4", "user4@email.com", 21), DefaultUserModel.asFemale("User 5", "user5@email.com", 26) ) ); } @Override public void addNewUser() { if (getUsers() == null) { setUsers(FXCollections.<UserModel>observableArrayList()); } getUsers().add(DefaultUserModel.newUser()); if (getUserSelectionModel() != null) { getUserSelectionModel().selectLast(); } } @Override public void removeSelectedUser() { if (getUsers() == null) { return; } if (getUserSelectionModel() == null) { return; } getUsers().remove(getUserSelectionModel().getSelectedItem()); } }
loadUsersでは、サンプルデータとして5件のUserModelを設定しています。
最後に、TableViewSimpleCRUDDemoModelのデフォルトの実装を以下のようにします。
public class DefaultTableViewSimpleCRUDDemoModel implements TableViewSimpleCRUDDemoModel { @Override public ObjectProperty<UserListModel> userListModelProperty() { return userListModel; } private final ObjectProperty<UserListModel> userListModel = new SimpleObjectProperty<>(this, "userListModel"); public final UserListModel getUserListModel() { return userListModel.get(); } public final void setUserListModel(UserListModel userListModel) { this.userListModel.set(userListModel); } @Override public void initialize() { setUserListModel(new DefaultUserListModel()); getUserListModel().loadUsers(); } }
以上ですべての実装が完了しましたので、Applicationクラスを以下のようにして実行します。
public class TableViewSimpleCRUDDemo extends Application { @Override public void start(Stage primaryStage) throws Exception { FXController.of(new TableViewSimpleCRUDDemoSceneController()) .fromDefaultLocation() .load() .with(new DefaultTableViewSimpleCRUDDemoModel()) .performOn(primaryStage); } public static void main(String... args) { launch(args); } }
TableViewを利用して、基本的なデータの表示・編集・追加・削除を行なってみました。データの操作はモデル側で行い、Viewへの表示については、モデルのプロパティをViewのプロパティにバインドすることで行うことになります。