【问题标题】:JavaFX TableView category/separation rowJavaFX TableView 类别/分隔行
【发布时间】:2015-07-12 02:53:04
【问题描述】:

我无法弄清楚如何解决这个问题:

我想在我的场景图中有一个 TableView 来表示某个类(产品)。该类有一个字符串类型字段,我想用它来对产品进行分类。因此,我想在循环中填充 TableView,添加所有产品,同时在每种特定类型的产品之前添加一个“类别”行。

因此,如果我有例如 10 种产品,假设其中 6 种是 'Alcohol' 类型,其余是 'Food' 类型,则 TableView 的第一行将其 'name' 列设置为“ALCOHOL”其余的列将是空白的,而整行的格式会略有不同(主要是 bg 颜色和字体)。然后在“酒精”类型的最后一个产品之后会有另一行类似的,说“食物”等。

任何想法我该怎么做?据我所知,一个 TableView 只能代表一个类,我不能创建多个表,因为滚动时我需要固定列标题功能。

非常感谢。

【问题讨论】:

    标签: javafx tableview


    【解决方案1】:

    您几乎可以使用 CSS 完成所有这些操作,然后在桌子上使用 rowFactory 和一个单元工厂,它只是为您想要为后续项目“隐藏”的单元格设置一个 css 类。同类型。

    rowFactory中,创建一个TableRow,观察表中的item列表,观察自己的index,根据是否是它的第一项在行上设置一个CSS PsuedoClass类型:

        PseudoClass firstOfTypePseudoclass = PseudoClass.getPseudoClass("first-of-type");
    
        table.setRowFactory(t -> {
            TableRow<Product> row = new TableRow<>();
            InvalidationListener listener = obs -> 
                row.pseudoClassStateChanged(firstOfTypePseudoclass, 
                        isFirstOfType(table.getItems(), row.getIndex()));
            table.getItems().addListener(listener);
            row.indexProperty().addListener(listener);
            return row ;
        });
    

    对于显示类型的列,cell factory实现只是标准实现,只是在cell上设置了一个css类:

        TableColumn<Product, Product.Type> typeColumn = ... ;
        typeColumn.setCellFactory(c -> {
            TableCell<Product, Product.Type> cell = new TableCell<Product, Product.Type>() {
                @Override
                public void updateItem(Product.Type type, boolean empty) {
                    super.updateItem(type, empty);
                    if (type == null) {
                        setText(null);
                    } else {
                        setText(type.toString());
                    }
                }
            };
            cell.getStyleClass().add("type-cell");
            return cell ;
        });
    

    然后附加一个外部样式表。您可以随意设置table-row-cell:first-of-type 的样式。然后只需将类型列中的单元格设置为不可见,除非它们是table-row-cell:first-of-type 的子节点:

    .table-row-cell:first-of-type {
        -fx-background-color: antiquewhite ;
    }
    .table-row-cell:first-of-type:odd {
        -fx-background-color: derive(antiquewhite, 20%);
    }
    .table-row-cell .type-cell {
        visibility: hidden ;
    }
    .table-row-cell:first-of-type .type-cell {
        visibility: visible ;
    }
    

    将该样式表保存为 first-of-type-table.css,以下完整示例可以满足您的需求:

    import java.util.Comparator;
    import java.util.List;
    import java.util.function.Function;
    
    import javafx.application.Application;
    import javafx.beans.InvalidationListener;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.collections.transformation.SortedList;
    import javafx.css.PseudoClass;
    import javafx.geometry.HPos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableCell;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableRow;
    import javafx.scene.control.TableView;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.ColumnConstraints;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.Priority;
    import javafx.stage.Stage;
    
    public class FirstOfTypeTableExample extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TableView<Product> table = new TableView<>() ;
    
            ObservableList<Product> products = FXCollections.observableArrayList();
            table.setItems(new SortedList<>(products, Comparator.comparing(Product::getType)));
    
            TableColumn<Product, Product.Type> typeColumn = column("Type", Product::typeProperty);
            typeColumn.setCellFactory(c -> {
                TableCell<Product, Product.Type> cell = new TableCell<Product, Product.Type>() {
                    @Override
                    public void updateItem(Product.Type type, boolean empty) {
                        super.updateItem(type, empty);
                        if (type == null) {
                            setText(null);
                        } else {
                            setText(type.toString());
                        }
                    }
                };
                cell.getStyleClass().add("type-cell");
                return cell ;
            });
    
            table.getColumns().add(typeColumn);
            table.getColumns().add(column("Name", Product::nameProperty));
            table.getColumns().add(column("Price", Product::priceProperty));
    
            PseudoClass firstOfTypePseudoclass = PseudoClass.getPseudoClass("first-of-type");
    
            table.setRowFactory(t -> {
                TableRow<Product> row = new TableRow<>();
                InvalidationListener listener = obs -> 
                    row.pseudoClassStateChanged(firstOfTypePseudoclass, 
                            isFirstOfType(table.getItems(), row.getIndex()));
                table.getItems().addListener(listener);
                row.indexProperty().addListener(listener);
                return row ;
            });
    
            products.addAll(
                    new Product("Chips", 1.99, Product.Type.FOOD),
                    new Product("Ice Cream", 3.99, Product.Type.FOOD),
                    new Product("Beer", 8.99, Product.Type.DRINK),
                    new Product("Laptop", 1099.99, Product.Type.OTHER));
    
            GridPane editor = createEditor(products);
    
            BorderPane root = new BorderPane(table, null, null, editor, null) ;
            Scene scene = new Scene(root, 600, 400);
            scene.getStylesheets().add("first-of-type-table.css");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        private boolean isFirstOfType(List<Product> products, int index) {
            if (index < 0 || index >= products.size()) {
                return false ;
            }
            if (index == 0) {
                return true ;
            }
            if (products.get(index).getType().equals(products.get(index-1).getType())) {
                return false ;
            } else {
                return true ;
            }
        }
    
        private GridPane createEditor(ObservableList<Product> products) {
            ComboBox<Product.Type> typeSelector = new ComboBox<>(FXCollections.observableArrayList(Product.Type.values()));
            TextField nameField = new TextField();
            TextField priceField = new TextField();
            Button add = new Button("Add");
            add.setOnAction(e -> {
                Product product = new Product(nameField.getText(), 
                        Double.parseDouble(priceField.getText()), typeSelector.getValue());
                products.add(product);
                nameField.setText("");
                priceField.setText("");
            });
    
            GridPane editor = new GridPane();
            editor.addRow(0, new Label("Type:"), typeSelector);
            editor.addRow(1, new Label("Name:"), nameField);
            editor.addRow(2, new Label("Price:"), priceField);
            editor.add(add, 3, 0, 2, 1);
    
            GridPane.setHalignment(add, HPos.CENTER);
            ColumnConstraints leftCol = new ColumnConstraints();
            leftCol.setHalignment(HPos.RIGHT);
            leftCol.setHgrow(Priority.NEVER);
    
            editor.getColumnConstraints().addAll(leftCol, new ColumnConstraints());
            editor.setHgap(10);
            editor.setVgap(5);
            return editor;
        }
    
    
        private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
            TableColumn<S, T> col = new TableColumn<>(title);
            col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
            return col ;
        }
    
    
        public  static class Product {
    
    
            public enum Type {FOOD, DRINK, OTHER }
    
            private final ObjectProperty<Type> type = new SimpleObjectProperty<>();
            private final StringProperty name = new SimpleStringProperty();
            private final DoubleProperty price = new SimpleDoubleProperty();
    
            public Product(String name, double price, Type type) {
                setName(name);
                setPrice(price);
                setType(type);
            }
    
            public final StringProperty nameProperty() {
                return this.name;
            }
            public final java.lang.String getName() {
                return this.nameProperty().get();
            }
            public final void setName(final java.lang.String name) {
                this.nameProperty().set(name);
            }
            public final DoubleProperty priceProperty() {
                return this.price;
            }
            public final double getPrice() {
                return this.priceProperty().get();
            }
            public final void setPrice(final double price) {
                this.priceProperty().set(price);
            }
            public final ObjectProperty<Type> typeProperty() {
                return this.type;
            }
            public final FirstOfTypeTableExample.Product.Type getType() {
                return this.typeProperty().get();
            }
            public final void setType(final FirstOfTypeTableExample.Product.Type type) {
                this.typeProperty().set(type);
            }
    
    
        }
    
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-27
      • 2016-02-16
      • 2020-06-30
      • 2018-11-18
      • 2019-10-09
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      • 2015-01-01
      相关资源
      最近更新 更多