【问题标题】:ComboBox in a tableview cell in JavaFXJavaFX中表格视图单元格中的组合框
【发布时间】:2016-05-09 22:58:11
【问题描述】:

我正在尝试将Combo Box 添加到我的Table View

基本上我有一个名为 TableViewTest 的类,它存储名称和描述,我可以在 Table View 中显示这些名称和描述,但我想要做的是添加第三列,每个单元格都有一个 @ 987654324@ 以便用户可以从多个选项中为每个人选择一个。

到目前为止,我已经创建了一个带有一些值的String 类型的ObservableList,并将它们添加到ComboBox 对象中。有谁知道我可以将此Combo Box 添加到表格中吗?

另外请记住,这段代码非常粗糙,我只是想让一些东西正常工作,我会在以后重构代码。

ObservableList<TableViewTest> products = FXCollections.observableArrayList();

    for(int i = 0; i < b.length; i++){

        // random String values
        products.add(new TableViewTest(b[i], a[i]));
    }

ObservableList<String> options = FXCollections.observableArrayList(
                                "1",
                                "2",
                                "3"
                                );
final ComboBox comboBox = new ComboBox(options);

TableColumn<TableViewTest, String> nameColumn = new TableColumn<> ("Name");
nameColumn.setMinWidth(200);
nameColumn.setCellValueFactory(new PropertyValueFactory<TableViewTest, String>("name"));

                //price Column
                //Stock Column
TableColumn<TableViewTest, String> StockColumn = new TableColumn<> ("Stock");
StockColumn.setMinWidth(150);
StockColumn.setCellValueFactory(new PropertyValueFactory<TableViewTest, String>("description"));


TableColumn<Object,ComboBox> PriceColumn;
PriceColumn = new TableColumn<>("Source");
PriceColumn.setMinWidth(150);
   //PriceColumn.setCellValueFactory(new PropertyValueFactory<>
   //(options));

   //PriceColumn.setCellFactory(ComboBoxTableCell.forTableColumn(new 
   //DefaultStringConverter(), options));


   //PriceColumn.setCellFactory(ComboBoxTableCell.forTableColumn( 
   //comboBox));

TableView<TableViewTest> table = new TableView<>();

table.setItems(products);
table.getColumns().addAll(nameColumn, StockColumn, PriceColumn);

【问题讨论】:

  • 您的表格列类型应该是该列中显示的数据的类型,而不是用于显示它的控件的类型。 IE。你需要TableColumn&lt;..., String&gt; priceColumn(我认为应该是TableColumn&lt;TableViewTest, String&gt; priceColumn)。那么你应该可以使用ComboBoxTableCell
  • 我真的希望有一些这样的 FXML 示例。有人知道吗?

标签: java javafx combobox


【解决方案1】:

所以我需要做基本相同的事情,在 TableView 的列中显示一个动态组合框。我确信这些其他解决方案是灵活的,但它们似乎是不必要的高级。

我找到的最简单的解决方案是将 ComboBox 属性添加到我的项目类(在您的情况下为 TableViewTest),为它创建一个公共 getter 并将其视为标准 SimpleXProperty。然后显示它,只需将组合框列的单元格值工厂设置为 PropertyValueFactory 并输入您的 getter 的名称。

以下代码是 groovy 语言,因为它是我实现项目所用的语言,但对于 Java 应该几乎相同:

请注意,ComboBoxItem 可以是您想在组合框中列出的任何类。

在我扩展 TableView 的自定义表格类中:

class CustomTable extends TableView<CustomTableItem> {

    CustomTable() {
        TableColumn columnA = new TableColumn("Column A")
        columnA.setCellValueFactory(new PropertyValueFactory<>("propertyA"))

        TableColumn<CustomTableItem, ComboBoxItem> comboBoxColumn = new TableColumn<CustomTableItem, ComboBoxItem>("ComboBox Column")
        comboBoxColumn.setCellValueFactory(new PropertyValueFactory<>("comboBoxProperty"))

        getColumns().setAll(columnA, comboBoxColumn)
    }
}

自定义表格项

class CustomTableItem {
    private SimpleStringProperty propertyA = new SimpleStringProperty()
    private ComboBox comboBox = new ComboBox()

    CustomTableItem(String someValue, List<ComboBoxItem> items) {
        propertA.setValue(someValue)
        comboBox.getItems().setAll(items)
        comboBox.setConverter(new CustomStringConverter()) // Optional
    }

    String getPropertyA() {
        return propertyA.value
    }

    ComboBox getComboBoxProperty() {
        return comboBox
    }
}

最后,作为奖励,使用StringConverter 来控制组合框中对象的显示方式

@CompileStatic
private static class CustomStringConverter extends StringConverter<ComboBoxItem> {

    @Override
    String toString(ComboBoxItem object) {
        return object?.toString()  // How the ComboBoxItem will be displayed in the combo box
    }

    @Override
    ComboBoxItem fromString(String string) {
        return null // Not used
    }
}

