使用数据模型Person
如上所述的示例,布尔属性,例如添加了已注册状态,因此名为“已注册”的第三个表列被添加到 TableView。
考虑代码示例:
//The Data Model
public class Person
{
/*
* Fields
*/
private StringProperty firstName;
private StringProperty lastName;
private BooleanProperty registered;
/*
* Constructors
*/
public Person(String firstName, String lastName, boolean registered)
{
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.registered = new SimpleBooleanProperty(registered);
}
public Person()
{
this(null, null, false);
}
/*
* Properties
*/
public StringProperty firstNameProperty() { return firstName; }
public String getFirstName() { return this.firstName.get(); }
public void setFirstName(String value) { this.firstName.set(value); }
public StringProperty lastNameProperty() { return lastName; }
public String getLastName() { return this.lastName.get(); }
public void setLastName(String value) { this.lastName.set(value); }
public BooleanProperty registeredProperty() { return registered; }
public boolean isRegistered() { return this.registered.get(); }
public void setRegistered(boolean value) { this.registered.set(value); }
}
//Dummy values for the data model
List<Person> personList = new ArrayList<Person>();
personList.add( new Person("John", "Smith", true) ;
personList.add( new Person("Jack", "Smith", false) );
TableView<Person> tblView = new TableView<Person>();
tblView.setItems( FXCollections.observableList(personList) );
TableColumn firstName_col = new TableColumn("First Name");
TableColumn lastName_col = new TableColumn("Last Name");
TableColumn registered_col = new TableColumn("Registered");
firstName.setCellValueFactory(new PropertyValueFactory<Person,String>("firstName"));
lastName.setCellValueFactory(new PropertyValueFactory<Person,String>("lastName"));
registered_col.setCellValueFactory(
new Callback<CellDataFeatures<Person,Boolean>,ObservableValue<Boolean>>()
{
//This callback tell the cell how to bind the data model 'Registered' property to
//the cell, itself.
@Override
public ObservableValue<Boolean> call(CellDataFeatures<Person, Boolean> param)
{
return param.getValue().registeredProperty();
}
});
//This tell how to insert and render a checkbox in the cell.
//
//The CheckBoxTableCell has the updateItem() method which by default links up the
//cell value (i.e. the 'Registered' property to the checkbox. And this method is
//automatically call at the appropriate time, such as when creating and rendering
//the cell (I believe).
//
//In this case, as the registed_col.setCellValueFactory() method has specified
//'Registered' in the actual data model (i.e. personList), therefore the checkbox will
//be bound to this property.
registered_col.setCellFactory( CheckBoxTableCell.forTableColumn(registered_col) );
tblView.getColumns.addAll(firstName_col, lastName_col, registered_col);
//table display preference - should not affect this exercise/problem
tblView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
这段代码工作正常。当迭代数据模型或tblView
用于访问的 UI 组件Registered
属性,它将显示正确的值,即使它发生变化(即取消/勾选复选框)。
尝试添加不绑定到数据模型的复选框的原始问题尚未得到解答。
假设添加了另一列调用“选择”,并且它仅包含一个复选框以直观地指示可以(或已)选择一行或多行。重申一下,此列复选框与数据模型没有任何相关含义Person
。从而在内部创建一个属性Person
保存该值的类在语义上是不必要的,并且可能被认为是糟糕的编码实践。那么这个问题是如何解决的呢?
如何将任意 BooleanProperty(或 personList 中每个人的列表)链接或绑定到相应的复选框?
TableColumn select_col = new TableColumn("Select");
//Set a boolean property to represent cell value.
select_col.setCellValueFactory(
new Callback<CellDataFeatures<Person,Boolean>,ObservableValue<Boolean>>()
{
@Override
public ObservableValue<Boolean> call(CellDataFeatures<Person,Boolean> param)
{
//PROBLEM -- What Code goes here?
}
};
//This call should be okay - it would display the checkbox according to the provided
//boolean (property). This was proven with
//registered_col.setCellFactory(CheckBoxTableCell.forTableColumn(registered_col)
select_col.setCellFactory(CheckBoxTableCell.forTableColumn(select_col);
一种解决方案是创建一个(匿名)内部类,该内部类可以子类化Person
并添加“选择”属性。使用与“注册”属性及其表列类似的代码来链接“选择”属性,它应该可以工作。如上所述,仅仅为了解决视觉问题而进行子类化就破坏了数据模型语义。
更好的解决方案可能是 - 为每个人创建一个布尔属性的内部列表personList
并将它们连接在一起。那么如何检索与每个人相对应的适当布尔属性personList
in the setCellValueFactory()
方法?一种可能的解决方案是使用索引位置personList
、选择列的布尔属性列表以及行索引。所以归结为获取行索引setCellValueFactory(CellDataFeatures)
,以及如何正确完成此操作?
考虑代码:
TableColumn<Person,Boolean> select_col = new TableColumn<Person,Boolea>("Select");
List<BooleanProperty> selectedRowList = new ArrayList<BooleanProperty>();
//This callback allows the checkbox in the column to access selectedRowList (or more
//exactly, the boolean property it contains
Callback<Integer,ObservableValue<Boolean>> selectedStateSelectColumn =
new Callback<Integer,ObservableValue<Boolean>>()
{
//index in this context reference the table cell index (I believe)
@Override
public ObservableValue<Boolean> call(Integer index)
{
return selectedRowList.get(index);
}
}
//Initialise the selectedRowList
for(Person p : personList)
{
//initially, it starts off as false, i.e. unticked state
selectedRowList.add( new SimpleBooleanProperty() );
}
select_col.setCellValueFactory(
new Callback<CellDataFeatures<Person,Boolean>,ObservableValue<Boolean>>
{
//retrieve the cell index and use it get boolean property in the selectedRowList
@Override
public ObservableValue<Boolean> call(CellDataFeatures<Person,Boolean> cdf)
{
TableView<Person> tblView = cdf.getTableView();
Person rowData = cdf.getValue();
int rowIndex = tblView.getItems().index( rowData );
return selectedRowList.get( rowIndex );
}
}
select_col.setCellFactory(
CheckBoxTableCell.forTableColumn(selectedStateSelectColumn));
这些片段对我有用。只需要重新组织即可编译和运行。不过,要点部分是正确的。
这个问题或情况很常见,但我花了几天时间来实施和解决。我希望这对其他人有好处。