表格(单元格放置组件)

对于JTable单元格的渲染主要是通过两个接口来实现的,一个是TableCellRenderer另一个是TableCellEditor,JTable默认是用的是DefaultCellRenderer和DefaultCellEditor,这两个都是在类似JTextfield的一个JComponent的基础上来实现的,如果我们需要在JTable的单元格内放置特殊的控件或者绘制出特殊的效果,就要实现TableCellRenderer和TableCellEditor接口,在其上绘制出自己需要的样式,再通过JTable的setCellRenderer和setCellEditor方法设置新的外观呈现.

首先我们先看看TableCellRenderer和TableCellEditor接口的区别, TableCellRenderer接口就是用来绘制和展示当前单元格的内容的,可以用文字、图片、组件、甚至Java2D来绘制效果; TableCellEditor主要是用来当用户点击具体的某个单元格进行编辑的时候来展现的,除了绘制之外,在点击时还会有更加复杂的效果出现.

先看Sun官方给的简单的例子,首先是TableCellRenderer的

运行图示如下:

我们只需要实现TableCellRenderer就可以了,

/**

* This interface defines the method required by any object * that would like to be a renderer for cells in a JTable

* in there, I put button in it.

*/

publicclass MyButtonRenderer extends JButton implements TableCellRenderer {

实现接口的方法:

@Override

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

然后设置属性:

setForeground(table.getSelectionForeground());

setBackground(table.getSelectionBackground());

setText((value == null) ? "" : value.toString());

使用也很简单,假如我们希望第一列是JButton,则

table.getColumnModel().getColumn(0).setCellRenderer(new MyButtonRenderer());

接着是TableCellEditor的实现,还是Sun给的例子:

运行图示如下

Sun公司在DefaultCellEditor类里提供了JComboBox参数的构造函数,直接使用就可以了.

//Set up the editor for the sport cells.

JComboBox comboBox = new JComboBox();

comboBox.addItem("Snowboarding");

comboBox.addItem("Rowing");

comboBox.addItem("Knitting");

comboBox.addItem("Speed reading");

comboBox.addItem("Pool");

comboBox.addItem("None of the above");

table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));

在这里看来,这个例子就可以了,但是它还是有问题的,什么问题呢,看下截图:

当JTable的单元格比较短时,下拉框显示的内容会出现不全的情况,需要修改一下:

问题在哪儿呢,在于JCombobox的UI,需要设置一下JCombobox的下拉菜单的宽度,具体实现在JCombobox那篇文章里已经实现了,这里我们直接使用,

String[] str = new String[] { "Snowboarding", "Rowing", "Knitting", "Speed reading", "None of the above" };

MyComboBox combo = new MyComboBox(str);

Dimension d = combo.getPreferredSize();

combo.setPopupWidth(d.width);

table.getColumnModel().getColumn(2).setCellEditor(newDefaultCellEditor(combo));

运行如下图:

到此为止,Renderer和Editor的简单实用就完成了,这些例子都是Sun官方给的,我大概

修改了一下,其实还有问题.

让我们回头看第一个例子:

当鼠标在JButton按下时,如下图:

JButton的效果消失了,因为Renderer只是处理表示的样式,对于可编辑的单元格就不可

以了,编辑状态下呈现的还是默认的JTextField组件,所以对于可编辑的单元格,我们需

要设置它的Editor.

我们需要写一个自己的Editor,为了简单就不实现TableCellEditor接口了,只需要继

承DefaultCellEditor.

/**

* The default editor for table and tree cells.

*/

