gpt4 book ai didi

tableview - JavaFX 自定义 MasterDetail Pane

转载 作者:行者123 更新时间:2023-12-03 09:13:03 25 4
gpt4 key购买 nike

我为我的项目创建了一个自定义的“主从” Pane ,其中我使用了一个拆分 Pane ,每个 Pane 中都有两个锚定 Pane 。其中一个是 TableView,其中填充了 Users (ObservableList)。在每一行(用户)上,我都实现了一个 ChangeListener ,如下所示:

table.getSelectionModel().selectedItemProperty().addListener(listElementChangeListener());

选择该行后,我会为 DetailPane 传递 UserObject,并在 TextFields 中可视化 User 数据> 详细信息。我已经实现了控件,以了解 User 是否在 Detail 中进行修改,如果是,我想阻止 TableView 中的行更改>.

当我修改用户时,我尝试从 TableView 中删除 ChangeListener,但效果不佳。

我正在考虑一个解决方案,例如设置焦点并将其保持在该行上,直到取消或保存修改的 User

有什么好的解决办法吗?

最佳答案

我可能会以不同的方式处理这个问题。我会将“详细 View ”中的控件双向绑定(bind)到 User 对象中的属性。这样,当用户编辑它们时,它们将在对象(和表)中更新。如果您愿意,还可以提供“取消”按钮以恢复到以前的值。

这是使用此方法的完整解决方案:

用户.java:

package usermasterdetail;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class User {

private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final BooleanProperty admin = new SimpleBooleanProperty();

public User(String firstName, String lastName, boolean admin) {
setFirstName(firstName);
setLastName(lastName);
setAdmin(admin);
}

public final StringProperty firstNameProperty() {
return this.firstName;
}


public final String getFirstName() {
return this.firstNameProperty().get();
}


public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}


public final StringProperty lastNameProperty() {
return this.lastName;
}


public final String getLastName() {
return this.lastNameProperty().get();
}


public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}


public final BooleanProperty adminProperty() {
return this.admin;
}


public final boolean isAdmin() {
return this.adminProperty().get();
}


public final void setAdmin(final boolean admin) {
this.adminProperty().set(admin);
}

}

DataModel.java:

package usermasterdetail;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class DataModel {

private final ObservableList<User> userList = FXCollections.observableArrayList(
new User("Jacob", "Smith", false),
new User("Isabella", "Johnson", true),
new User("Ethan", "Williams", false),
new User("Emma", "Jones", true),
new User("Michael", "Brown", true)
);

private final ObjectProperty<User> currentUser = new SimpleObjectProperty<>();

public final ObjectProperty<User> currentUserProperty() {
return this.currentUser;
}


public final User getCurrentUser() {
return this.currentUserProperty().get();
}


public final void setCurrentUser(final User currentUser) {
this.currentUserProperty().set(currentUser);
}


public ObservableList<User> getUserList() {
return userList;
}

}

TableController.java:

package usermasterdetail;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;

public class TableController {

@FXML
private TableView<User> table ;
@FXML
private TableColumn<User, String> firstNameColumn ;
@FXML
private TableColumn<User, String> lastNameColumn ;
@FXML
private TableColumn<User, Boolean> adminColumn ;

private DataModel model ;

public void initialize() {
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
adminColumn.setCellValueFactory(cellData -> cellData.getValue().adminProperty());
adminColumn.setCellFactory(CheckBoxTableCell.forTableColumn(adminColumn));
}

public void setDataModel(DataModel dataModel) {
if (model != null) {
model.currentUserProperty().unbind();
}
this.model = dataModel ;
dataModel.currentUserProperty().bind(table.getSelectionModel().selectedItemProperty());
table.setItems(model.getUserList());
}
}

UserEditorController.java:

package usermasterdetail;

import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;

