再议将Swing组件与JTable的单元格组件

再议将Swing组件与JTable的单元格组件_第1张图片可以说JTable是Swing组件中最重要的组件,只要程序和数据库打交道的程序必然有表格组件,而且多数情况下又不会很直白的把数据填到表格里,一般都会把各种组件放在表格里以便于对特定数据的呈现,也因此表格组件成了Swing里最让人头痛的组件。网上有很多如何把某个组件加载到表格的中的例子,但那些程序都只是用了极少的数据来演示,并不需要对表格进行刷新,因此那些例子里都没有关闭单元格编辑状态的代码,而当数据量大到需要多页显示时就出了问题,表格中的某列单元格使用了自定义的组件(诸如按钮之类的),当这些单元格被点击后再刷新表格的时会抛数组越界异常,像这样:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0 >= 0
at java.util.Vector.elementAt(Unknown Source)
at javax.swing.table.DefaultTableModel.setValueAt(Unknown Source)
at javax.swing.JTable.setValueAt(Unknown Source)
at javax.swing.JTable.editingStopped(Unknown Source)
at javax.swing.AbstractCellEditor.fireEditingStopped(Unknown Source)
at javax.swing.AbstractCellEditor.stopCellEditing(Unknown Source)
at javax.swing.JTable.columnMarginChanged(Unknown Source)
at javax.swing.table.DefaultTableColumnModel.fireColumnMarginChanged(Unknown Source)
at javax.swing.table.DefaultTableColumnModel.propertyChange(Unknown Source)
at java.beans.PropertyChangeSupport.fire(Unknown Source)
像这种问题很多人会以为问题是出在像表格里加载数据时出了错,又或者是某个单元格没有得到值等原因。
但真实的原因是没有关闭单元格的编辑状态,当点击某单元格后程序会使单元格状态变为编辑状态,如果此时不对表格内的数据重新加载的话是不会抛上述异常的,但只要一重新加载数据就立马抛异常,因此需要使用fireEditingStopped方法来关闭编辑状态。

我因为学习SQLite的需要而专门编写了一个带有JTable的程序,更详细的就不多说了,再此直接贴出代码以供参考

private JScrollPane createTable()
	{
		String[] columnHeaders = { "书名", "库存", "单价", "类型", "评分", "入库日期", "出版社", "联系电话" };
		tableModel = new DefaultTableModel();
		// 创建表格头部字段
		tableModel.addColumn("");
		for (String header : columnHeaders)
			tableModel.addColumn(header);
		tableModel.addColumn("操作");

		JTable table = new JTable(tableModel);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // 表格选择为单选
		table.setRowHeight(25); // 设置行高
		table.setShowVerticalLines(false);// 使表格的列线条不显示
		// 使第一列的大小固定不变
		table.getColumn("").setPreferredWidth(25);
		table.getColumn("").setMaxWidth(25);
		table.getColumn("").setMinWidth(25);
		// 设置表格排序,表格默认将所有值都作为字符串进行排序,
		// 所以对于表格中已有字符型列可以不用自定义排序类
		// 但需要对非字符串的列进行自定义排序
		TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
		sorter.setComparator(2, new Comparator<Integer>()
		{
			public int compare(Integer o1, Integer o2)
			{
				return o1.compareTo(o2);
			}
		});
		// 对单价列进行排序,由于单位已经被格式化了货币字符串
		// 所以要对字符串进行转换后再比较
		sorter.setComparator(3, new Comparator<String>()
		{
			public int compare(String o1, String o2)
			{
				BigDecimal b1 = new BigDecimal(o1.substring(1));
				BigDecimal b2 = new BigDecimal(o2.substring(1));
				return b1.compareTo(b2);
			}
		});
		sorter.setComparator(6, new Comparator<java.sql.Date>()
		{
			public int compare(Date o1, Date o2)
			{
				return o1.compareTo(o2);
			}
		});
		table.setRowSorter(sorter);
		// 设置各列的呈现方式
		new CheckBoxColumn(table.getColumn(""));
		new LabelColumn(table.getColumn("书名"));
		new LabelColumn(table.getColumn("单价"), null, Color.BLUE, SwingConstants.RIGHT);
		new ProgressColumn(table.getColumn("库存"), Book.STORAGE_MAXIMUM);
		new LabelColumn(table.getColumn("评分"), SwingConstants.CENTER);
		new LabelColumn(table.getColumn("类型"));
		new LabelColumn(table.getColumn("入库日期"), SwingConstants.LEFT);
		new LabelColumn(table.getColumn("出版社"), SwingConstants.CENTER);
		new ComboBoxColumn(table.getColumn("联系电话"));
		new ButtonColumn(table.getColumn("操作"), "编辑", center);
		return new JScrollPane(table);
	}

