二十七、Swing表格组件
- 表格是最常用的数据统计形式之一,在Swing中由JTable类实现表格。
1. 创建、定制以及操纵表格
创建表格
- 在JTable类中除了默认的构造方法外,还提供了如下利用指定表格列名数组和表格数据数组创建表格的构造方法。
JTable(Object[][] rowData, Object[] columnNames)
rowData:封装表格数据的数组
columnNames:封装表格列名的数组
- 在使用表格时,通常将其添加到滚动面板中,然后将滚动面板添加到相应的位置。
定制表格
表格创建完成后,还需要对其进行一系列的定义,以便适合于具体的使用情况。默认情况下通过双击表格中的单元格就可以对其进行编辑。
如果不需要提供该功能,可以通过重构JTable类的isCellEditable(int row, int column)方法实现。默认情况下该方法返回boolean型值true,表示指定单元格可编辑,如果返回false则表示不可编辑。
如果表格只有几列,通常不需要表格列的可重新排列功能。在创建不支持滚动条的表格时已经使用了JTableHeader 类的对象,通过该类的setReorderingAllowed(boolean reorderingAllowed)方法即可设置表格是否支持重新排列功能,设为false表示不支持重新排列功能。
操纵表格
- 在编写应用表格的程序时,经常需要获得表格的一些信息,如表格拥有的行数和列数。下面是JTable类中三个经常用来获得表格信息的方法。
getRowCount():获得表格拥有的行数,返回值为int型。
getColumnCount ():获得表格拥有的列数,返回值为int型。
getColumnName(int column):获得位于指定索引位置的列的名称,返回值为String型。
getValueAt(row,column):获得指定位置的数据。
下面看一个实例,该实例涉及到创建表格、定制表格以及操纵表格:
import javax.swing.*;
import java.awt.*;
public class biaoge1 extends JFrame
{
private JTable table;
public static void main(String[] args)
{
biaoge1 frame=new biaoge1();
frame.setVisible(true);
}
public biaoge1()
{
super();
setTitle("操作表格");
setBounds(100,100,500,375);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c=getContentPane();
String[] columnNames={"A","B","C","D","E","F","G"};
String[][] tableValus=new String[20][columnNames.length];
for (int row=0;row<tableValus.length;row++)
{
for (int column=0;column<columnNames.length;column++)
{
tableValus[row][column]=columnNames[column]+row;
}
}
JTable table=new JTable(tableValus,columnNames);
JScrollPane sc=new JScrollPane(table);
c.add(sc,BorderLayout.CENTER);
table.setSelectionBackground(Color.red);
table.setSelectionForeground(Color.yellow);
table.setRowHeight(20);
table.setSelectionMode(2);
table.setAutoResizeMode(4);
System.out.println("表格公有"+table.getRowCount()+"行"+table.getColumnCount()+"列");
System.out.println("第二列的名称是:"+table.getColumnName(1));
System.out.println("第二行第二列的值为:"+table.getValueAt(1,1));
}
}
运行结果:
- 由运行结果可以看到:首先创建了一个表格,实现了表格的创建,该表格具有滚动条;其次该表格可以随意选中任意行,也可以调整任意列的列宽,表格高度也可以根据个人需要设置;最后能操纵表格,在控制台处可以得到表格的一些信息。
2. 表格模型与表格
利用表格模型创建表格
- 接口TableModel定义了一个表格模型,抽象类AbstractTableModel实现了TableModel接口的大部分方法,只有如下三个抽象方法没有实现。
public int getRowCount();
public int getColumnCount();
public Object getValueAt(int rowIndex, int columnIndex);
- 通过继承AbstractTableModel类实现上面三个抽象方法可以创建自己的表格模型类。DefaultTableModel类是由Swing提供的继承了AbstractTableModel类并实现了上面三个抽象方法的表格模型类。
维护表格模型
在使用表格时,经常需要对表格中的内容进行维护,例如向表格中添加新的数据行、修改表格中某一单元格的值、从表格中删除指定的数据行……这些操作均可以通过维护表格模型来完成。
在向表格模型中添加新的数据行时有两种情况:一种情况是添加到表格模型的尾部,另一种情况是插入到表格模型的指定索引位置。
(1)添加到表格模型的尾部,可以通过addRow()方法完成。
(2)添加到表格模型的指定位置,可以通过insertRow ()方法完成。
下面看一个实例:
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TableModelTest extends JFrame
{
private JTable table;
private JTextField aTextField,bTextField;
private JButton addButton,delButton,updButton;
private DefaultTableModel model;
public static void main(String args[])
{
TableModelTest frame1=new TableModelTest();
frame1.setVisible(true);
}
public TableModelTest()
{
super();
setTitle("维护表格");
setBounds(100,100,510,300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] columnNames={"A","B"};
String[][] tableValues={{"A1","B1"},{"A2","B2"},{"A3","B3"}};
model=new DefaultTableModel(tableValues,columnNames);
table =new JTable(model);
JScrollPane sc=new JScrollPane(table);
getContentPane().add(sc, BorderLayout.CENTER);
buttonInit();
addListener();
}
private void buttonInit()
{
final JPanel panel=new JPanel();
getContentPane().add(panel,BorderLayout.SOUTH);
panel.add(new JLabel("A:"));
aTextField=new JTextField("A4",10);
panel.add(aTextField);
panel.add(new JLabel("B:"));
bTextField=new JTextField("B4",10);
panel.add(bTextField);
addButton=new JButton("添加");
updButton=new JButton("修改");
delButton=new JButton("删除");
panel.add(addButton);
panel.add(updButton);
panel.add(delButton);
}
private void addListener()
{
//添加按钮事件
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String rowData[] ={aTextField.getText(),bTextField.getText()};
model.addRow(rowData);
int rowCount=table.getRowCount()+1;
aTextField.setText("A"+rowCount);
bTextField.setText("B"+rowCount);
}
});
//修改按钮事件
updButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow=table.getSelectedRow();
if (selectedRow!=-1)
{
model.setValueAt(aTextField.getText(),selectedRow,0);
model.setValueAt(bTextField.getText(),selectedRow,1);
}
}
});
//删除按钮事件
delButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow=table.getSelectedRow();
if (selectedRow!=-1)
{
model.removeRow(selectedRow);
}
}
});
model.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
int type=e.getType();
int row=e.getFirstRow();
int column=e.getColumn();
row=row+1;
column=column+1;
if (type==TableModelEvent.INSERT)
{
System.out.println("此事件由\"插入\"行触发");
System.out.println("此次插入的是"+row+"行");
}else if (type==TableModelEvent.UPDATE)
{
System.out.println("此事件由\"修改\"行触发");
System.out.println("此次修改的是"+row+"行"+column+"列");
}
else if (type==TableModelEvent.DELETE)
{
System.out.println("此事件由\"删除\"行触发");
System.out.println("此次删除的是"+row+"行");
}else
{
System.out.println("此事件是由其他原因触发的");
}
}
});
}
}
运行结果:
- 由运行结果可以看到,可以实现对表格的维护(即添加、修改、删除),并且做了事件监听,在控制台处可以看到对表格实行了哪些操作。
3. 提供行标题栏的表格
通过JTable类创建的表格的列标题栏是永远可见的,即使是向下滚动了垂直滚动条,这就大大增强了表格的可读性。但是当不能显示出表格的所有列时,如果向右滚动水平滚动条则会导致表格左侧的部分列不可见,如果能够使表格左侧的一列或几列不随着水平滚动条滚动,也能够永远可见,就解决了上面的问题。
可以通过两个并列显示的表格实现这样的效果,其中左侧的表格用来显示永远可见的一列或几列,右侧的表格则用来显示其他的表格列。
Example:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class ExampleFrame_07 extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public static void main(String args[]) {
try {
ExampleFrame_07 frame = new ExampleFrame_07();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public ExampleFrame_07() {
super();
setTitle("提供行标题栏的表格");
setBounds(100, 100, 500, 375);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Vector<String> columnNameV = new Vector<>();
columnNameV.add("日期");
for (int i = 1; i < 21; i++) {
columnNameV.add("商品" + i);
}
Vector<Vector<Object>> tableValueV = new Vector<>();
for (int row = 1; row < 31; row++) {
Vector<Object> rowV = new Vector<>();
rowV.add(row);
for (int col = 0; col < 20; col++) {
rowV.add((int) (Math.random() * 1000));
}
tableValueV.add(rowV);
}
final MFixedColumnTable panel = new MFixedColumnTable(columnNameV,
tableValueV, 1);
getContentPane().add(panel, BorderLayout.CENTER);
//
}
}
FixedTable:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class MFixedColumnTable extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private JTable fixedColumnTable;// 固定列表格对象
private FixedColumnTableModel fixedColumnTableModel;// 固定列表格模型对象
private JTable floatingColumnTable;// 移动列表格对象
// 移动列表格模型对象
private FloatingColumnTableModel floatingColumnTableModel;
private Vector<String> columnNameV;// 表格列名数组
private Vector<Vector<Object>> tableValueV;// 表格数据数组
private int fixedColumn = 1;// 固定列数量
public MFixedColumnTable(Vector<String> columnNameV,
Vector<Vector<Object>> tableValueV, int fixedColumn) {
super();
setLayout(new BorderLayout());
this.columnNameV = columnNameV;
this.tableValueV = tableValueV;
this.fixedColumn = fixedColumn;
// 创建固定列表格模型对象
fixedColumnTableModel = new FixedColumnTableModel();
// 创建固定列表格对象
fixedColumnTable = new JTable(fixedColumnTableModel);
// 获得选择模型对象
ListSelectionModel fixed = fixedColumnTable.getSelectionModel();
// 选择模式为单选
fixed.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 添加行被选中的事件监听器
fixed.addListSelectionListener(new MListSelectionListener(true));
// 创建可移动列表格模型对象
floatingColumnTableModel = new FloatingColumnTableModel();
// 创建可移动列表格对象
floatingColumnTable = new JTable(floatingColumnTableModel);
// 关闭表格的自动调整功能
floatingColumnTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
ListSelectionModel floating = floatingColumnTable
.getSelectionModel();// 获得选择模型对象
// 选择模式为单选
floating.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 添加行被选中的事件监听器
MListSelectionListener listener = new MListSelectionListener(false);
floating.addListSelectionListener(listener);
JScrollPane scrollPane = new JScrollPane();// 创建一个滚动面版对象
// 将固定列表格头放到滚动面版的左上方
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
fixedColumnTable.getTableHeader());
// 创建一个用来显示基础信息的视口对象
JViewport viewport = new JViewport();
viewport.setView(fixedColumnTable);// 将固定列表格添加到视口中
// 设置视口的首选大小为固定列表格的首选大小
viewport.setPreferredSize(fixedColumnTable.getPreferredSize());
// 将视口添加到滚动面版的标题视口中
scrollPane.setRowHeaderView(viewport);
// 将可移动表格添加到默认视口
scrollPane.setViewportView(floatingColumnTable);
add(scrollPane, BorderLayout.CENTER);
}
private class FixedColumnTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
public int getColumnCount() {// 返回固定列的数量
return fixedColumn;
}
public int getRowCount() {// 返回行数
return tableValueV.size();
}
// 返回指定单元格的值
public Object getValueAt(int rowIndex, int columnIndex) {
return tableValueV.get(rowIndex).get(columnIndex);
}
@Override
public String getColumnName(int columnIndex) {// 返回指定列的名称
return columnNameV.get(columnIndex);
}
}
private class FloatingColumnTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
public int getColumnCount() {// 返回可移动列的数量
return columnNameV.size() - fixedColumn;// 需要扣除固定列的数量
}
public int getRowCount() {// 返回行数
return tableValueV.size();
}
// 返回指定单元格的值
public Object getValueAt(int rowIndex, int columnIndex) {
// 需要为列索引加上固定列的数量
return tableValueV.get(rowIndex)
.get(columnIndex + fixedColumn);
}
@Override
public String getColumnName(int columnIndex) {// 返回指定列的名称
// 需要为列索引加上固定列的数量
return columnNameV.get(columnIndex + fixedColumn);
}
}
private class MListSelectionListener implements ListSelectionListener {
boolean isFixedColumnTable = true; // 默认由选中固定列表格中的行触发
public MListSelectionListener(boolean isFixedColumnTable) {
this.isFixedColumnTable = isFixedColumnTable;
}
public void valueChanged(ListSelectionEvent e) {
if (isFixedColumnTable) { // 由选中固定列表格中的行触发
// 获得固定列表格中的选中行
int row = fixedColumnTable.getSelectedRow();
// 同时选中右侧可移动列表格中的相应行
floatingColumnTable.setRowSelectionInterval(row, row);
} else { // 由选中可移动列表格中的行触发
// 获得可移动列表格中的选中行
int row = floatingColumnTable.getSelectedRow();
// 同时选中左侧固定列表格中的相应行
fixedColumnTable.setRowSelectionInterval(row, row);
}
}
}
}
运行结果:
- 由结果可看到,由两个表格拼成了一个带有行标题的表格。