JavaFx TableView疑難詳解

TableView是個十分有用的控件,適應性和靈活性很是強,能夠對它進行任意的修改,好比界面樣式、功能。本文將從一步步提問的方式講解TableViewcss

<!--more-->java

  1. 建立已知列的TableView

    已知列的表格的建立,須要把TableView的TableColumn關聯到模型的屬性,TableView是個模板類,實際上是TableView<T>,這個T就是模型,例以下代碼:api

    // MyModel.java
    public class MyModel{
      private String name;
      private String url;
      // getters, setters
      ...
    }
    // init your tableView
    TableColumn<MyModel, String> t1 = new TableColumn();
    // 關聯MyModel中的name屬性
    t1.setCellValueFactory(new PropertyValueFactory<>("name")); 
    t1.setCellFactory(p->{
      // 建立此列的Cell的時候的回調,容許讓你本身去建立
    });

    特別要說明的是setCellValueFactorysetCellFactory不是衝突的,我用的時候一直覺得是衝突,就是隻能用其中一個,另一個就失效了,其實不是,setCellFactory它的意圖是在建立這列的時候要作的事情,你能夠改變TableCell的任何內容,包括UI和Value,而setCellValueFactory呢,它的重點是關聯屬性,從你傳遞給它的Model中經過對應屬性的getter來獲取值。在setCellFactory中的TableCell有個回調,叫updateItem,它能夠獲取到你設置到此Cell的值,這個值是跟setCellValueFactory所關聯的屬性有關。oracle

  2. 建立動態列的TableView

    參考:https://community.oracle.com/thread/2474328ide

    由於列是不定的,模型是沒有屬性對應的,建立列的時候你根本不知道列是什麼,看以下實現代碼:測試

    column.setCellValueFactory(param -> {
                ObservableList<VarCell> values = param.getValue();
                if (columnIndex >= values.size()) {
                    return new SimpleObjectProperty<>(null);
                } else {
                    return new SimpleObjectProperty<>(param.getValue().get(columnIndex));
                }
            });

    建立動態列的tableview,它的模型是一個ObservableList<T>,你的setCellValueFactory不能使用PropertyValueFactory,而是如上代碼所示,經過列的索引來獲取此列的值ui

  3. [列拖動] 如何捕獲列拖動事件?

    列拖動是tablview一個默認的自帶的效果,可是並無專門的事件給你去監聽它,而是監聽列的變化,方法:給tableview的columns添加Listener,判斷變更列的狀態是不是replaced的狀態,例如:this

    tableView.getColumns().addListener(new ListChangeListener<TableColumn<ObservableList<VarCell>, ?>>() {
                @Override
                public void onChanged(Change<? extends TableColumn<ObservableList<VarCell>, ?>> change) {
                    change.next();
                    if (change.wasReplaced()) {
                        // 表示當前拖動過了
                    }
                }
            });
  4. [列拖動] 如何防止第一列被拖動?

    在上一問的基礎上,實現第一列不容許被拖動的功能。url

    參考:https://stackoverflow.com/questions/30645606/javafx-restrict-column-rearrangement-on-drag-and-dropspa

    tableView.getColumns().addListener(new ListChangeListener<TableColumn<ObservableList<String>, ?>>() {
                private boolean suspended;
    
                @Override
                public void onChanged(Change<? extends TableColumn<ObservableList<String>, ?>> change) {
                    change.next();
    
                    if (change.wasReplaced() && !suspended) {
                        List<TableColumn<ObservableList<String>, ?>> oldList = new ArrayList<>(change.getRemoved());
                        List<TableColumn<ObservableList<String>, ?>> newList = new ArrayList<>(tableView.getColumns());
    
                        // first column changed => revert to original list
                        if (oldList.get(0) != newList.get(0)) {
                            this.suspended = true;
                            tableView.getColumns().setAll(oldList);
                            this.suspended = false;
                        } 
                    }
                }
            });

    上面的代碼中有三個關鍵的地方,是tableview本來提供的api,一個是change.wasReplaced表示當前的變更是否被替換了,第二個是change.getRemoved,表示獲取要移除掉的列,進一步的意思就是原來的列,也就是此前的tablecolumns,第三個是tableview.getColumns這個是獲取如今列,有了這些信息,就能夠判斷,oldList.get(0)!=newList(0),表示若是新老列的第一列不相同,表示是第一列是變更的,可是咱們不容許變更,所以,調用tableview.getColumns().setAll(oldList)用來恢復原來的列。這樣就禁止拖動第一列了。

  5. [列拖動] 如何禁用列拖動效果?

    參考:https://stackoverflow.com/questions/22202782/how-to-prevent-tableview-from-doing-tablecolumn-re-order-in-javafx-8

    給列設置一個屬性:column.impl_setReorderable(false);

    impl_setReorderable前面帶**impl_**前綴,表示它是一個未來可能會被刪除的方法,可是爲了解決目前沒法解決的問題,暫時把impl的私有方法改成了public方法,參考個人博客中的如何自定義Taborder的文章,是同樣的道理。

  6. [行拖動] 如何拖動行,進行換行?

    參考:https://stackoverflow.com/questions/28603224/sort-tableview-with-drag-and-drop-rows

    已經測試過的代碼:

    tableView.setRowFactory(tv -> {
                TableRow<ObservableList<String>> row = new TableRow<>();
    
                row.setOnDragDetected(event -> {
                    log.info("row drag detected");
                    if (!row.isEmpty()) {
                        Integer index = row.getIndex();
                        Dragboard db = row.startDragAndDrop(TransferMode.MOVE);
                        db.setDragView(row.snapshot(null, null));
                        ClipboardContent cc = new ClipboardContent();
                        cc.put(SERIALIZED_MIME_TYPE, index);
                        db.setContent(cc);
                        event.consume();
                    }
                });
    
                row.setOnDragOver(event -> {
                    log.info("row drag over");
                    Dragboard db = event.getDragboard();
                    if (db.hasContent(SERIALIZED_MIME_TYPE)) {
                        if (row.getIndex() != ((Integer) db.getContent(SERIALIZED_MIME_TYPE)).intValue()) {
                            event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
                            event.consume();
                        }
                    }
                });
    
                row.setOnDragDropped(event -> {
                    log.info("row drag dropped");
                    Dragboard db = event.getDragboard();
                    if (db.hasContent(SERIALIZED_MIME_TYPE)) {
                        int draggedIndex = (Integer) db.getContent(SERIALIZED_MIME_TYPE);
                        ObservableList<String> draggedPerson = tableView.getItems().remove(draggedIndex);
    
                        int dropIndex;
    
                        if (row.isEmpty()) {
                            dropIndex = tableView.getItems().size();
                        } else {
                            dropIndex = row.getIndex();
                        }
    
                        tableView.getItems().add(dropIndex, draggedPerson);
    
                        event.setDropCompleted(true);
                        tableView.getSelectionModel().select(dropIndex);
                        event.consume();
                    }
                });
    
                return row;
            });
  7. 修改TableView樣式

    使用css,參考以下個人測試代碼

    .table-view {
        -fx-border-width: 1px;
        -fx-border-color: #CACACA;
        -fx-background-color: transparent;
    }
    
    .table-view:focused {
        -fx-background-color: transparent;
    }
    
    .table-view .table-cell {
        -fx-font-size: 12px;
    }
    
    .table-view .filler {
        -fx-background-color: #BDE8FF;
    }
    
    .table-view .text {
        -fx-text-fill: red;
    }
    
    .table-view .column-header {
        -fx-background-color: #BDE8FF;
        -fx-pref-height: 37px;
        -fx-border-width: 1px;
        -fx-border-color: #D9D9D9;
        -fx-border-insets: -2px -2px 0px -2px;
    }
    
    .table-view .column-header-background .label {
        -fx-text-fill: #363739;
        -fx-font-weight: normal;
        -fx-font-size: 12px;
    }
    
    .table-row-cell {
        /*行高*/
        -fx-cell-size: 35px;
    }
    
    .table-row-cell .cell {
        -fx-alignment: center;
        -fx-text-fill: #333333;
    }
    
    .table-view .table-cell:selected {
        -fx-text-fill: white;
    }
    
    .table-view .table-column .column-header {
        -fx-background-color: #363739;
    }
    
    .cell {
        /*-fx-border-width: 0px 1px 0 0;*/
        /*-fx-border-color: #CACACA;*/
    }
    
    .table-view .scroll-bar {
        -fx-background-color: transparent;
    }
    
    .viewport {
        -fx-background-color: white;
    }
  8. 如何自定義列頭,好比本身設置一個可編輯的列頭呢?

    答案是給你的TableColumn設置setGraphic,可編輯的列頭的話,你讓你的graphic中有編輯框,雙擊顯示編輯框,按enter鍵確認編輯,以下是一個個人實現:

    package com.itestin.ui.datamgt.table;
    
    import com.itestin.ui.recordNreplay.logic.CommonLogic;
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.geometry.Pos;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TextField;
    import javafx.scene.control.Tooltip;
    import javafx.scene.input.KeyCode;
    import javafx.scene.layout.StackPane;
    
    /**
     * Created by cmlanche on 2017/6/1.
     */
    public class EditColumn extends StackPane {
    
        private EditColumnCallback callback;
    
        private Label label;
        private TextField textField;
    
        private StringProperty title;
        private BooleanProperty editing;
        private BooleanProperty editable;
        private BooleanProperty textFieldFocus;
        private TableColumn tableColumn;
    
        private String oldTitle;
    
        public EditColumn(TableColumn tableColumn) {
            super();
            this.tableColumn = tableColumn;
            this.init();
        }
    
        private void init() {
            this.setMaxWidth(120);
            this.setAlignment(Pos.CENTER);
            this.setStyle("-fx-background-color: #D9D9D9;");
            label = new Label();
            label.setStyle("-fx-font-size: 12px; -fx-text-fill: #333333;");
            textField = new TextField();
            textField.getStyleClass().add("tablefx-header-editor");
            label.textProperty().bindBidirectional(titleProperty());
            textField.textProperty().bindBidirectional(titleProperty());
            label.setTooltip(new Tooltip());
            label.getTooltip().textProperty().bindBidirectional(label.textProperty());
            this.getChildren().addAll(label, textField);
            editingProperty().addListener((observable, oldValue, newValue) -> {
                if (isEditable()) {
                    if (newValue) {
                        label.setVisible(false);
                        textField.setVisible(true);
                        textField.setFocusTraversable(true);
                        textField.requestFocus();
                    } else {
                        label.setVisible(true);
                        textField.setVisible(false);
                    }
                }
            });
            textField.textProperty().addListener((observable, oldValue, newValue) -> {
                textField.setStyle("-fx-border-color: #25B3FA;");
            });
            this.setStyle("-fx-background-color: transparent;");
            textField.setOnKeyPressed(event -> {
                if (isEditing()) {
                    if (event.getCode() == KeyCode.ENTER) {
                        event.consume(); // 放置對tabview的其餘列產生影響,不讓消息透傳
                        oldTitle = textField.getText();
                        setEditing(false);
                        // 提交編輯
                        if (callback != null) {
                            callback.editCommit(tableColumn, textField.getText());
                        }
                    } else if (event.getCode() == KeyCode.ESCAPE) {
                        setTitle(oldTitle);
                        setEditing(false);
                        // 取消編輯
                        if (callback != null) {
                            callback.cancelEdit();
                        }
                    } else if (event.getCode() == KeyCode.TAB) {
                        setEditing(false);
                    }
                }
            });
            textFieldFocusProperty().addListener((observable, oldValue, newValue) -> {
                if (newValue) {
                    textField.setFocusTraversable(true);
                    textField.requestFocus();
                }
            });
            setEditing(false);
        }
    
        public String getTitle() {
            return titleProperty().get();
        }
    
        public StringProperty titleProperty() {
            if (title == null) {
                title = new SimpleStringProperty();
            }
            return title;
        }
    
        public void setTitle(String title) {
            this.oldTitle = title;
            this.titleProperty().set(title);
        }
    
        public boolean isEditing() {
            return editingProperty().get();
        }
    
        public BooleanProperty editingProperty() {
            if (editing == null) {
                editing = new SimpleBooleanProperty(true);
            }
            return editing;
        }
    
        public void setEditing(boolean editing) {
            this.editingProperty().set(editing);
        }
    
        public void setEditCallback(EditColumnCallback callback) {
            this.callback = callback;
        }
    
        public boolean isEditable() {
            return editableProperty().get();
        }
    
        public BooleanProperty editableProperty() {
            if (editable == null) {
                editable = new SimpleBooleanProperty(true);
            }
            return editable;
        }
    
        public void setEditable(boolean editable) {
            this.editableProperty().set(editable);
        }
    
        public boolean isTextFieldFocus() {
            return textFieldFocusProperty().get();
        }
    
        public BooleanProperty textFieldFocusProperty() {
            if (textFieldFocus == null) {
                textFieldFocus = new SimpleBooleanProperty();
            }
            return textFieldFocus;
        }
    
        public void setTextFieldFocus(boolean textFieldFocus) {
            this.textFieldFocusProperty().set(textFieldFocus);
        }
    }

最後歡迎加入個人javafx探討羣:518914410

本文版權原創 by cmlanche.com

相關文章
相關標籤/搜索