【讨论】:

    【解决方案2】:

    表格列的类型始终是第一个类型参数的每行中项目的类型(即与您用于表格视图的类型相同),以及每个(当前)值的类型第二个参数列中的单元格。因此,如果您的表格视图类型为TableViewTest,并且您的组合框选择Strings,您应该:

    TableColumn<TableViewTest, String> priceColumn ;
    

    单元格值工厂仍应映射到 TableViewTest 类中的属性,即假设您有:

    public class TableViewTest {
    
        // ...
    
        public StringProperty priceProperty() {
            // ...
        }
    
        // ...
    }
    

    那么你可以这样做:

    priceColumn.setCellValueFactory(new PropertyValueFactory<>("price"));
    

    或者(更好):

    priceColumn.setCellValueFactory(cellData -> cellData.getValue().priceProperty());
    

    那么你可以这样做:

    priceColumn.setCellFactory(ComboBoxTableCell.forTableColumn(options));
    

    这是一个 SSCCE:

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.scene.Scene;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.control.cell.ComboBoxTableCell;
    import javafx.scene.control.cell.TextFieldTableCell;
    import javafx.scene.layout.BorderPane;
    import javafx.stage.Stage;
    
    public class TableWithComboBoxExample extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TableView<Contact> contactTable = new TableView<>();
            contactTable.setEditable(true);
    
            TableColumn<Contact, String> nameCol = new TableColumn<>("Name");
            nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
            nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
            contactTable.getColumns().add(nameCol);
    
    
            TableColumn<Contact, String> categoryCol = new TableColumn<>("Category");
            categoryCol.setCellValueFactory(cellData -> cellData.getValue().categoryProperty());
    
            categoryCol.setCellFactory(ComboBoxTableCell.forTableColumn("Friends", "Family", "Work Contacts"));
    
            contactTable.getColumns().add(categoryCol);
    
            contactTable.getItems().addAll(
                new Contact("Bill Gates", "Work Contacts"),
                new Contact("Barack Obama", "Friends"),
                new Contact("Tim Cook", "Work Contacts")
            );
    
            Scene scene = new Scene(new BorderPane(contactTable), 600, 600);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static class Contact {
            private final StringProperty name = new SimpleStringProperty();
            private final StringProperty category = new SimpleStringProperty();
    
            public Contact(String name, String category) {
                setName(name);
                setCategory(category);
            }
    
            public final StringProperty nameProperty() {
                return this.name;
            }
    
    
            public final String getName() {
                return this.nameProperty().get();
            }
    
    
            public final void setName(final String name) {
                this.nameProperty().set(name);
            }
    
    
            public final StringProperty categoryProperty() {
                return this.category;
            }
    
    
            public final String getCategory() {
                return this.categoryProperty().get();
            }
    
    
            public final void setCategory(final String category) {
                this.categoryProperty().set(category);
            }
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 我目前正在尝试您的代码,但遇到了问题,因为我需要让它适用于 java 7,所以我不能使用 lamdas 表达式,我想我找到了一种方法来解决无法使用lamda 表达式,但我现在在 setcellFactory 方法调用上遇到了另一个错误。不兼容的类型:Callback,TableCell 无法转换为 Callback
    • Java 7 不再公开支持。在撰写本文时,与 Java 8 的发布日期相比,我们更接近 Java 9 的发布日期。如果您正在编写桌面应用程序,那么根本没有理由继续使用旧版本。对于错误:如果您使用的是TableView&lt;TableViewTest&gt;,则代码中不应有TableColumn&lt;Object, String&gt;TableCell&lt;Object, String&gt;
    • 你说得对,在那里下载了 java 8,它运行良好。感谢您的帮助
    【解决方案3】:

    James_D 的回答效果很好,但需要用户单击该项目才能看到ComboBox。如果你想在一个列中有ComboBoxes,总是显示,你必须使用自定义cellFactory

    示例:

    public class TableViewTest {
    
        ...
    
        private final StringProperty option = new SimpleStringProperty();
    
        public String getOption() {
            return option.get();
        }
    
        public void setOption(String value) {
            option.set(value);
        }
    
        public StringProperty optionProperty() {
            return option;
        }
        
    }
    
        TableColumn<TableViewTest, StringProperty> column = new TableColumn<>("option");
        column.setCellValueFactory(i -> {
            final StringProperty value = i.getValue().optionProperty();
            // binding to constant value
            return Bindings.createObjectBinding(() -> value);
        });
        
        column.setCellFactory(col -> {
            TableCell<TableViewTest, StringProperty> c = new TableCell<>();
            final ComboBox<String> comboBox = new ComboBox<>(options);
            c.itemProperty().addListener((observable, oldValue, newValue) -> {
                if (oldValue != null) {
                    comboBox.valueProperty().unbindBidirectional(oldValue);
                }
                if (newValue != null) {
                    comboBox.valueProperty().bindBidirectional(newValue);
                }
            });
            c.graphicProperty().bind(Bindings.when(c.emptyProperty()).then((Node) null).otherwise(comboBox));
            return c;
        });
    

    【讨论】:

    • 为什么要像这样将可观察对象包装在可观察对象中?当然,您可以在没有这种间接级别的情况下实现“永远在线”组合框吗?
    • @James_D:使用编辑事件进行这项工作会更加复杂。另一种选择是访问行值而不是单元格值,这是我不喜欢的,因为恕我直言,cellValueFactory 应该决定正在显示的项目的部分,而 cellFactory 应该决定该值的视觉表示。
    • 同意后者...但我认为您可以只与几个听众一起处理编辑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2018-11-08
    相关资源
    最近更新 更多