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のプロパティにバインドすることで行うことになります。