《JAVA:从入门到精通》part 24

二十七、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);
            }
        }
    }
}

运行结果:
带行标题的表格

  • 由结果可看到,由两个表格拼成了一个带有行标题的表格。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,093评论 1 32
  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,046评论 0 4
  • 1.import static是Java 5增加的功能,就是将Import类中的静态方法,可以作为本类的静态方法来...
    XLsn0w阅读 1,217评论 0 2
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,737评论 1 92
  • 利用JTable类直接创建表格 创建表格: 在JTable类中除了默认的构造方法之外,还提供了利用指定表格列名数组...
    我的袜子都是洞阅读 6,890评论 0 9