publicclass MyButtonCellEditor extends DefaultCellEditor {

 

定义两个属性:

//editor show

private JButton button = null;

//text

private String label = null;

分别代表编辑状态下显示的组件和显示的值.

然后重写getTableCellEditorComponent方法,在编辑状态表示我们自己的组件.

/**

* Sets an initial <code>value</code> for the editor.

*/

@Override

public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

设置组件样式:

button.setForeground(table.getSelectionForeground());

button.setBackground(table.getSelectionBackground());

label = (value == null) ? "" : value.toString();

button.setText(label);

returnbutton;

然后还需要重写getCellEditorValue方法,返回编辑完成后的值,

@Override

public Object getCellEditorValue() {

returnnew String(label);

}

使用和以前设置Renderer和Editor一样,设置2个就可以了.

table.getColumnModel().getColumn(0).setCellRenderer(new MyButtonRenderer());

table.getColumnModel().getColumn(0).setCellEditor(new MyButtonCellEditor());

最后按下效果正常了:

到此为止,简单的Renderer和Editor就差不多了,但是我们在JTable放置的都是基本的Swing组件,可不可以放置复杂的呢,当然是可以的,下面我们放置一个选择组:

如下图:

它也需要实现自己的Renderer和Editor,我们可以把这个显示选择按钮组的单元格看做一个组件,当然首先就是把这个组件作出来:

/**

* create the pane that some radio pane in it.

*/

publicclass MyRadioPanel extends JPanel {

它只有一个属性,根据给定数组长度构建Radio数组,

/** radio button group. */

private JRadioButton[] buttons = null;

再看它的构造函数:

public MyRadioPanel(String[] strButtonText) {

我们在这里构造JRadioButton:

buttons[i] = new JRadioButton(strButtonText[i]);

加入到面板:

add(buttons[i]);

再添加取得和设置JRadioButton选择的方法:

/**

* get button groups.

*/

public JRadioButton[] getButtons() {

returnbuttons;

}

/**

* set which index select.

*/

publicvoid setSelectedIndex(int index) {

for (int i = 0; i < buttons.length; i++) {

buttons[i].setSelected(i == index);

}

}

然后就是写Renderer了,我们继承MyRadioPanel并且实现TableCellRenderer接口就可以了.

publicclass MyRadioCellRenderer extends MyRadioPanel implements

TableCellRenderer {

构造函数直接使用MyRadioCellRenderer的

public MyRadioCellRenderer(String[] strButtonTexts) {

super(strButtonTexts);

}

然后是实现接口的getTableCellRendererComponent方法:

@Override

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

if (value instanceof Integer) {

setSelectedIndex(((Integer) value).intValue());

}

returnthis;

}

最后就是Editor了,

/**

* create cell editor that radio in it.

*/

publicclass MyRadioCellEditor extends DefaultCellEditor implements

ItemListener {

在它的构造函数里我们为JRadioButton添加监听:

JRadioButton[] buttons = panel.getButtons();

buttons[i].addItemListener(this);

在监听处理中我们停止编辑,

@Override

publicvoid itemStateChanged(ItemEvent e) {

super.fireEditingStopped();

}

然后我们需要覆盖DefaultCellEditor的getTableCellEditorComponent,返回我们需要显示的MyRadioPanel.

@Override

public Component getTableCellEditorComponent(JTable table, Object value,

boolean isSelected, int row, int column) {

if (value instanceof Integer) {

panel.setSelectedIndex(((Integer) value).intValue());

}

returnpanel;

}

最后我们重写getCellEditorValue,返回编辑完成后我们显示的值:

@Override

public Object getCellEditorValue() {

returnnew Integer(panel.getSelectedIndex());

}

使用也很简单,和前面设置Renderer和Editor一样:

String[] answer = { "A", "B", "C" };

table.getColumnModel().getColumn(1).setCellRenderer(

new MyRadioCellRenderer(answer));

table.getColumnModel().getColumn(1).setCellEditor(

new MyRadioCellEditor(newMyRadioCellRenderer(answer)));

接下来我们看一个比较综合的例子,首先还是从画面开始:

先从简单的开始做起,首先使JTable的第三列显示成进度条,这个和前面的设置Renderer一样,实现TableCellRenderer就可以了.

/**

* This interface defines the method required by any object * that would like to be a renderer for cells in a JTable

* in there, I put progress bar in it.

*/

publicclass MyProgressCellRenderer extends JProgressBar implements

TableCellRenderer {

它提供一个属性放置各个颜色区间需要设置的颜色:

/** the progress bar's color. */

private Hashtable<Integer, Color> limitColors = null;

在构造函数里我们设置显示的最大和最小值:

/**

* Creates a progress bar using the specified orientation, * minimum, and maximum.

*/

public MyProgressCellRenderer(int min, int max) {

super(JProgressBar.HORIZONTAL, min, max);

setBorderPainted(false);

}

然后实现TableCellRenderer接口的getTableCellRendererComponent方法,设置显示组件和颜色:

@Override

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

先根据单元格的值取得颜色:

Color color = getColor(n);

if (color != null) {

setForeground(color);

}

同时设置JProcessBar的值并返回它.

setValue(n);

returnthis;

最后还提供一个设置颜色的方法:

publicvoid setLimits(Hashtable<Integer, Color> limitColors) {

它把传入的颜色表按照大小先排序,然后设置好.

这样一个简单的显示进度条的TabelCellRenderer就完成了.然后通过setRenderer来使用它.

//create renderer.

MyProgressCellRenderer renderer = new MyProgressCellRenderer(

MyProgressTableModel.MIN, MyProgressTableModel.MAX);

renderer.setStringPainted(true);

renderer.setBackground(table.getBackground());

// set limit value and fill color

Hashtable<Integer, Color> limitColors = new Hashtable<Integer, Color>();

limitColors.put(new Integer(0), Color.green);

limitColors.put(new Integer(20), Color.GRAY);

limitColors.put(new Integer(40), Color.blue);

limitColors.put(new Integer(60), Color.yellow);

limitColors.put(new Integer(80), Color.red);

renderer.setLimits(limitColors);

//set renderer      table.getColumnModel().getColumn(2).setCellRenderer(renderer);

然后我们需要考虑的是这个Renderer的值无法变化,只能根据初始化的时候的数值显示,这明显是不行的,所以我们考虑给JTable加上改变,改变第二列的数字,第三列进度条随之改变,如图示:


这时我们需要修改我们的TableModel,默认的已经无法满足我们的需要了,我们需要自己写一个:

publicclass MyProgressTableModel extends DefaultTableModel {

在它的构造函数里面,我们增加一个监听:

this.addTableModelListener(new TableModelListener() {

@Override

publicvoid tableChanged(TableModelEvent e) {

当引起TableModel改变的事件是UPDATE时并且是第二列时候:

//when table action is update.

if (e.getType() == TableModelEvent.UPDATE) {

int col = e.getColumn();

if (col == 1) {

我们取得新设立的value,赋予第三列:

//get the new set value.

Integer value = (Integer) model.getValueAt(row, col);

model.setValueAt(checkMinMax(value), row, ++col);

重写isCellEditable方法,设置可编辑的列:

@Override

publicboolean isCellEditable(int row, int col) {

switch (col) {

case 1:

returntrue;

default:

returnfalse;

}

}

重写setValueAt方法,设置可赋予的值:

@Override

publicvoid setValueAt(Object obj, int row, int col) {

这样一个我们需要的TableModel就完成了,修改第二列的值,第三列进度条也随之改变,使用也很简单:

// set the table model.

table.setModel(dm);

就可以了.

到这里,这个进度条JTable基本完成了,但是在实际运用中可能会出现这样的问题:

我们编辑JTable的时候给它的单元格赋予了一个不正常的值,导致显示不正常,但是却无法返回旧有的状态,这样我们就需要再次改进它:

当输入错误的值时:

然后可以返回以前的状态:

这时候我们需要设置的是第二列的Editor,使它编辑状态时可以验证我们的输入,并触发:

/**

* Implements a cell editor that uses a formatted text

* field to edit Integer values.

*/

publicclass MyIntegerEditor extends DefaultCellEditor {

它有一个参数,用来处理编辑值的:

//show component when cell edit

private JFormattedTextField ftf;

然后重写DefaultCellEditor的getTableCellEditorComponent方法,返回我们定义的JFormattedTextField.

JFormattedTextField ftf = (JFormattedTextField) super

.getTableCellEditorComponent(table, value, isSelected, row, column); ftf.setValue(value);

return ftf;

重写getCellEditorValue方法,保证我们返回值正确:

getCellEditorValue

@Override

public Object getCellEditorValue() {

取得编辑完成的值:

Object o = ftf.getValue();

判断然后返回.

然后重写stopCellEditing方法,判断编辑的值是否正确,不正确的情况下提示用户,询问用户是返回还是重新设置.

// Override to check whether the edit is valid,

// setting the value if it is and complaining if it isn't.

@Override

publicboolean stopCellEditing() {

JFormattedTextField ftf = (JFormattedTextField) getComponent();

if (ftf.isEditValid()) {

try {

ftf.commitEdit();

catch (java.text.ParseException exc) {

}

else { // text is invalid

if (!userSaysRevert()) {

// user wants to edit don't let the editor go away

returnfalse;

}

}

returnsuper.stopCellEditing();

}

到目前为止,这个类基本完成了,但是只有焦点离开单元格才触发验证事件,比较不和逻辑,我们加入一个键盘监听,回车也可以触发.

ftf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");

ftf.getActionMap().put("check", new AbstractAction() {

@Override

publicvoid actionPerformed(ActionEvent e) {

// The text is invalid.

if (!ftf.isEditValid()) {

if (userSaysRevert()) {

// reverted inform the editor

ftf.postActionEvent();

}

else

try {

// The text is valid, so use it.

ftf.commitEdit();

// stop editing

ftf.postActionEvent();

catch (java.text.ParseException exc) {

}

}

然后就可以使用它了,和前面设置一个Editor一样:

table.getColumnModel().getColumn(1).setCellEditor(

new MyIntegerEditor(MyProgressTableModel.MIN,

MyProgressTableModel.MAX));

到目前为止,JTable的Renderer和Editor就完成了,实际使用中也就这样了,但是还有一种特殊情况需要说一下,虽然这样变态需求一般现实中很难碰到.上面我们所有的例子都是对某一个列来说的,但是如果有人需要第一行显示正常单元格,第二行显示JCombobox,第三行显示JButton怎么处理呢.其实也相差不大,自己写个Renderer和Editor,里面实现一个Renderer和Editor的序列,依次展现就可以了.

先看图:


首先要做的写一个类实现TableCellEditor接口,

publicclass MyCellEditor implements TableCellEditor {

它有两个属性:

/** save all editor to it. */

private Hashtable<Integer, TableCellEditor> editors = null;

/** each cell editor. */

private TableCellEditor editor = null;

分别存储了此Editor上所有的Editor队列和当前需要使用的Editor.

再看它的构造函数,

/**

* Constructs a EachRowEditor. create default editor

*/

public MyCellEditor(JTable table) {

它初始化了Editor队列

editors = new Hashtable<Integer, TableCellEditor>();

然后实现TableCellEditor接口的getTableCellEditorComponent方法

@Override

public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {

根据行号取得当前单元格的Editor:

editor = (TableCellEditor) editors.get(new Integer(row));

没有的话,使用默认的:

if (editor == null) {

editor = new DefaultCellEditor(new JTextField());

}

然后返回当前Renderer下的单元格:

returneditor.getTableCellEditorComponent(table, value, isSelected, row, column);

接着实现stopCellEditing、cancelCellEditing、addCellEditorListener、

removeCellEditorListener、isCellEditable、shouldSelectCell方法,

在这些方法里取得当前那个单元格被编辑,取得正编辑的单元格的Editor,再调用Editor

同样的方法就可以了.

if (e == null) {

row = table.getSelectionModel().getAnchorSelectionIndex();

else {

row = table.rowAtPoint(e.getPoint());

}

editor = (TableCellEditor) editors.get(new Integer(row));

if (editor == null) {

editor = new DefaultCellEditor(new JTextField());

}

最后提供一个设置单元格Editor的方法,

/**

* add cell editor to it.

*/

publicvoid setEditorAt(int row, TableCellEditor editor) {

editors.put(new Integer(row), editor);

}

这样可以实现单元格级别的Editor就实现了,同样的Renderer也一样,同样实现TableCellRenderer接口和它里面的方法就可以了,同样用对列存储每个单元格的Renderer,这里就不写了.

最后是使用:

先创建JTable需要用到的Editor,再创建单一Cell用到的Editor,

//create all cell editor

MyCellEditor rowEditor = new MyCellEditor(table);

//create cell editors

MyButtonCellEditor buttonEditor = new MyButtonCellEditor();

DefaultCellEditor comboBoxEditor = new

DefaultCellEditor(comboBox);

然后为需要的单元格设置Editor,

//put cell editor in all cell editors

rowEditor.setEditorAt(0, comboBoxEditor);

rowEditor.setEditorAt(1, comboBoxEditor);

rowEditor.setEditorAt(2, buttonEditor);

rowEditor.setEditorAt(3, buttonEditor);

最后设置JTable的Editor,

//set table editor

table.getColumnModel().getColumn(0).setCellEditor(rowEditor);

同样的,Renderer和Editor完全一样.这样一个可以为具体单元格设置Renderer和Editor的例子就完成了.

到此为止,关于在JTable的单元格放置组件的例子就全部完成了,总结起来也很简单,就是设置Renderer和Editor,至于更复杂的效果,比如合并单元格之类的,就需要重写JTable的TableUI了,这就在以后说了

在JTable单元格上 加入组件,并赋予可编辑能力 [转]的更多相关文章

  1. JTable 单元格合并 【转】

    单元格合并 一.单元格合并.(1)我们可以使用Jtable的三个方法:getCellRect(),columnAtPoint(),and rowAtPoint().第一个方法返回一个单元格的边界(Re ...

  2. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 表格:将悬停的颜色应用在行或者单元格上

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. JTable单元格放自定义控件(一)-如何在JTable的单元格放JPanel

    原文链接:http://blog.sina.com.cn/s/blog_7f1c8c710101hdpf.html 最近自己尝试着模仿着实现一款非常有名的进销库存管理系统(智慧记)里面的一个功能.功能 ...

  4. [办公应用]如何将excel合并单元格分拆后每个单元格上仍保留数据?

    合并单元格虽然美观,但是无法进行排序.筛选等操作. 只有合并单元格拆分后才可以按常规进行统计.但是普通拆分后,excel仅保留合并单元格数据到区域左上角的单元格. 解决方案:选定多个合并单元格,应用本 ...

  5. easyUI的doCellTip 就是鼠标放到单元格上有个提示的功能

    1:这个东西是我抄的(抄的哪儿的我就想不起来了- -)弹出的窗没有样式  不是很好看 //扩展 $.extend($.fn.datagrid.methods, { /** * 开打提示功能 * @pa ...

  6. DEV中右键菜单如何只在非空单元格上显示?

    问题: 1. 开发时,我的winform程序中有很多gridview,我希望右键菜单只在我点击非空的行时才显示,点击其他空白区域时不显示: 2. 有一个树状导航图,treelist 中的节点都有右键菜 ...

  7. [从产品角度学excel 04]-单元格的“衣服”

    忘记发这里了..补发一下 这是<从产品角度学EXCEL>系列——单元格篇. 前言请看: 0 为什么要关注EXCEL的本质 1 excel是怎样运作的 2 EXCEL里的树形结构 3 单元格 ...

  8. DataGridView的单元格如何嵌入多个按钮控件

    前段时间我有一个朋友面试公司的时候遇到这个面试题,他也给了份原题给我瞧瞧,并没有什么特别的要点,关于这一类问题,如何在网格上的单元格嵌入多个控件(如按钮.超链接等)问题,我在网上搜索了下这类问题,发现 ...

  9. java表格的使用 单元格绘制二

    JTable单元格是由单元格绘制器绘制出来的,这是一些执行TableCellRenderer接口的类.TableCellRenderer接口定义了唯一的getTableCellRendererComp ...

随机推荐

  1. Redis源码分析(adlist)

    源码版本:redis-4.0.1 源码位置: adlist.h : listNode.list数据结构定义. adlist.c:函数功能实现. 一.adlist简介 Redis中的链表叫adlist( ...

  2. Linux&C 线程控制 课后习题

    Q1:多线程与多进程相比有什么优势? 多进程程序耗费的资源大,因为fork()的时候子进程需要继承父进程的几乎所有东西,但是多线程程序线程只继承一部分,即自己的私有数据,例如自己的线程ID,一组寄存器 ...

  3. Typecho 反序列化漏洞 分析及复现

    0x00 漏洞简介 CVE-2018-18753 漏洞概述: typecho 是一款非常简洁快速博客 CMS,前台 install.php 文件存在反序列化漏洞,通过构造的反序列化字符串注入可以执行任 ...

  4. 腾讯发布 K8s 多集群管理开源项目 Clusternet

    11月4日,在腾讯数字生态大会上,腾讯宣布了云原生领域一项重磅开源进展-- K8s 多集群管理项目 Clusternet 正式开源. Clusternet 由腾讯联合多点生活.QQ音乐.富途证券.微众 ...

  5. Leetcode 课程表 C++ 图的深度搜索和广度搜索练习

    广度搜索(degree) struct GraphNode{ int label; vector<GraphNode*> neighbours; GraphNode(int x):labe ...

  6. part 36 AngularJS route reload

    In this video we will discuss angular route service reload() method. This method is useful when you ...

  7. AtCoder Regular Contest 127

    Portal B Description 给出\(n(\leq5\times10^4),L(\leq15)\),构造\(3n\)个不同\(L\)位的三进制数,使得在这\(3n\)个数的每一位上,0/1 ...

  8. koa2使用ejs模板引擎

    在koa中使用ejs并不需要像在node中一样安装了还要引用,只需要npm了就行,同时还需要安装koa-views模块.如: const views = require('koa-views'); 对 ...

  9. [loj6278]数列分块入门2

    做法1 以$K$为块大小分块,并对每一个块再维护一个排序后的结果,预处理复杂度为$o(n\log K )$ 区间修改时将整块打上标记,散块暴力修改并归并排序,单次复杂度为$o(\frac{n}{K}+ ...

  10. [atAGC052D]Equal LIS

    令$f_{i}$表示以$i$为结尾的最长上升子序列,显然可以快速预处理 令$L=\max_{i=1}^{n}f_{i}$,当$L$为偶数,考虑如下构造-- 将所有$f_{i}\le \frac{L}{ ...