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. js中元素(图片)切换和隐藏显示问题

    这个知识点其实也简单,(当然是在理清思路的情况下),在没预习的情况下听的还真是艰难,上课以来唯一的一次懵逼了一天,感觉乱乱的,全是新属性,所以今晚的我破天荒的熬夜敲代码,一定要弄懂! 现在就来梳理下头 ...

  2. Python之路- 反射&定制自己的数据类型

    一.isinstance和issubclass isinstance(obj,cls)检查是否obj是否是类 cls 的对象 issubclass(sub, super)检查sub类是否是 super ...

  3. python在接口测试的实际应用

    今天看到@51Testing软件测试网 关于"关于接口测试的总结"的文章,进去浏览了一下,文章主要是针对接口测试的理论性的描述,而实战的内容没有涉及到.刚好今天我将我的系列文章的& ...

  4. Machine Learning——Supervised Learning(机器学习之监督学习)

    监督学习是指:利用一组已知类别的样本调整分类器的参数,使其达到所要求性能的过程. 我们来看一个例子:预测房价(注:本文例子取自业界大牛吴恩达老师的机器学习课程) 如下图所示:横轴表示房子的面积,单位是 ...

  5. VMWare下ubuntu无法全屏的问题解决

    遇到的情况: 在VMWare中,安装ubuntu 最新版操作系统(16.04).运行该系统,发现ubuntu系统在虚拟机中,只能居中显示,全屏也只能占一半显示屏幕.怎么看,怎么不舒服. 分析问题: 一 ...

  6. SCP“免密” 远程COPY较多文件

    一.linux脚本实现自动输入密码 使用Linux的程序员对输入密码这个举动一定不陌生,在Linux下对用户有严格的权限限制,干很多事情越过了权限就得输入密码,比如使用超级用户执行命令,又比如ftp. ...

  7. Java数据类型(基本数据类型)学习

    Java数据类型(基本数据类型)学习 与其他语言一样,Java编程同样存在,比如int a,float b等.在学习变量之前我就必须先了解Java的数据类型啦. Java的数据类型包括基本数据类型和引 ...

  8. yum 安装redis 及简单命令(推荐测试环境,安装简单)

    第1章 redis 入门 1.1 yum 安装 安装repo源 cd /etc/yum.repos.d/ wget http://mirrors.aliyun.com/repo/epel-6.repo ...

  9. LNMP源码编译安装

    思路:根据Linux系统以及公司网站系统的信息,选择合适的安装包进行安装 一.查看系统信息 # uname -a                        # 查看内核/操作系统/CPU信息 # ...

  10. SSH免密码(日志三)

    上一篇:JDK安装以及安装过程中出现的问题(日志二) 原理,就是RSA加密,含有公钥和私钥,具体言之,用公钥来确认请求人是否是私钥的持有人. 1, 2, 3, 4, ssh免密码过程中遇到的问题:需要 ...