JavaFX TableView

时间:2020-01-09 10:36:41  来源:igfitidea点击:

JavaFX TableView使我们可以在JavaFX应用程序内部显示表视图。 JavaFX TableView由类javafx.scene.control.TableView表示。这是JavaFXTableView的屏幕截图:

与TableView相关的类

JavaFX TableView类使用一组相关的类来完成其工作。这些类是:

  • TableColumn(表格列)
  • TableRow(表格行)
  • TableCell(表格单元)
  • TablePosition(表格位置)
  • TableViewFocusModel
  • TableViewSelectionModel

这些类的确切函数将在本教程后面的相应部分中介绍。

JavaFX TableView示例

这是一个完整但简单的JavaFXTableView代码示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TableViewExample extends Application {

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

  @Override
  public void start(Stage primaryStage) {

    TableView tableView = new TableView();

    TableColumn<String, Person> column1 = 
    new TableColumn<>("First Name");
    
    column1.setCellValueFactory(
        new PropertyValueFactory<>("firstName"));

    TableColumn<String, Person> column2 = 
    new TableColumn<>("Last Name");
    
    column2.setCellValueFactory(
        new PropertyValueFactory<>("lastName"));

    tableView.getColumns().add(column1);
    tableView.getColumns().add(column2);

    tableView.getItems().add(
      new Person("John", "Doe"));
    tableView.getItems().add(
      new Person("Jane", "Deer"));

    VBox vbox = new VBox(tableView);

    Scene scene = new Scene(vbox);

    primaryStage.setScene(scene);

    primaryStage.show();
  }

}

这是此示例中使用的Person类:

public class Person {

    private String firstName = null;
    private String lastName = null;

