TableView是个十分有用的控件,适应性和灵活性非常强,可以对它进行任意的修改,比如界面样式、功能。本文将从一步步提问的方式讲解TableView

欢迎加入我的javafx探讨群:518914410

本文版权原创 by cmlanche.com, 文章链接:http://cmlanche.com/2017/06/08/JavaFx-TableView详解/

  1. 创建已知列的TableView

    已知列的表格的创建,需要把TableView的TableColumn关联到模型的属性,TableView是个模板类,其实是TableView,这个T就是模型,例如下代码:

    // 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所关联的属性有关。

  2. 创建动态列的TableView

    参考:https://community.oracle.com/thread/2474328

    因为列是不定的,模型是没有属性对应的,创建列的时候你根本不知道列是什么,看如下实现代码:

    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,而是如上代码所示,通过列的索引来获取此列的值

  3. [列拖动] 如何捕获列拖动事件?

    列拖动是tablview一个默认的自带的效果,但是并没有专门的事件给你去监听它,而是监听列的变化,方法:给tableview的columns添加Listener,判断变动列的状态是否是replaced的状态,例如:

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

    在上一问的基础上,实现第一列不允许被拖动的功能。

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

    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 TableView疑难详解的更多相关文章

  1. IOS详解TableView——选项抽屉(天猫商品列表)

    在之前的有篇文章讲述了利用HeaderView来写类似QQ好友列表的表视图. 这里写的天猫抽屉其实也可以用该方法实现,具体到细节每个人也有所不同.这里采用的是点击cell对cell进行运动处理以展开“ ...

  2. tableview 详解I

    在开发iphone的应用时基本上都要用到UITableView,这里讲解一下UITableView的使用方法及代理的调用情况 UITableView使用详解 - (void)viewDidLoad { ...

  3. JavaFx ObservableList的使用详解

    原文地址:JavaFx ObservableList的使用详解 | Stars-One的杂货小窝 最近在研究MVVM模式,发现可以将之前写的FxRecyclerView控件改造一下,便是开始尝试,尝试 ...

  4. TableView 常用技巧与功能详解

    分割线顶格iOS8 UITableview分割线顶格的做法 //iOS8 Cell分割线顶格 if ([_tableView respondsToSelector:@selector(setSepar ...

  5. 服务器.htaccess 详解以及 .htaccess 参数说明(转载)

    htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录.作为用户,所能使用的命令受到限 ...

  6. .htaccess详解及.htaccess参数说明【转】

    目录(?)[-] htaccess 详解 htaccess rewrite 规则详细说明 RewriteEngine OnOff RewriteBase URL-path RewriteCond Te ...

  7. iPhone应用开发 UITableView学习点滴详解

    iPhone应用开发 UITableView学习点滴详解是本文要介绍的内容,内容不多,主要是以代码实现UITableView的学习点滴,我们来看内容. -.建立 UITableView DataTab ...

  8. Cocos2d-x 3.X手游开发实例详解

    Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...

  9. XML解析之SAX详解

    XML解析之SAX详解 本文属于作者原创 http://www.cnblogs.com/ldnh/ XML解析的五个步骤 1.打开文档 (void)parserDidStartDocument:(NS ...

随机推荐

  1. SIP DB33标准笔记 监控图像获取

    实时监控图像的获取: a) 实时监控图像的获取过程应包括获取实时流.释放实时流.应使用 RFC 3261 中定义的方法INVITE 获取一个摄像机的实时监控视频流. 取消没有完成的连接应采用 CANC ...

  2. 设计模式(1)单例模式(Singleton)

    设计模式(0)简单工厂模式 源码地址 0 单例模式简介 0.0 单例模式定义 单例模式是GOF二十三中经典设计模式的简单常用的一种设计模式,单例模式的基本结构需满足以下要求. 单例模式的核心结构只有一 ...

  3. androidStudio通过svn进行版本控制

    andoridStudio配置使用svn(以windows为例) 1.先安装svn客户端程序,TortoiseSVN,注意安装过程中要勾选command line client tools(默认是不安 ...

  4. 分享一个随机更改 MAC地址 软件

    有些软件 是根据 MAC地址 来判断 是不是 已经 安装过 这个 软件 (针对 有些软件是 可以 免费 使用的 ) 要想 一直 使用 的话 只需要 修改一下 mac地址 就可以 继续 使用! 在百度中 ...

  5. UBIFS文件系统介绍

    1.  引言 UBIFS,Unsorted Block Image File System,无排序区块图像文件系统.它是用于固态硬盘存储设备上,并与LogFS相互竞争,作为JFFS2的后继文件系统之一 ...

  6. 使用PhpIniDir加载php配置文件出错,不能解析。

    我也是在apache服务器上配置php,在加载了php的配置文件之后,就出问题了. PHPIniDir "H:/php54/"增加了类似这句话之后,服务器就启动不了了,或者.php ...

  7. Stimulsoft报表操作笔记(一):统计

    一.引言 报表大家应该都知道是什么,简单来说就是用表格.图表等格式来动态显示数据.现在web系统中很多需要使用到报表统计.打印功能等,将所需用到的数据绑定到指定的位置,然后分类汇总,这样查看起来更清晰 ...

  8. fstream 学习

    #include <fstream> 引用:http://blog.csdn.net/qiang60125/article/details/5949750(fstream 常用方法详解) ...

  9. DIV+CSS 规范命名集合

    一: 命名规范说明: 1).所有的命名最好都小写 2).属性的值一定要用双引号("")括起来,且一定要有值如class="divcss5",id="d ...

  10. (转)详解JS位置、宽高属性之一:offset系列

    很多初学者对于JavaScript中的offset.scroll.client一直弄不明白,虽然网上到处都可以看一张图(图1),但这张图太多太杂,并且由于浏览器差异性,图示也不完全正确. 图一 不知道 ...