public class UserEditorController {

@FXML
private TextField firstNameField ;
@FXML
private TextField lastNameField ;
@FXML
private CheckBox adminCheckBox ;

private String cachedFirstName ;
private String cachedLastName ;
private boolean cachedAdmin ;

private ChangeListener<User> userListener = (obs, oldUser, newUser) -> {
if (oldUser != null) {
firstNameField.textProperty().unbindBidirectional(oldUser.firstNameProperty());
lastNameField.textProperty().unbindBidirectional(oldUser.lastNameProperty());
adminCheckBox.selectedProperty().unbindBidirectional(oldUser.adminProperty());
}

if (newUser == null) {
firstNameField.clear();
lastNameField.clear();
adminCheckBox.setSelected(false);
} else {
firstNameField.textProperty().bindBidirectional(newUser.firstNameProperty());
lastNameField.textProperty().bindBidirectional(newUser.lastNameProperty());
adminCheckBox.selectedProperty().bindBidirectional(newUser.adminProperty());

cachedFirstName = newUser.getFirstName();
cachedLastName = newUser.getLastName();
cachedAdmin = newUser.isAdmin();
}
};


private DataModel model ;

public void setDataModel(DataModel dataModel) {
if (this.model != null) {
this.model.currentUserProperty().removeListener(userListener);
}
this.model = dataModel ;
this.model.currentUserProperty().addListener(userListener);
}

@FXML
private void cancel() {
firstNameField.setText(cachedFirstName);
lastNameField.setText(cachedLastName);
adminCheckBox.setSelected(cachedAdmin);
}
}

表.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>

<StackPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="usermasterdetail.TableController">
<TableView fx:id="table">
<columns>
<TableColumn fx:id="firstNameColumn" text="First Name"/>
<TableColumn fx:id="lastNameColumn" text="Last Name"/>
<TableColumn fx:id="adminColumn" text="Administrator"/>
</columns>
</TableView>
</StackPane>

用户编辑器.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="usermasterdetail.UserEditorController"
hgap="5" vgap="5" alignment="CENTER">

<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="NEVER"/>
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES"/>
</columnConstraints>

<padding>
<Insets top="5" left="5" bottom="5" right="5"/>
</padding>

<Label text="First Name:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<Label text="Last Name:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<Label text="Admin Priviliges:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>

<TextField fx:id="firstNameField" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<TextField fx:id="lastNameField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<CheckBox fx:id="adminCheckBox" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Button text="Cancel" onAction="#cancel" GridPane.columnIndex="0" GridPane.rowIndex="3" GridPane.columnSpan="2"
GridPane.halignment="CENTER"/>

</GridPane>

MainController.java:

package usermasterdetail;

import javafx.fxml.FXML;

public class MainController {
@FXML
private TableController tableController ;
@FXML
private UserEditorController editorController ;

private final DataModel model = new DataModel();

public void initialize() {
tableController.setDataModel(model);
editorController.setDataModel(model);
}
}

Main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.SplitPane?>

<SplitPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="usermasterdetail.MainController">
<items>
<fx:include fx:id="table" source="Table.fxml"/>
<fx:include fx:id="editor" source="UserEditor.fxml"/>
</items>
</SplitPane>

最后是 Main.java:

package usermasterdetail;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

@Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("Main.fxml")), 800, 600));
primaryStage.show();
}

public static void main(String[] args) {
launch(args);
}
}

如果您更喜欢您所描述的用户体验,您可以(如 @SSchuette 在注释中描述的那样),只需将表的禁用属性绑定(bind)到修改属性即可。这将防止用户在编辑数据时更改选择(即与表中的数据不一致)。为此,您只需要在模型中修改属性:

package usermasterdetail;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class DataModel {

private final ObservableList<User> userList = FXCollections.observableArrayList(
new User("Jacob", "Smith", false),
new User("Isabella", "Johnson", true),
new User("Ethan", "Williams", false),
new User("Emma", "Jones", true),
new User("Michael", "Brown", true)
);

private final ObjectProperty<User> currentUser = new SimpleObjectProperty<>();

private final BooleanProperty modifying = new SimpleBooleanProperty();

public final ObjectProperty<User> currentUserProperty() {
return this.currentUser;
}


public final usermasterdetail.User getCurrentUser() {
return this.currentUserProperty().get();
}


public final void setCurrentUser(final usermasterdetail.User currentUser) {
this.currentUserProperty().set(currentUser);
}


public ObservableList<User> getUserList() {
return userList;
}


public final BooleanProperty modifyingProperty() {
return this.modifying;
}



public final boolean isModifying() {
return this.modifyingProperty().get();
}



public final void setModifying(final boolean modifying) {
this.modifyingProperty().set(modifying);
}


}

然后在表 Controller 中您可以将禁用属性绑定(bind)到它:

package usermasterdetail;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;

