TableView是個十分有用的控件,適應性和靈活性很是強,能夠對它進行任意的修改,好比界面樣式、功能。本文將從一步步提問的方式講解TableViewcss
<!--more-->java
已知列的表格的建立,須要把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的時候的回調,容許讓你本身去建立 });
特別要說明的是,setCellValueFactory
和setCellFactory
不是衝突的,我用的時候一直覺得是衝突,就是隻能用其中一個,另一個就失效了,其實不是,setCellFactory它的意圖是在建立這列的時候要作的事情,你能夠改變TableCell的任何內容,包括UI和Value,而setCellValueFactory呢,它的重點是關聯屬性,從你傳遞給它的Model中經過對應屬性的getter來獲取值。在setCellFactory中的TableCell有個回調,叫updateItem
,它能夠獲取到你設置到此Cell的值,這個值是跟setCellValueFactory所關聯的屬性有關。oracle
參考: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
列拖動是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()) { // 表示當前拖動過了 } } });
在上一問的基礎上,實現第一列不容許被拖動的功能。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)
用來恢復原來的列。這樣就禁止拖動第一列了。
給列設置一個屬性:column.impl_setReorderable(false);
impl_setReorderable
前面帶**impl_**前綴,表示它是一個未來可能會被刪除的方法,可是爲了解決目前沒法解決的問題,暫時把impl的私有方法改成了public方法,參考個人博客中的如何自定義Taborder的文章,是同樣的道理。
參考: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; });
使用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; }
答案是給你的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); } }
518914410
本文版權原創 by cmlanche.com