返回

自定义 TableView 列的可观察性:如何访问类的所有属性?

java

## 自定义 TableView 列的可观察性:超越 PropertyValueFactory

作为程序员,在设计复杂的 GUI 界面时,我们经常需要处理表格数据。TableView 控件是 JavaFX 中用于呈现表格数据的强大组件。但是,默认情况下,TableView 列仅绑定到特定对象的属性,这可能会限制我们访问其他相关数据的灵活性。本文将深入探讨一种有效的方法,利用 ObservableValueFactory 和自定义单元格工厂,使自定义 TableView 列可观察,从而可以访问类的所有属性,而不仅仅是绑定的属性显示值。

为什么需要可观察性?

在某些情况下,我们可能需要在 TableView 单元格的更新方法中使用类的所有属性,而不仅仅是绑定的属性显示值。这对于创建复杂的展示或交互式表格至关重要。例如,我们可能有一个包含多个属性的对象,我们希望在单元格中显示它们的组合值。

使用 ObservableValueFactory

为了使自定义 TableView 列可观察,我们可以在创建列时使用 ObservableValueFactory。ObservableValueFactory 提供了一个 ObservableValue 接口的实现,该接口返回列中单元格表示的对象。通过使用 ObservableValueFactory,我们不必使用 PropertyValueFactory,从而可以访问对象的任何属性。

col_2.setCellValueFactory(data -> new ObservableValue<>()
{
    @Override
    public void addListener(ChangeListener<? super myclass> listener) {}

    @Override
    public void removeListener(ChangeListener<? super myclass> listener) {}

    @Override
    public myclass getValue()
    {
        return data.getValue();
    }

    @Override
    public void addListener(InvalidationListener listener) {}

    @Override
    public void removeListener(InvalidationListener listener) {

    }
});

创建自定义单元格工厂

使用 ObservableValueFactory 后,我们需要创建自定义单元格工厂,以便在列中实现可观察性。单元格工厂负责创建和更新表格单元格的图形表示。

col_2.setCellFactory(p ->
{
    TableCell<myclass,myclass> cell = new TableCell<>()
    {
        @Override
        protected void updateItem(myclass item, boolean empty)
        {
            if (item != null)
            {
                Label l = new Label();
                l.setText("combination display " + item.fld2Property().getValue() + " " + item.fld4Property().getValue());
                setGraphic(l);
            }
            else
            {
                setGraphic(null);
                setText(null);
            }
        }
    };
    return cell;
});

在自定义单元格工厂中,我们创建一个 TableCell 实例,并覆盖其 updateItem 方法。在 updateItem 方法中,我们可以访问通过 ObservableValueFactory 提供的对象,并使用其属性创建单元格的图形表示。

示例代码

以下示例代码演示了如何使用 ObservableValueFactory 和自定义单元格工厂使自定义 TableView 列可观察:

// 创建自定义的 myclass 对象,其中包含多个属性
public class myclass
{
    private IntegerProperty fld1;
    private StringProperty fld2;
    private StringProperty fld3;
    private StringProperty fld4;

    // 构造函数
    public myclass(int fld1, String fld2, String fld3, String fld4)
    {
        this.fld1 = new SimpleIntegerProperty(fld1);
        this.fld2 = new SimpleStringProperty(fld2);
        this.fld3 = new SimpleStringProperty(fld3);
        this.fld4 = new SimpleStringProperty(fld4);
    }

    // 省略 getter 和 setter 方法
}

// 在控制器类中
public class HelloController
{
    // 定义 TableView 和列
    @FXML
    private TableView<myclass> tableView;
    @FXML
    private TableColumn<myclass, Integer> col_1;
    @FXML
    private TableColumn<myclass, myclass> col_2;
    @FXML
    private TableColumn<myclass, String> col_3;
    @FXML
    private TableColumn<myclass, String> col_4;

    // 初始化数据
    public void initialize()
    {
        // 创建 ObservableValueFactory
        col_2.setCellValueFactory(data -> new ObservableValue<>()
        {
            @Override
            public void addListener(ChangeListener<? super myclass> listener) {}

            @Override
            public void removeListener(ChangeListener<? super myclass> listener) {}

            @Override
            public myclass getValue()
            {
                return data.getValue();
            }

            @Override
            public void addListener(InvalidationListener listener) {}

            @Override
            public void removeListener(InvalidationListener listener) {}
        });

        // 创建自定义单元格工厂
        col_2.setCellFactory(p ->
        {
            TableCell<myclass, myclass> cell = new TableCell<>()
            {
                @Override
                protected void updateItem(myclass item, boolean empty)
                {
                    if (item != null)
                    {
                        Label l = new Label();
                        l.setText("combination display " + item.fld2Property().getValue() + " " + item.fld4Property().getValue());
                        setGraphic(l);
                    }
                    else
                    {
                        setGraphic(null);
                        setText(null);
                    }
                }
            };
            return cell;
        });

        // 设置其他列
        col_1.setCellValueFactory(new PropertyValueFactory<>("fld1"));
        col_3.setCellValueFactory(new PropertyValueFactory<>("fld3"));
        col_4.setCellValueFactory(new PropertyValueFactory<>("fld4"));

        // 加载数据到 TableView
        tableView.setItems(FXCollections.observableArrayList(origlist));
    }
}

通过使用 ObservableValueFactory 和自定义单元格工厂,我们成功地使自定义 TableView 列可观察,从而能够在单元格的 updateItem 方法中访问类的所有属性,这增强了我们创建灵活且交互式表格的能力。

常见问题解答

1. 使用 ObservableValueFactory 和自定义单元格工厂有什么好处?

  • 访问类的所有属性,而不受 PropertyValueFactory 的限制。
  • 允许在单元格中创建复杂的显示,组合不同的属性值。
  • 增强表格数据的灵活性,使其适用于更广泛的场景。

2. ObservableValueFactory 和 PropertyValueFactory 之间的区别是什么?

  • ObservableValueFactory 返回一个 ObservableValue 对象,该对象提供列中单元格表示的对象。
  • PropertyValueFactory 将特定对象的属性直接绑定到列。

3. 我可以在 JavaFX 中使用 ObservableValueFactory 来创建所有自定义列吗?

  • 对于需要访问类的所有属性或创建自定义单元格显示的列,ObservableValueFactory 是理想的选择。对于简单列,使用 PropertyValueFactory 可能更合适。

4. 如何在使用 ObservableValueFactory 的情况下访问单元格中对象的属性?

  • 在自定义单元格工厂的 updateItem 方法中,您可以通过调用 getValue() 方法访问 ObservableValue 中存储的对象。然后,您可以使用对象属性来创建单元格的图形表示。

5. 是否可以在其他类型的 JavaFX 组件中使用 ObservableValueFactory?

  • ObservableValueFactory 可以用于需要显示和操作可观察对象的任何 JavaFX 组件。它不仅限于 TableView 列。