请解释如何使用CheckBoxTableCell

时间:2014-08-21 06:41:48

标签: tableview javafx-8

我想了解更多关于如何实际使用或子类(如有必要)CheckBoxTableCell。有一个特定的情况我想使用这个类,因此复选框不会绑定到底层数据模型属性。

假设我有一个列呼叫'选择'其中有复选框。此列或多或少用作对行的可视标记。用户可以勾选这些框,然后使用按钮对勾选的行执行某些操作(例如,删除所有勾选的行)。

我正在搜索有关此主题的教程。虽然有一些教程或解释,但它们涉及与复选框相关的一些支持数据模型。

所以我正在寻找关于此的更详细解释,其中复选框是动态生成的,并且用作用户界面的辅助辅助,例如上面解释的示例。此外,我想知道编辑如何发挥作用并将其正确编码为标准和约定,特别是当Java 8引入了属性更新,新的javafx类等时。

如果有帮助,一个常见的参考示例可能是“人物”的数据模型,它只有一个属性“名称”。 ObservableList可以绑定到显示名称的TableView。设置在最左侧(tableview)的另一列是针对每个名称的复选框。最后,至少一个按钮允许对人员列表进行某种形式的操纵。为了简单起见,该按钮只是根据当按钮被操作时是否标记了该人的姓名时,删除了列表中的人员。此外,一个按钮可以添加新人,但它可以是可选的。

我希望我能够以简明扼要的方式撰写主题,并且有一个明确的解决方案。提前感谢能够提供相关信息的任何人。

2 个答案:

答案 0 :(得分:14)

使用如上所述的数据模型Person示例,布尔属性,例如,已注册状态已添加,因此第三个表格栏名为“已注册”#39;添加到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属性时,它将显示正确的值,即使它发生更改(即取消/勾选复选框)。

尝试添加未绑定到数据模型的复选框的原始问题尚未得到解答。

假设另一个列呼叫&#39;选择&#39;添加它,它只是包含一个复选框,以直观地指示可以(或是)选择一行或多行。重申,此列复选框与数据模型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并添加&#39;选择&#39;属性。使用类似的代码来注册&#39;属性及其表格列链接&#39;选择&#39;财产,它应该工作。如上所述,仅仅为了解决视觉问题而进行子类化会破坏数据模型的语义。

更好的解决方案是 - 为personList中的每个人创建一个布尔属性的内部列表,并将它们链接在一起。那么如何在personList方法中检索与setCellValueFactory()中每个人相对应的适当布尔属性?一种可能的解决方案是使用personList中的索引位置,select列的boolean属性列表和行索引。所以归结为将行索引放在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));

这些代码段对我有用。它只需要重新组织即可编译和运行。但是,要点部分是正确的。

这个问题或情况很常见,但实施和解决却花了我几天的时间。我希望这对其他人有利。

答案 1 :(得分:6)

Java 8

在控制器中:

checked.setCellValueFactory(
      param -> param.getValue().isChecked()
);

checked.setCellFactory(CheckBoxTableCell.forTableColumn(checked));

模特:

public class FileFound {

private String fileName;
private Long fileSize;
private String fileExt;
private String fullPath;
private Path filePath;
private BooleanProperty checked = new SimpleBooleanProperty(true);


public String getFileName() {
    return fileName;
}

public void setFileName(String fileName) {
    this.fileName = fileName;
}

public Long getFileSize() {
    return fileSize;
}

public void setFileSize(Long fileSize) {
    this.fileSize = fileSize;
}

public String getFileExt() {
    return fileExt;
}

public void setFileExt(String fileExt) {
    this.fileExt = fileExt;
}

public String getFullPath() {
    return fullPath;
}

public void setFullPath(String fullPath) {
    this.fullPath = fullPath;
}

public Path getFilePath() {
    return filePath;
}

public void setFilePath(Path filePath) {
    this.filePath = filePath;
}

public ObservableBooleanValue isChecked() {
    return checked;
}

public void setChecked(Boolean checked) {
    this.checked.set(checked);
}}