public class TableController {

@FXML
private TableView<User> table ;
@FXML
private TableColumn<User, String> firstNameColumn ;
@FXML
private TableColumn<User, String> lastNameColumn ;
@FXML
private TableColumn<User, Boolean> adminColumn ;

private DataModel model ;

public void initialize() {
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
adminColumn.setCellValueFactory(cellData -> cellData.getValue().adminProperty());
adminColumn.setCellFactory(CheckBoxTableCell.forTableColumn(adminColumn));
}

public void setDataModel(DataModel dataModel) {
if (model != null) {
model.currentUserProperty().unbind();
}
this.model = dataModel ;
dataModel.currentUserProperty().bind(table.getSelectionModel().selectedItemProperty());
table.setItems(model.getUserList());
table.disableProperty().bind(model.modifyingProperty());
}
}

唯一需要做的地方是确保在数据不同步时将修改属性设置为 true(尽管听起来您已经这样做了):

package usermasterdetail;

import javafx.beans.value.ChangeListener;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;

public class UserEditorController {

@FXML
private TextField firstNameField ;
@FXML
private TextField lastNameField ;
@FXML
private CheckBox adminCheckBox ;

private DataModel model ;

private ChangeListener<Object> modifyingListener = (obs, oldValue, newValue) -> {
if (model != null) {
if (model.getCurrentUser() == null) {
model.setModifying(false);
} else {
model.setModifying(! (model.getCurrentUser().getFirstName().equals(firstNameField.getText())
&& model.getCurrentUser().getLastName().equals(lastNameField.getText())
&& model.getCurrentUser().isAdmin() == adminCheckBox.isSelected()));
}
}

};

private ChangeListener<User> userListener = (obs, oldUser, newUser) -> {
if (oldUser != null) {
oldUser.firstNameProperty().removeListener(modifyingListener);
oldUser.lastNameProperty().removeListener(modifyingListener);
oldUser.adminProperty().removeListener(modifyingListener);
}
if (newUser == null) {
firstNameField.clear();
lastNameField.clear();
adminCheckBox.setSelected(false);
} else {
firstNameField.setText(newUser.getFirstName());
lastNameField.setText(newUser.getLastName());
adminCheckBox.setSelected(newUser.isAdmin());

newUser.firstNameProperty().addListener(modifyingListener);
newUser.lastNameProperty().addListener(modifyingListener);
newUser.adminProperty().addListener(modifyingListener);
}
};


public void setDataModel(DataModel dataModel) {
if (this.model != null) {
this.model.currentUserProperty().removeListener(userListener);
}
this.model = dataModel ;
this.model.currentUserProperty().addListener(userListener);
}

public void initialize() {
firstNameField.textProperty().addListener(modifyingListener);
lastNameField.textProperty().addListener(modifyingListener);
adminCheckBox.selectedProperty().addListener(modifyingListener);
}


@FXML
private void cancel() {

if (model != null) {
firstNameField.setText(model.getCurrentUser().getFirstName());
lastNameField.setText(model.getCurrentUser().getLastName());
adminCheckBox.setSelected(model.getCurrentUser().isAdmin());
}
}

@FXML
private void update() {
if (model != null && model.getCurrentUser() != null) {
model.getCurrentUser().setFirstName(firstNameField.getText());
model.getCurrentUser().setLastName(lastNameField.getText());
model.getCurrentUser().setAdmin(adminCheckBox.isSelected());

}
}


}

此解决方案需要一个额外的按钮来强制更新数据(和表):

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.HBox?>

<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="usermasterdetail.UserEditorController"
hgap="5" vgap="5" alignment="CENTER">

<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="NEVER"/>
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES"/>
</columnConstraints>

<padding>
<Insets top="5" left="5" bottom="5" right="5"/>
</padding>

<Label text="First Name:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<Label text="Last Name:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<Label text="Admin Priviliges:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>

<TextField fx:id="firstNameField" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<TextField fx:id="lastNameField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<CheckBox fx:id="adminCheckBox" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<HBox spacing="5" alignment="CENTER" GridPane.columnIndex="0" GridPane.rowIndex="3" GridPane.columnSpan="2">
<Button text="Update" onAction="#update"/>
<Button text="Cancel" onAction="#cancel"/>
</HBox>

</GridPane>

关于tableview - JavaFX 自定义 MasterDetail Pane ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40106017/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com