public void updateTable()
	{
		// 清空表格模型内的数据
		tableModel.getDataVector().clear();
		tableModel.fireTableDataChanged();
		new SwingWorker<List<BookView>, Vector>()
		{
			protected List<BookView> doInBackground() throws Exception
			{
				// 得到书籍的总数
				bookTotal = center.getDataManager().getBookTotal();
				// 根据当前的页数的返回一个结果集
				List<BookView> books = center.getDataManager().getBookViewByPage(pageSize, pageNo);
				for (BookView book : books)
				{
					Vector rowData = new Vector();
					rowData.add(new Boolean(false));
					rowData.add(book.getBookName());
					rowData.add(book.getStorage());
					rowData.add(NumberFormat.getCurrencyInstance().format(book.getPrice().doubleValue()));
					rowData.add(book.getTypeName());
					rowData.add(book.getGrade());
					rowData.add(book.getStorageDate());
					rowData.add(book.getPublishName());
					rowData.add(book.getPublishPhone());
					rowData.add(book.getId());
					this.publish(rowData);
				}
				return books;
			}

			protected void process(List<Vector> chunks)
			{
				lblStatueBar.setText(StringHelper.LOADING);
				for (Vector rowData : chunks)
				{
					tableModel.addRow(rowData);
				}
			}

			protected void done()
			{
				lblStatueBar.setText("共有" + bookTotal + "条书籍记录!");
				pageTotal = bookTotal / pageSize;
				pageTotal += bookTotal / pageSize > 0 ? 1 : 0;
				cboPage.removeAllItems();
				for (int p = 1; p <= pageTotal; p++)
					cboPage.addItem(p);
				cboPage.setSelectedItem(pageNo);
				chkDeleteAll.setSelected(false);
			};
		}.execute();
	}

package gui.tableColumn;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class CheckBoxColumn extends AbstractCellEditor implements TableCellEditor, TableCellRenderer
{
	private JCheckBox chkRender;
	private JCheckBox chkEditor;
	private Object value;

	public CheckBoxColumn(TableColumn tc)
	{
		chkRender = new JCheckBox();
		chkEditor = new JCheckBox();
		chkEditor.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				fireEditingStopped();
			}
		});
		tc.setCellEditor(this);
		tc.setCellRenderer(this);
	}

	@Override
	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
	{
		chkRender.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
		chkRender.setSelected((Boolean) value);
		return chkRender;
	}

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
	{
		chkEditor.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
		if(value!=null)
		{
			Boolean res = (Boolean)value;
			res = !res;
			this.value = res;
			chkEditor.setSelected(res);
		}
		return chkEditor;
	}

	@Override
	public Object getCellEditorValue()
	{
		return this.value;
	}

}

package gui.tableColumn;

import gui.BookDialog;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import manager.ManagerCenter;

public class ButtonColumn extends AbstractCellEditor implements TableCellEditor, TableCellRenderer, ActionListener
{
	private JButton btnRender;
	private JButton btnEditor;
	private Object value;
	private ManagerCenter center;

	public ButtonColumn(TableColumn tc, String name, ManagerCenter center)
	{
		this.center = center;
		btnRender = new JButton(name);
		btnEditor = new JButton(name);
		btnEditor.addActionListener(this);
		tc.setCellEditor(this);
		tc.setCellRenderer(this);
	}

	@Override
	public Object getCellEditorValue()
	{
		// TODO Auto-generated method stub
		return this.value;
	}

