2015-05-02 36 views
1

我有麻烦找出如何处理这个问题:JavaFX TableView类别/分隔行

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

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

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

非常感谢。

回答

2

你几乎可以用CSS完成所有这些工作,然后在表格上添加一个rowFactory的小魔术,以及一个细胞工厂,它为你想隐藏的细胞设置一个css类,用于后续的相同项目类型。

rowFactory,创建一个TableRow是观察表中的项目列表,并遵守自己的索引,并将该行一个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 ; 
    }); 

为了显示类型的柱,该细胞工厂实现仅仅是一个标准的实现,但在小区设置一个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 ; 
} 

并另存为第一的型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); 
    } 
}