表格(单元格放置组件)

对于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. DeWeb和WebXone的区别

    DeWeb和WebXone的区别 相同点: 1 两者为同一开发者研发.QQ:45300355,碧树西风 2 都是为了解决Delphi开发Web的问题 区别: 1 WebXone采用的ActiveX/N ...

  2. zabbix 自定义监控项,监控tomcat访问量

    uv:访客量.每个独立上网电脑视为一位访客.pv:访问量.页面浏览量或者点击量,访客每访问一次记录一次. 1.创建文件 /home/zabbix/pvuv_number.sh [ #/bin/bash ...

  3. OpenXml SDK学习笔记(4):设置文件级别的样式

    观察上一段日记最后的代码: 这里的样式基本可以理解为行内CSS.那么既然有行内的样式,就肯定有外部的样式.那这部分就对应笔记1里说的style.xml文件.这个文件对应的是Document.MainD ...

  4. 利用DNS缓存和TLS协议将受限SSRF变为通用SSRF

    本文首发于先知社区 前言 这是今年BlackHat上的一个议题:When TLS Hacks You,作者是latacora的Joshua Maddux 议题提出了一个新的ssrf攻击思路,利用DNS ...

  5. Java踩坑之List的removeAll方法

    最近在公司写东西,发现List的removeAll方法报错 Demo代码如下: List<Long> ids1 = Arrays.asList(1L, 3L, 2L); List<L ...

  6. Linux——搭建FTP服务

    一.FTP基本概念: 1.FTP的作用: 实现文件系统的安全匿名访问:包括上传.下载和查看,可以应用于Windows和Linux系统 2.FTP的工作原理 server与client都支持ftp传输协 ...

  7. 查看python是32位,还是64位

    步骤:cmd打开命令行,输入python,查看. 如果32bit,则是32位:如果是64,则是64位 如果需要安装客户端进行orcale数据库操作,则要保证python\

  8. WinForm训练一_改变窗体大小

    1 //引用系统命名空间 2 using System; 3 //项目命名空间 4 using System.Collections.Generic; 5 using System.Component ...

  9. [hdu5901]Count primes

    最简单的是利用Min25筛求$h(n)$的过程,即 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1000005 ...

  10. [bzoj1264]基因匹配

    首先朴素dp的方程,即$f[i][j]=max(f[i][j-1],f[i-1][j],(a[i]==b[j])*(f[i-1][j-1]+1))$,这中间特殊的转移只在a[i]=b[j]时,而在这道 ...