    public Person() {
    }

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

创建一个TableView

为了使用JavaFX的TableView组件,我们必须首先创建一个TableView实例。这是创建JavaFXTableView实例的示例:

TableView tableView = new TableView();

将TableColumn添加到TableView

创建了" TableView"后,我们需要向" TableView"实例中添加一个或者多个" TableColumn"实例。 " TableColumn"代表值的垂直列。每个值都显示在其自己的行上,通常从" TableView"中显示的对象列表中提取。这是将两个TableColumn实例添加到JavaFX TableView实例的示例:

TableView tableView = new TableView();

TableColumn<String, Person> firstNameColumn = 
    TableColumn<>("First Name");
firstNameColumn.setCellValueFactory(
    new PropertyValueFactory<>("firstName"));

TableColumn<String, Person> lastNameColumn = 
    new TableColumn<>("Last Name");
lastNameColumn.setCellValueFactory(
    new PropertyValueFactory<>("lastName"));

请注意,传递给TableTable类的构造函数的Java String参数。该字符串将显示为该列上方的列标题。我们可以在此页面顶部的屏幕快照中看到此类列标题的示例。

TableColumn单元格值工厂

一个TableColumn必须在其上设置一个单元格值工厂。单元格值工厂提取要在列中每个单元格(每行)中显示的值。在上面的示例中,使用了" PropertyValueFactory"。 " PropertyValueFactory"工厂可以从Java对象提取属性值(字段值)。属性名称作为参数传递给PropertyValueFactory构造函数,如下所示:

PropertyValueFactory factory = 
    new PropertyValueFactory<>("firstName");

属性名" firstName"将与" Person"对象的getter getter方法" getFirstName()"匹配,其中包含值的值将显示在每一行上。

在前面显示的示例中,在第二个" TableColumn"实例上设置了第二个" PropertyValueFactory"。传递给第二个PropertyValueFactory的属性名称是lastName,它将与Person类的getter方法getLastName()相匹配。

将数据添加到TableView

将TableColumn实例添加到JavaFX TableView实例后,可以将要显示的数据添加到TableView。数据通常包含在常规Java对象(POJO)列表中。这是向TableView中添加两个Person对象(在本JavaFX TableView教程前面显示的类)的示例:

tableView.getItems().add(new Person("John", "Doe"));
tableView.getItems().add(new Person("Jane", "Deer"));

当无行显示时设置占位符

我们可以将占位符设置为当JavaFXTableView没有要显示的行时显示。占位符必须是JavaFXNode类的实例,大多数(如果不是全部)JavaFX控件都是该实例。因此,我们可以使用JavaFX ImageView或者JavaFX Label作为占位符。这是在JavaFXTableView中使用Label作为占位符的示例:

tableView.setPlaceholder(
    new Label("No rows to display"));

这是对应的TableView在显示占位符的情况下的样子:

TableView选择模型

JavaFX TableView组件具有一个内部SelectionModel,用于读取用户选择了哪些行和/或者单元格,或者以编程方式选择行和单元格。在以下各节中,我将仔细研究JavaFX TableView SelectionModel。

获取TableView SelectionModel实例

要获取JavaFX TableView的SelectionModel,必须调用其getSelectionModel()方法。这是一个从JavaFX TableView获取SelectionModel的示例:

TableViewSelectionModel selectionModel = 
    tableView.getSelectionModel();

TableViewSelectionModel类可以在其上设置通用类型。通常,该类型将与TableView中显示的数据使用的类型相同。这是在获取TableViewSelectionModel时声明其通用类型的示例:

TableViewSelectionModel<Person> selectionModel = 
    tableView.getSelectionModel();

设置选择模式

我们可以使用其setSelectionMode()方法设置TableView TableViewSelectionModel的选择模式。我们可以选择是否应该只选择一行,还是选择多行,甚至选择单个单元格。这是设置TableViewSelectionModel的选择模式的示例:

// set selection mode to only 1 row
selectionModel.setSelectionMode(
    SelectionMode.SINGLE);

// set selection mode to multiple rows
selectionModel.setSelectionMode(
    SelectionMode.MULTIPLE);

第一行将选择模式设置为一次只允许选择一行。顺便说一下,这是TableViewSelectionModel的默认设置。

第二行设置选择模式以允许选择多行。用户在选择行以选择多个行时,需要按住SHIFT或者CTRL键。

获取选定的行

要获取JavaFX TableView的选定行项目的列表,请调用SelectionModelgetSelectedItems()方法。返回的列表是只读的。这是一个从JavaFX TableView SelectionModel获取所选行的列表的示例:

ObservableList selectedItems = 
    selectionModel.getSelectedItems();

如果在TableViewSelectionModel上设置了通用类型,则从getSelectedItems()返回的ObservableList将具有相同的通用类型。这是一个例子:

ObservableList<Person> selectedItems = 
    selectionModel.getSelectedItems();

获取选定的指数

我们也可以只获取选定行的索引列表,而不是选定项本身。这是从TableViewSelectionModel获取所选行的索引列表的示例:

ObservableList<Integer> selectedIndices = 
    selectionModel.getSelectedIndices();

清空选项

我们可以使用TableViewSelectionModel clearSelection()方法清除所有选定的行和单元格。这是一个通过TableViewSelectionModelclearSelection()方法清除TableView的所有选定行或者单元格的示例:

selectionModel.clearSelection();

聆听选择更改

可以在TableViewSelectionModel中侦听选择更改。为此,必须将侦听器添加到由getSelectedItems()或者getSelectedIndices()返回的ObservableList之一。我们应该使用两个列表中的哪一个取决于选择更改时是否需要获取所选项目或者所选索引。这是将更改侦听器添加到由getSelectedItems()返回的ObservableList的示例:

ObservableList<Person> selectedItems = 
    selectionModel.getSelectedItems();

selectedItems.addListener(
  new ListChangeListener<Person>() {
    @Override
    public void onChanged(
      Change<? extends Person> change) {
        System.out.println(
          "Selection changed: " + change.getList());
      }
})

以编程方式选择行

可以在JavaFX TableView中以编程方式选择行。我们可以通过TableViewSelectionModel对象的许多选择方法来执行此操作。要选择具有特定索引的行,可以使用select(int)方法。这是在JavaFX TableView中选择具有给定索引的单行的示例:

selectionModel.select(1);

本示例选择索引为1的行。请记住,行索引从0开始。

嵌套列

JavaFX TableView支持我们添加到其中的TableColumn实例的嵌套。嵌套列仅表示将名为"客户"的表列细分为两个嵌套列,例如"客户编号"和"名称"。并且"名称"列可以再次细分为"名字"和"姓氏"。这是一个JavaFX TableView嵌套列示例,该示例显示:

嵌套级别中最低的TableColumn实例称为叶列。只有叶列可以显示数据。因此,仅将单元格值工厂添加到叶TableColumn实例。嵌套级别较高的TableColumn实例主要是作为视觉效果存在的。

这是上面的JavaFX TableView嵌套列示例的代码:

TableView tableView = new TableView();
    
TableColumn<String, Customer> customer   = 
    new TableColumn<>("Customer");
TableColumn<String, Customer> customerId = 
    new TableColumn<>("Customer No");
TableColumn<String, Customer> name       = 
    new TableColumn<>("Name");
TableColumn<String, Customer> firstName  = 
    new TableColumn<>("First Name");
TableColumn<String, Customer> lastName   = 
    new TableColumn<>("Last Name");

name     .getColumns().addAll(firstName, lastName);
customer .getColumns().addAll(customerId, name);
tableView.getColumns().addAll(customer);

customerId.setCellValueFactory(
    new PropertyValueFactory<>("customerNo"));
firstName .setCellValueFactory(
    new PropertyValueFactory<>("firstName"));
lastName  .setCellValueFactory(
    new PropertyValueFactory<>("lastName"));

tableView.getItems().add(
    new Customer("007", "Jane", "Deer"));
tableView.getItems().add(
    new Customer("006", "John", "Doe"));
tableView.getItems().add(
    new Customer("008", "Mack", "Alamo"));

可见叶柱

JavaFX TableView类具有一个名为c的方法,该方法可以返回TableView的可见叶列(TableColumn)。我们将可见叶列的索引传递给getVisibleLeafColumn()方法的参数。这是从JavaFX TableView获取第二(索引1)可见叶列的示例:

TableColumn<String, Customer> leafColumn =
    tableView.getVisibleLeafColumn(1);

如果上面的代码是针对上一节中的示例中配置的TableView执行的,则返回的TableColumn将是"名字" TableColumn。

我们还可以通过TableViewgetVisibleLeafColumns()获取所有可见的叶列。

隐藏栏

JavaFX TableView使我们可以通过传递TableTable的setVisible()来传递参数false来隐藏TableColumn。这是隐藏TableColumn的示例:

tableColumn.setVisible(false);

要再次显示隐藏的列,只需调用setVisible(),将true作为参数值即可。这是显示TableColumn的示例:

tableColumn.setVisible(true);

重新排序栏

JavaFX TableView使我们可以重新排序其列。我们可以通过两种方式记录其列。

第一种方法是将一列拖动到TableView内的其他水平位置。这是由最终用户而不是开发人员完成的。

第二种方法是更改由TableView的getColumns()方法返回的ObservableList中的TableColumn实例的顺序。这是通过编程方式完成的。这是删除第一列并将其再次添加为TableView的最后一列的示例:

tableView.getColumns().add(
    tableView.getColumns().remove(0));

排序行

JavaFX TableView使我们可以对TableView中的行进行排序。行有两种排序方式。

第一种方法是让用户单击TableColumn标题单元格(其中显示列标题)。这将对TableView中该列的值之后的行进行排序。如果用户再次单击同一TableColumn单元格,则排序顺序将颠倒。并且,如果用户第三次单击,该列上的排序将再次被禁用。

对TableView的行进行排序的第二种方法是编程的。我们可以设置排序顺序(升序或者降序),设置自定义比较器以比较TableColumn的值,设置要显示的自定义排序图标,甚至完全禁用给定TableColumn的排序。这些选项将在下面更详细地说明。

排序类型

我们可以使用TableColumn的setSortType()方法指定希望该列使用的排序类型。我们可以将排序类型设置为升序或者降序。这是两个示例,展示了如何设置两种排序类型:

tableColumn.setSortType(TableColumn.SortType.ASCENDING);

tableColumn.setSortType(TableColumn.SortType.DESCENDING);

禁用列的排序

可以完成对给定TableColumn的禁用排序。我们可以通过TableColumn的setSortable()方法来实现。这是一个禁用给定TableColumn排序的示例:

tableColumn.setSortable(false);

要重新启用TableColumn的排序,只需调用setSortable(),其值为true即可,如下所示:

tableColumn.setSortable(false);

列自定义比较器

我们可以在TableColumn上设置自定义的Comparator接口实现。此Comparator实现将确定如何对该列中显示的值进行排序。我们可以通过TableColumn的setComparator()方法设置Comparator。这是在TableColumn上设置自定义Comparator的示例:

Comparator<String%gt; columnComparator =
    (String v1, String v2) -> {

  return v1.toLowerCase().compareTo(
        v2.toLowerCase());

};

tableColumn.setComparator(columnComparator);

本示例首先创建一个Comparator实现,该实现以小写形式比较String对象。然后,它在TableColumn上设置此Comparator。然后,该比较器将确定在该列中显示的值的排序顺序。

列默认比较器

TableColumn上设置了一个默认的Comparator,它可以为我们执行某种智能形式的默认排序。有时,默认的Comparator就足够了。这是默认比较器比较同一列内显示的两个值的方式:

