最近用JTable表格做界面,其中需要这么一个功能,可以删除当前选中行,于是就设计成了在单元格中渲染成按钮,点击按钮就可以删除当前所在行的形式,如下图所示:

java word设置表格内文字格式_java word设置表格内文字格式

代码如下:

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);

加入这一段之后,对应的问题就解决了。