JavaFx TableView疑难详解
TableView是个十分有用的控件,适应性和灵活性非常强,可以对它进行任意的修改,比如界面样式、功能。本文将从一步步提问的方式讲解TableView
欢迎加入我的javafx探讨群:518914410
本文版权原创 by cmlanche.com, 文章链接:http://cmlanche.com/2017/06/08/JavaFx-TableView详解/
创建已知列的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的时候的回调,允许让你自己去创建
});
特别要说明的是,
setCellValueFactory和setCellFactory不是冲突的,我用的时候一直以为是冲突,就是只能用其中一个,另外一个就失效了,其实不是,setCellFactory它的意图是在创建这列的时候要做的事情,你可以改变TableCell的任何内容,包括UI和Value,而setCellValueFactory呢,它的重点是关联属性,从你传递给它的Model中通过对应属性的getter来获取值。在setCellFactory中的TableCell有个回调,叫updateItem,它可以获取到你设置到此Cell的值,这个值是跟setCellValueFactory所关联的属性有关。创建动态列的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,而是如上代码所示,通过列的索引来获取此列的值[列拖动] 如何捕获列拖动事件?
列拖动是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()) {
// 表示当前拖动过了
}
}
});
[列拖动] 如何防止第一列被拖动?
在上一问的基础上,实现第一列不允许被拖动的功能。
参考: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)用来恢复原来的列。这样就禁止拖动第一列了。[列拖动] 如何禁用列拖动效果?
给列设置一个属性:
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;
});
修改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;
}
如何自定义列头,比如自己设置一个可编辑的列头呢?
答案是给你的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疑难详解的更多相关文章
- IOS详解TableView——选项抽屉(天猫商品列表)
在之前的有篇文章讲述了利用HeaderView来写类似QQ好友列表的表视图. 这里写的天猫抽屉其实也可以用该方法实现,具体到细节每个人也有所不同.这里采用的是点击cell对cell进行运动处理以展开“ ...
- tableview 详解I
在开发iphone的应用时基本上都要用到UITableView,这里讲解一下UITableView的使用方法及代理的调用情况 UITableView使用详解 - (void)viewDidLoad { ...
- JavaFx ObservableList的使用详解
原文地址:JavaFx ObservableList的使用详解 | Stars-One的杂货小窝 最近在研究MVVM模式,发现可以将之前写的FxRecyclerView控件改造一下,便是开始尝试,尝试 ...
- TableView 常用技巧与功能详解
分割线顶格iOS8 UITableview分割线顶格的做法 //iOS8 Cell分割线顶格 if ([_tableView respondsToSelector:@selector(setSepar ...
- 服务器.htaccess 详解以及 .htaccess 参数说明(转载)
htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录.作为用户,所能使用的命令受到限 ...
- .htaccess详解及.htaccess参数说明【转】
目录(?)[-] htaccess 详解 htaccess rewrite 规则详细说明 RewriteEngine OnOff RewriteBase URL-path RewriteCond Te ...
- iPhone应用开发 UITableView学习点滴详解
iPhone应用开发 UITableView学习点滴详解是本文要介绍的内容,内容不多,主要是以代码实现UITableView的学习点滴,我们来看内容. -.建立 UITableView DataTab ...
- Cocos2d-x 3.X手游开发实例详解
Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...
- XML解析之SAX详解
XML解析之SAX详解 本文属于作者原创 http://www.cnblogs.com/ldnh/ XML解析的五个步骤 1.打开文档 (void)parserDidStartDocument:(NS ...
随机推荐
- Block Token 原理分析
介绍 文件权限检查由NameNode执行,而不是DataNode执行. 默认情况下,任何客户端都可以访问只有其块ID的任何块. 为了解决这个问题,Hadoop引入了块访问令牌的概念. 块访问令牌由Na ...
- 字符型SQL注入
字符型SQL注入 很早就基于DVWA实现了字符型的SQL注入,但是一直感觉自己没有理解的特别清楚,这次又看了一下网上的一些讲解,试着总结一下.以下是我的一写浅薄见解,请大家批判着看. 基本原理 看 ...
- 浅析如何在Nancy中使用Swagger生成API文档
前言 上一篇博客介绍了使用Nancy框架内部的方法来创建了一个简单到不能再简单的Document.但是还有许许多多的不足. 为了能稍微完善一下这个Document,这篇引用了当前流行的Swagger, ...
- Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace 解决方法
这个问题是在用到spring时,本地IDE里面跑的很正常,但是打包后在集群上运行时报错. 多方查找资料后确定了问题的根源,由于在依赖中调用了spring的许多包,会存在文件覆盖的情况. 具体是 这三个 ...
- 简单五子棋,没有电脑AI
刚学了C#委托,做了个五子棋练习,把前台绘制和后台逻辑分开,前台绘制方法用委托传给后台逻辑. 界面好简单... 先看类图 控制类控制整个游戏的逻辑,包括调用棋盘类的属性初始化棋盘.初始化两个棋手.轮流 ...
- 用ng-view创建单页APP
我们假设我们有一个单页面的程序,并且想为这个页面添加动画效果.点击某一个链接会将一个试图滑出,同时将另一个试图滑入. 我们将会使用: 使用 ngRoute 来为我们的页面路由 使用 ngAnimate ...
- JavaScript常用的方法和函数(setAttribute和getAttribute )
仅记录学习的新知识和示例,无干货. 1.setAttribute和getAttribute (Attribute:属性) setAttribute:为元素添加指定的属性,并为其赋值: ...
- Lamp环境搭建Linux CentOS6.5编译安装mysql5.6
经典web开发组合Lamp环境搭建之mysql安装详解 安装前准备 通过rpm命令检查centos上是否已经安装mysql,然后卸载已经存在的mysql版本 [root@localhost src]# ...
- zoj1654 Place the Robots 二分图最大匹配
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=654 将每一行的包含空地的区域编号 再将每一列的包含空地的区域编号 然 ...
- js算法集合(一) 水仙花数 及拓展(自幂数的判断)
js算法集合(一) ★ 最近有些朋友跟我说对js中的一些算法感到很迷惑,知道这个算法到底是怎么回事,但是就是不会用代码把它写出来,这里我跟大家分享一下做水仙花数的算法的思路,并对其扩展到自幂数的算法, ...