  • 检查" null"值。空值被认为小于非空值。如果两个值均为" null",则认为它们相等。
  • 如果第一个值是实现Comparable接口的对象,则默认的Comparator将返回value1.compareTo(value2)。
  • 如果规则1和2不适用于这些值,则默认的Comparator会使用它们的.toString()方法将这两个值转换为String,然后进行比较。

列排序节点

TableColumn类使用"排序节点"来显示当前应用于该列的排序类型。排序节点显示在列标题内。默认的排序节点是箭头形的三角形。

我们可以通过setColumns()方法更改TableColumn的排序节点,并传递一个JavaFX Node用作排序节点。这是将ImageView设置为TableColumn上的排序节点的示例:

FileInputStream input = new FileInputStream("images/sort-image.png");
Image image = new Image(input);
ImageView sortImage = new ImageView(image);

tableColumn.setSortNode(sortImage);

注意:当排序类型从升序更改为降序时,TableColumn所使用的默认排序节点将旋转180度。但是,对于自定义排序节点,似乎不会发生这种轮换。排序类型更改后,我们可能必须自己管理排序节点的更改。

列排序顺序

JavaFX TableView使我们可以指定TableView的默认排序顺序。 TableView中的项目将根据此顺序进行排序,直到用户单击某些TableColumn标头并更改排序顺序为止。

默认排序顺序由TableColumn实例的ObservableList组成。 TableView中的项目将在此列表中第一个TableColumn上的排序集之后进行排序。如果第一个TableColumn的值相等,则将根据第二个TableColumn上的排序集对项目进行排序,如果它们相等,则将对第三个TableColumn等进行排序。

这是设置默认排序顺序的示例:

tableView.getSortOrder().addAll(lastNameColumn, firstNameColumn);

此示例将排序顺序设置为" lastNameColumn",然后设置为" firstNameColumn"。

注意:在运行示例时,看到排序顺序正确反映在TableView中显示的项目中,我遇到了一些挑战,但是还是有些事情发生。我们可能需要稍微使用此函数才能按需完成它!

手动触发排序

JavaFX TableView控件包含一个名为sort()的方法,该方法在被调用时将触发TableView中各项的排序。这是一个校准TableViewsort()方法的示例:

tableView.sort();

排序事件

JavaFX TableView会在对项目进行排序之前触发一个sort事件。我们可以使用TableViewsetOnSort()方法侦听排序事件,并将EventHandler接口实现作为参数传递。这是在JavaFX TableView上侦听排序事件的示例:

tableView.setOnSort((event) -> {
    System.out.println("Sorting items");
});

顺便说一句,如果事件监听器调用event.consume(),则排序将被取消。看起来是这样的:

tableView.setOnSort((event) -> {
    event.consume();
});

禁用TableView的排序

我们可以通过侦听排序事件并使用它们(调用event.consume())或者对所有TableColumn实例禁用排序来禁用整个TableView的排序。还有其他禁用整个TableView排序的方法,但是这两种方法就足够了。

自定义单元格渲染

JavaFX TableView使我们可以自定义TableView中显示的单元格的呈现。添加到TableView的每个TableColumn在TableView中的每个可见行都有一个单元格。这意味着,如果TableView具有10个可见行和3列,则每列(TableColumn)都有10个单元格,总共30个单元格。

如前所述,单元格计数由可见行和列(可见单元格)的数量确定。 TableView的项目列表中是否有1000个数据项目(行)无关紧要。在给定的时间可见多少行才很重要。可见单元格对象将被重用以显示在任何给定时间可见的任何行的值。与为每一行(数据项)的每一列创建一个单元格对象相比,该设计节省了大量的单元格对象创建。

若要自定义单元格渲染,必须在要为其自定义单元格渲染的TableColumn上设置单元格工厂。我们可以通过TableColumn的setCellFactory()方法设置单元格工厂。 setCellFactory()方法采用Callback接口实现作为参数。回调实现创建TableCell实例。每个TableCell实例对应于TableView中的一个可见单元格。每个TableCell都会创建一次Callback实现,因此Callback实现不需要知道TableView中有多少个可见单元格。

为了更好地了解单元工厂的工作方式,让我们看一个在TableColumn上设置单元工厂的示例:

customerColumn.setCellFactory((tableColumn) -> {
    TableCell<Customer, String> tableCell = new TableCell<>() {
        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            this.setText(null);
            this.setGraphic(null);

            if(!empty){
                this.setText(item.toUpperCase());
            }
        }
    };

    return tableCell;
});

本示例使用Java Lambda表达式的回调实现调用setCellFactory()方法。

回调实现创建一个TableCell并返回它。创建的TableCell是TableCell类的匿名子类,其中的updateItem()方法被覆盖。每次TableCell要显示新项目(行)的列值时,都会调用TableCell子类的updateItem()方法。这就是为什么updateItem()方法首先调用setText(null)和setGraphic(null)清除TableCell之前显示的任何文本和图形的原因。

清除上一项(行)中的值后,如果单元格不为空,则设置新值。传递给updateItem()的第二个参数是布尔值,如果单元格为空,则其值为true。如果单元格为空,则TableCell不应显示任何值。

TableCell的呈现类似于JavaFX Label,这意味着它可以显示图形和文本。为了显示图形,TableCell的updateItem()以JavaFX Node对象作为参数调用setGraphic()方法。为了显示一个文本,调用带有字符串作为参数的setText()。 TableCell可以同时显示图形和文本。