	@Override
	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
	{
		if (hasFocus)
		{
			btnRender.setForeground(table.getForeground());
			btnRender.setBackground(UIManager.getColor("Button.background "));
		} else
			if (isSelected)
			{
				btnRender.setForeground(table.getSelectionForeground());
				btnRender.setBackground(table.getSelectionBackground());
			} else
			{
				btnRender.setForeground(table.getForeground());
				btnRender.setBackground(UIManager.getColor("Button.background "));
			}
		return btnRender;
	}

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
	{
		// TODO Auto-generated method stub
		this.value = value;
		if (isSelected)
		{
			btnEditor.setForeground(table.getSelectionForeground());
			btnEditor.setBackground(table.getSelectionBackground());
		} else
		{
			btnEditor.setForeground(table.getForeground());
			btnEditor.setBackground(UIManager.getColor("Button.background "));
		}
		return btnEditor;
	}

	public void actionPerformed(ActionEvent e)
	{
		// TODO Auto-generated method stub
		// System.out.println(value);
		fireEditingStopped();
		new BookDialog(center, Integer.parseInt(value.toString()));
	}
}

package gui.tableColumn;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class ComboBoxColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor
{
	private JComboBox cboRender;
	private JComboBox cboEditor;
	private Object value;
	private JTable table;

	public ComboBoxColumn(TableColumn tc)
	{
		tc.setCellEditor(this);
		tc.setCellRenderer(this);
	}

	public Object getCellEditorValue()
	{
		return value;
	}

	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
	{
		if (value != null)
		{
			String[] phones = value.toString().split("\\|");
			cboRender = new JComboBox(phones);
		}
		return cboRender;
	}

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
	{
		this.value = value;
		this.table = table;
		if (value != null)
		{
			String[] phones = value.toString().split("\\|");
			cboEditor = new JComboBox(phones);
			cboEditor.addFocusListener(new FocusListener()
			{
				public void focusGained(FocusEvent e)
				{
				}

				public void focusLost(FocusEvent e)
				{
					fireEditingStopped();
				}
			});
		}
		return cboEditor;
	}

}

package gui.tableColumn;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;

import javax.swing.AbstractCellEditor;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class LabelColumn extends AbstractCellEditor implements TableCellEditor, TableCellRenderer
{
	private Font font;
	private Color fontColor;
	private JLabel lblRender;

	public LabelColumn(TableColumn tc, Font font, Color fontColor, int alignment)
	{
		lblRender = new JLabel();
		lblRender.setOpaque(true);
		tc.setCellEditor(this);
		tc.setCellRenderer(this);
		if (font != null)
			this.font = font;
		if (fontColor != null)
			this.fontColor = fontColor;
		if (alignment != -1)
			lblRender.setHorizontalAlignment(alignment);
	}

	public LabelColumn(TableColumn tc, Font font)
	{
		this(tc, font, null, SwingConstants.LEFT);
	}

	public LabelColumn(TableColumn tc, Color fontColor)
	{
		this(tc, null, fontColor, SwingConstants.LEFT);
	}

	public LabelColumn(TableColumn tc, int alignment)
	{
		this(tc, null, null, alignment);
	}

	public LabelColumn(TableColumn tc)
	{
		this(tc, null, null, SwingConstants.LEFT);
	}

	@Override
	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
	{
		lblRender.setText(value != null ? value.toString() : "");
		lblRender.setFont(this.font == null ? table.getFont() : this.font);
		lblRender.setForeground(this.fontColor == null ? table.getForeground() : this.fontColor);
		lblRender.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
		return lblRender;
	}

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
	{
		return null;
	}

	@Override
	public Object getCellEditorValue()
	{
		// TODO Auto-generated method stub
		return null;
	}

}

package gui.tableColumn;

import java.awt.Component;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class ProgressColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor
{
	private JProgressBar pbRender;

	public ProgressColumn(TableColumn tc, int maximum)
	{
		pbRender = new JProgressBar();
		pbRender.setMaximum(maximum);
		pbRender.setBorder(null);
		tc.setCellEditor(this);
		tc.setCellRenderer(this);
	}

	public Object getCellEditorValue()
	{
		return null;
	}

	@Override
	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
	{
		pbRender.setString(value.toString());
		pbRender.setStringPainted(true);
		pbRender.setValue(Integer.parseInt(value.toString()));
		return pbRender;
	}

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
	{
		return null;
	}
}


你可能感兴趣的:(再议将Swing组件与JTable的单元格组件)