最近用JTable表格做界面,其中需要这么一个功能,可以删除当前选中行,于是就设计成了在单元格中渲染成按钮,点击按钮就可以删除当前所在行的形式,如下图所示:
代码如下:
TestTable类
package com.ant;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestTable {
private JFrame frame;
private JTable table;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestTable window = new TestTable();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public TestTable() {
this.initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
this.frame = new JFrame();
this.frame.setBounds(100, 100, 450, 300);
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.getContentPane().setLayout(null);
JPanel panel = new JPanel();
panel.setBounds(10, 10, 414, 242);
this.frame.getContentPane().add(panel);
panel.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 10, 394, 222);
panel.add(scrollPane);
this.table = new JTable();
scrollPane.setViewportView(this.table);
DefaultTableModel model = new DefaultTableModel();
String[] a = {"a", "b", "c"};
model.setColumnIdentifiers(a);
for (int i = 0; i < 5; i++) {
String[] b = {"1", "2", "3"};
model.addRow(b);
}
this.table.setModel(model);
this.table.setRowHeight(35);
this.table.getColumnModel().getColumn(2).setCellEditor(new MyButtonEditor(table));
this.table.getColumnModel().getColumn(2).setCellRenderer(new MyButtonRender());
this.table.setRowSelectionAllowed(false);// 禁止表格的选择功能。不然在点击按钮时表格的整行都会被选中。也可以通过其它方式来实现。
}
}
MyButtonRender类(渲染):
package com.ant;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class MyButtonRender implements TableCellRenderer
{
private JPanel panel;
private JButton button;
public MyButtonRender()
{
this.initButton();
this.initPanel();
// 添加按钮。
this.panel.add(this.button);
}
private void initButton()
{
this.button = new JButton();
// 设置按钮的大小及位置。
this.button.setBounds(0, 0, 80, 35);
// 在渲染器里边添加按钮的事件是不会触发的
// this.button.addActionListener(new ActionListener()
// {
//
// public void actionPerformed(ActionEvent e)
// {
// // TODO Auto-generated method stub
// }
// });
}
private void initPanel()
{
this.panel = new JPanel();
// panel使用绝对定位,这样button就不会充满整个单元格。
this.panel.setLayout(null);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column)
{
// 只为按钮赋值即可。也可以作其它操作,如绘背景等。
this.button.setText("delete");
return this.panel;
}
}
MyButtonEditor类(单元格编辑器类,任何渲染的组件,如按钮,下拉框等,需要触发监听事件时,都是在编辑器类中触发的,渲染类中不能触发。):
package com.ant;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableModel;
/**
* 自定义一个往列里边添加按钮的单元格编辑器。最好继承DefaultCellEditor,不然要实现的方法就太多了。
*
*/
public class MyButtonEditor extends DefaultCellEditor {
/**
* serialVersionUID
*/
private static final long serialVersionUID = -6546334664166791132L;
private JPanel panel;
private JButton button;
private JTable table1;
public MyButtonEditor(JTable _table1) {
// DefautlCellEditor有此构造器,需要传入一个,但这个不会使用到,直接new一个即可。
super(new JTextField());
table1 = _table1;
// 设置点击几次激活编辑。
this.setClickCountToStart(1);
this.initButton();
this.initPanel();
// 添加按钮。
this.panel.add(this.button);
}
private void initButton() {
this.button = new JButton();
// 设置按钮的大小及位置。
this.button.setBounds(0, 0, 80, 35);
// 为按钮添加事件。这里只能添加ActionListner事件,Mouse事件无效。
this.button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//教程说明
//
JButton btn = (JButton)(e.getSource());
ChangeEvent ce = new ChangeEvent(btn);
table1.editingStopped(ce);
System.out.println(table1.getSelectedRow());
DefaultTableModel model = (DefaultTableModel)(table1.getModel());
model.removeRow(table1.getSelectedRow());
}
});
}
private void initPanel() {
this.panel = new JPanel();
// panel使用绝对定位,这样button就不会充满整个单元格。
this.panel.setLayout(null);
}
/**
* 这里重写父类的编辑方法,返回一个JPanel对象即可(也可以直接返回一个Button对象,但是那样会填充满整个单元格)
*/
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
// 只为按钮赋值即可。也可以作其它操作。
this.button.setText("delete");
return this.panel;
}
/**
* 重写编辑单元格时获取的值。如果不重写,这里可能会为按钮设置错误的值。
*/
@Override
public Object getCellEditorValue() {
return this.button.getText();
}
}
当点击第一行delete按钮时,会删除当前行数据,但是继续点击第一行delete按钮,就会出现ArrayIndexOutOfBoundsException异常,如下:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -1
at java.util.Vector.removeElementAt(Vector.java:565)
at javax.swing.table.DefaultTableModel.removeRow(DefaultTableModel.java:462)
at com.ant.MyButtonEditor$1.actionPerformed(MyButtonEditor.java:64)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6539)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6304)
at java.awt.Container.processEvent(Container.java:2239)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2297)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4904)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4535)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4476)
at java.awt.Container.dispatchEventImpl(Container.java:2283)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:760)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:84)
at java.awt.EventQueue$4.run(EventQueue.java:733)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:730)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:205)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
原因就是:因为JTable中的editor值变化当时,并不会引发JTable的变化,只有焦点不在该editor时,才会有消息通知JTable,会生成一个javax.swing.AbstractCellEditor.fireEditingStopped消息,继而引发javax.swing.JTable.editingStopped事件,但事件发生时,该行已经被删除了,因而出现上面的错误。
==怎样解决呢?
在editor的相应事件中执行editingStopped或者editingCanceled即可。关键代码就在MyButtonEditor类中,如下:
// 为按钮添加事件。这里只能添加ActionListner事件,Mouse事件无效。
this.button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//教程说明
//
JButton btn = (JButton)(e.getSource());
ChangeEvent ce = new ChangeEvent(btn);
table1.editingStopped(ce);
System.out.println(table1.getSelectedRow());
DefaultTableModel model = (DefaultTableModel)(table1.getModel());
model.removeRow(table1.getSelectedRow());
}
});
其中需要加入(按钮作为示例,如果时其他组件则根据情况变化):
JButton btn = (JButton)(e.getSource());
ChangeEvent ce = new ChangeEvent(btn);
table1.editingStopped(ce);
加入这一段之后,对应的问题就解决了。