Swing 组件 JTable 详解

目录[-]

  • 创建壹個简单的表格
  • 增加壹個表格到容器中
  • 设置和更改列宽度
  • 用户的选择
  • 创建壹個 Table Model
  • 监控数据的更新
  • 发起数据更新事件
  • 相关内容:编辑器和渲染器
  • 使用自定义的渲染器
  • 为单元格指定工具提示信息
  • 为表头指定工具提示信息
  • 排序与过滤数据
  • 使用 ComboBox 下拉列表作为编辑器
  • 使用其它编辑器
  • 使用编辑器验证用户输入的文本信息的合法性
  • 打印表格内容
  • 使用 JTable 类你可以显示表中的数据,也可以允许用户编辑表中的数据,JTable 不包含数据,也不缓存数据,它只是你的数据的壹個视图,下图是一个在滚动窗格显示的一个典型的表格

    Swing 组件 JTable 详解_第1张图片

    本节其余部分告诉如何完成一些表格相关的常用任务

    创建壹個简单的表格

    1、点击  Launch 可以使用  Java Web Start 运行  SimpleTableDemo ,或者由你自己来编译运行  SimpleTableDemo.java ; 
    2、点击包含 "Snowboarding" 的单元格,这壹行都会被选中,说明你已经选择了 Kathy Smith 的数据。壹個特殊的高亮显示说明 "Snowboarding" 单元格是可以编辑的。通常来讲,你可以通过双击单元格来编辑该单元的文本信息。 
    3、将鼠标指向 "First Name",现在点击鼠标并且拖到右边,如同你所看到的壹样,用户可以重新排列表格中的列名称。 
    4、将鼠标指向列头的右边缘,现在点击鼠标并且左右拖动,该列宽度发生了改变,并且其它列填充了多出来的空间。 
    5、调整包含表格的窗口大小,以便它比实际展示需要的空间更大壹些。所有的表格元素变宽了,水平的填充了多出来的额外空间。 

    SimpleTableDemo.java 中的表格在 String 数组中声明列名称: 
    ?
    1
    String[] columnNames = { "First Name" , "Last Name" , "Sport" , "# of Years" , "Vegetarian" };

    它的数据被初始化并且存储在壹個二维的对象数组中:

    ?
    1
    2
    3
    4
    5
    6
    7
    Object[][] data = {
                 { "Kathy" , "Smith" , "Snowboarding" , new Integer( 5 ), new Boolean( false )},
                 { "John" , "Doe" , "Rowing" , new Integer( 3 ), new Boolean( true )},
                 { "Sue" , "Black" , "Knitting" , new Integer( 2 ), new Boolean( false )},
                 { "Jane" , "White" , "Speed reading" , new Integer( 20 ), new Boolean( true )},
                 { "Joe" , "Brown" , "Pool" , new Integer( 10 ), new Boolean( false )}
             };
    然后表格对象使用 data 和 columnNames 完成构建。
    ?
    1
    JTable table = new JTable(data, columnNames);

    壹共有两個 JTable 构造方法直接接受数据,SimpleTableDemo 使用第壹种:

    ?
    1
    2
    JTable(Object[][] rowData, Object[] columnNames)
    JTable(Vector rowData, Vector columnNames)
    这些构造方法的优点就是它们非常易用,然而,这些构造方法也有它的缺点: 
    他们设置每個单元格可编辑; 
    他们以相同的方式对待所有的数据类型(全部识别为 String)。比如说,如果表中的某壹列包含 Boolean 数据,表格可以将该数据显示为壹個复选框。因此,如果你使用上述两個构造方法中任意壹個,你的 Boolean 数据都会被显示为字符串。你可以在上图中的 Vegetarian 列中查看这种差异。 
    他们要求你把所有的数据都存储在壹個二维数组或者 Vector 对象中,但是这样做对于某些情况下的数据是不合适的。比如说,如果你从数据库中初始化壹系列数据,你可能希望直接查询对象以获取结果,而不是把所有的结果都复制到数组或者 Vector 中。 
    如果你希望绕过这些限制,你需要实现自己的 Table Model,可以参考  创建壹個 Table Model。 

    增加壹個表格到容器中

    如下是壹段典型的创建壹個卷动窗格作为表格的容器的代码:

    ?
    1
    2
    JScrollPane scrollPane = new JScrollPane(table);
    table.setFillsViewportHeight( true );
    上面两行代码做了如下事情:调用 JScrollPane 构造方法并传入 table 对象作为其参数,这句话为 table 对象创建了壹個滚动窗格,table 被自动添加到该容器中; 
    调用  JTable.setFillsViewportHeight() 设置了fillsViewportHeight 属性,设置表格是否需要大到足以填充封闭视图的全部高度。当这個属性为 true 时,表格对象使用容器的所有高度,哪怕没有足够多的行来使用垂直的空间,这样做可以更加容易的使用表格作为拖拽的目标。 
    滚动空格自动的将表头放在了顶端,当表格中的数据滚动时仍然可以看到列名显示在顶上。 
    如果你使用表格却不包含滚动空格,那么你需要获取表头组件并且自己来放置它,比如说像下面这样:
    ?
    1
    2
    3
    container.setLayout( new BorderLayout());
    container.add(table.getTableHeader(), BorderLayout.PAGE_START);
    container.add(table, BorderLayout.CENTER);

    设置和更改列宽度

    默认情况下,表中所有的列开始时都是等宽的,并且所有的列会自动填充表格的整個宽度。当表格变宽或者变窄(当用户调整包含表格的窗口时会发生),所有的列宽会自动适应变化。
    当用户通过拖动列的右边缘来调整列宽度时,其它列必须改变宽度,或者表格的宽度也必须改变。默认情况下,表格的宽度会保持原样,所有拖拽点右左边的列会调整以适应右边的空间增大,所有拖拽点左边的列会调整以适应左边的空间减少。
    如果希望自定义初始的列宽,对于你的表格中的每壹列,你可以调用 setPreferredWidth 方法来进行设置。这会设置推荐宽度和他们的近似相对宽度。比如说:增加如下代码到 SimpleTableDemo 会让第三列比其它列要宽壹些。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    TableColumn column = null ;
    for ( int i = 0 ; i < 5 ; i++) {
         column = table.getColumnModel().getColumn(i);
         if (i == 2 ) {
             column.setPreferredWidth( 100 ); //第三列宽壹些
         } else {
             column.setPreferredWidth( 50 );
         }
    }
    如同上述代码显示的壹样,表格的每壹列都由壹個  TableColumn 对象表示。TableColumn 为列宽度的最小值,推荐值,最大值提供了 getter 和 setter 方法,同样也提供了获取当前列宽度的方法。比如说,基于壹個近似的空间设置需要绘制的单元格元素的宽度,可以参考  TableRenderDemo.java 的 initColumnSizes() 方法。 
    当用户明确的调整宽度时,  列的首选宽度设置用户指定的尺寸,同时变成列的当前宽度然而,当表格自己调整时(通常因为窗口调整了)列的推荐宽度则不会改变,存在的首选宽度用于计算新的列宽以填充可用的空间。 
    你可以通过调用  setAutoResizeMode(int) 方法改变表格的宽度调整行为。

    用户的选择

    在这個默认配置下,表格支持选择壹行或者多行,用户可以选择连续的几行。最后一个单元格指示的用户得到一个特殊的迹象,在金属的视觉上看,这個元素超出了轮廓,它有时被称作焦点元素或者当前元素,用户使用鼠标或者键盘来做出选择,具体描述见下表:

    操作    
    鼠标动作
    键盘动作
    选择单行
    单击    
    上下光标键
    延伸的连续选择
    Shift + 单击 或者在表格行上拖动
    Shift + 上下光标键
    添加行选择/切换行选择
    Ctrl + 单击
    移动控制向上箭头或下箭头选择控制铅,然后使用空格键添加到选择或控制,空格键切换行选择。

    要查看选择行是如何执行的,请点击  Launch 以使用  Java™ Web Start 来运行  TableSelectionDemo.java,或者由你自己来编译运行这個  例子。

    这個样例程序展示了相似的表格,允许用户操纵特定的 JTable 选项。它还有壹個文本面板记录选择事件。在下方的截图中,壹個用户运行程序,单击第壹行,然后 Ctrl + 单击第三行,注意点击最后壹個单元格周围的轮廓,这就是突出率先选择的金属外观和感觉

    Swing 组件 JTable 详解_第2张图片

    在"Selection Mode"下方有几個单选按钮,点击标签是"Single Selection"的按钮。现在你可以每次只选择壹行了。如果你点击标签是"Single Interval Selection"的单选按钮,你可以选择连续的壹组行记录。所有的"Selection Mode"下方的单选按钮都调用了方法 JTable.setSelectionMode 这個方法只接受壹個参数,且必须是类 javax.swing.ListSelectionModel 中的常量值之壹,如下所示:MULTIPLE_INTERVAL_SELECTION,SINGLE_INTERVAL_SELECTION 还有 SINGLE_SELECTION。

    回到 TableSelectionDemo,注意在"Selection Options"下方的三個复选框,每個复选框控制壹個 boolean 型的由 JTable 定义的绑定变量:

    "行选择" 控制行选择,对应的有两個方法 setRowSelectionAllowed() 和 getRowSelectionAllowed()。当这個绑定属性为 true,并且 columnSelectionAllowed 的属性值为 false 时,用户可以选择行记录;
    "列选择" 控制列选择,对应的有两個方法 setColumnSelectionAllowed() 和 getColumnSelectionAllowed()。当这個绑定属性为 true,并且 rowSelectionAllowed 绑定属性为 false 时,用户可以选择列记录;
    "单元格选择" 控制单元格选择,对应的有两個方法 setCellSelectionEnabled() 和 getCellSelectionEnabled()。当这個绑定属性为 true 时,用户可以选择单個单元格或者矩形块状的单元格;

    注意:JTable 使用非常简单的选择概念管理行和列的交叉点,  不是设计来处理完全独立单元格选择的 
    如果你清除所有的三個复选框,将三個绑定属性设置为 false,那麽就不会有选择。只有高亮元素显示。 
    你可能会注意到在间隔选择模式下 "Cell Selection" 复选框被禁用。这是因为在演示中,不支持这种模式下的单元格选择。你可以在间隔选择模式下指定单元格的选择,但是结果集是壹個不会产生任何有效选择的表格。 

    你同样可能注意到更改任意三個选项都会影响到其它选项,这是因为同时允许行选择和列选择和允许单元格选择完全壹样,JTable 自动更新了三個绑定变量以保持它们的壹致性。

    注意:设置 cellSelectionEnabled 到某個值会对 rowSelectionEnabled 和 columnSelectionEnabled 有边缘影响,它们也会被设置为这個值。同时设置 rowSelectionEnabled 和 columnSelectionEnabled 到某個值同样对 cellSelectionEnabled 有边缘影响,它也会被设置为那個值。设置 rowSelectionEnabled 和 columnSelectionEnabled 为不同的值会对 cellSelectionEnabled 有边缘影响,它会被设置为 false。

    为了获取当前选择,使用 JTable.getSelectedRows 会返回选择的所有行下标,使用 JTable.getSelectedColumns 会返回所有的列下标。 要检索率先选择的坐标,参照表和表的列模型的选择模型。下面的代码格式化包含率先选择的行和列的字符串:

    ?
    1
    String.format( "Lead Selection: %d, %d. " , table.getSelectionModel().getLeadSelectionIndex(), table.getColumnModel().getSelectionModel().getLeadSelectionIndex());

    使用选择功能生成壹系列的事件。请参考 编写事件监听器 课程的 如何编写壹系列选择事件监听器 这壹节。

    注意:选择数据实际描述为在视图中选择单元格 (表格数据就像它排序或者过滤之后显示出来的壹样) 而不是选择表格的 Model。这個区别不会影响你,除非你查看重新排列的数据,包括排序,过滤或者用户操作过的行。在这种情况下,你必须使用在排序和过滤中提到的转换方法转换选择坐标。

    创建壹個 Table Model

    每個表对象使用壹個 table model 对象来管理实际的表数据。table model 对象必须实现 TableModel 接口。如果开发人员不提供 table model 对象的定义,那么 JTable 会自动创建壹個 DefaultTableModel 类的实例,这种关系如下图所示。

    Swing 组件 JTable 详解_第3张图片

    用 SimpleTableDemo 作为 table model 构造 JTable 表格的示例代码如下:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    new AbstractTableModel() {
         public String getColumnName( int col) {
             return columnNames[col].toString();
         }
         public int getRowCount() { return rowData.length; }
         public int getColumnCount() { return columnNames.length; }
         public Object getValueAt( int row, int col) {
             return rowData[row][col];
         }
         public boolean isCellEditable( int row, int col)
             { return true ; }
         public void setValueAt(Object value, int row, int col) {
             rowData[row][col] = value;
             fireTableCellUpdated(row, col);
         }
    }
    如同上述代码所示,实现壹個 table model 可以很简单。通常来讲,你可以在  AbstractTableModel 的子类里来实现你自己的 Table Model。你的 model 可能把数据保存在 Array,Vector 或者哈希表中,或者它也可以从外部源获取数据,比如说从数据库获取数据。它甚至可以在运行期间生成表数据。

    这個表格与 SimpleTableDemo 在如下几個地方存在不同点:
    1、TableDemo 的自定义 table model 哪怕很简单,它也可以决定数据的类型,帮助 JTable 以更好的格式展示数据。从另壹方面讲,SimpleTableDemo 自动创建的 table model 并不知道列 # of Years 包含靠右对齐的数字,并且具有特殊的格式。它不知道列 Vegerarian 包含布尔型值,可以将其展示成复选框。
    2、在 TableDemo 中实现的自定义 table model 不允许你编辑姓名列,然而它允许你编辑其它列。在 SimpleTableDemo 中,所有的元素都是可以编辑的。

    可以看到来自 TableDemo.java 的如下代码和来自 SimpleTableDemo.java 的不壹样。黑体字显示创建表格的 model 与为创建 SimpleTableDemo 而自动生成的 table model 不壹样。 

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public TableDemo() {
         ...
         JTable table = new JTable( new MyTableModel());
         ...
    }
     
    class MyTableModel extends AbstractTableModel {
         private String[] columnNames = ... //和以前壹样
         private Object[][] data = ... //和以前壹样
     
         public int getColumnCount() {
             return columnNames.length;
         }
     
         public int getRowCount() {
             return data.length;
         }
     
         public String getColumnName( int col) {
             return columnNames[col];
         }
     
         public Object getValueAt( int row, int col) {
             return data[row][col];
         }
     
         public Class getColumnClass( int c) {
             return getValueAt( 0 , c).getClass();
         }
     
         //如果你的表格不可编辑就不要实现这個方法
         public boolean isCellEditable( int row, int col) {
             return col >= 2 ;
         }
     
         //如果你的表格中的数据不改变,则不需要实现这個方法
         public void setValueAt(Object value, int row, int col) {
             data[row][col] = value;
             fireTableCellUpdated(row, col);
         }
         ...
    }

    监控数据的更新

    当表格中的数据发生改变时,每個 table model 都可以有壹系列的监听器,监听器都是 TableModelListener 的实例。在如下代码中,SimpleTableDemo 继承了这样壹個监听器。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import javax.swing.event.*;
    import javax.swing.table.TableModel;
     
    public class SimpleTableDemo ... implements TableModelListener {
         ...
         public SimpleTableDemo() {
             ...
             table.getModel().addTableModelListener( this );
             ...
         }
     
         public void tableChanged(TableModelEvent e) {
             int row = e.getFirstRow();
             int column = e.getColumn();
             TableModel model = (TableModel)e.getSource();
             String columnName = model.getColumnName(column);
             Object data = model.getValueAt(row, column);
     
             ... //对数据做壹些事情...
         }
         ...
    }

    发起数据更新事件

    为了发起数据更新的事件,table model 必须知道如何创建壹個 TableModelEvent 对象。这可以是壹個复杂的过程,但是它现在已经在 DefaultTableModel 中实现了。你既可以允许 JTable 使用默认的DefaultTableModel 实例,也可以创建你自己的 DefaultTableModel 的自定义的子类。我的理解,发起数据更新事件的含义就是,当数据改变时,通知所有相关的监听器,让它们知道某行某列的数据发生了改变,就是这样。如果 DefaultTableModel 对于你自定义的 table model 类而言不适用,你就可以考虑继承 AbstractTableModel 类实现壹個子类。这個类为创建 TableModelEvent 对象实现了壹個简单的框架。你的自定义类只需要在每次表数据被外部源改变时,简单的调用如下的 AbstractTableModel 方法。

    方法名
    发生的变化
    fireTableCellUpdated(int row, int column)
    更新了某個单元格,其坐标为(row, column)
    fireTableRowsUpdated(int firstRow, int lastRow)
    更新了从 firstRow 到 lastRow 的行记录,包括 firstRow 和 lastRow
    fireTableDataChanged()
    更新了整個表格中的数据
    fireTableRowsInserted(int firstRow, int lastRow)
    在行的范围[firstRow, lastRow]内已经插入新的数据,包含 firstRow 和 lastRow
    fireTableRowsDeleted(int firstRow, int lastRow)
    在行范围[firstRow, lastRow]内存在的行被删除了,包含 firstRow 和 lastRow
    fireTableStructureChanged()
    整個表格失效,包括表中的数据和表的结构

    相关内容:编辑器和渲染器

    在你开始继续接下来几個任务之前,你需要理解表格是如何画出它们的单元格。你可能期望每個表格中的单元格都是壹個组件。然而,出于性能方面考虑,Swing 的表格的实现是不壹样的。
    相反,单独壹個单元格渲染器通常可以用来画出所有的包含相同数据类型的单元格。你可以将渲染器想象成壹個可配置的墨水印章,表格使用它来给适当格式化的数据盖章。当用户开始编辑单元格中的数据时,壹個单元格渲染器接手单元格,控制单元格的编辑行为。
    举例来说,TableDemo 中的每個# of Years 列的单元格包含 Number 型数据,比较特殊,是 Integer 对象。默认情况下,单元格渲染器对于壹個包含数字的列,在每個列中的单元格上都使用壹個 JLable 实例来画出合适的数字,靠右对齐。如果用户开始编辑其中壹個单元格,默认的单元格编辑器使用靠右对齐的 JTextField 来控制单元格的编辑。
    为了选择渲染器来显示列中的单元,你首先需要决定表格是否需要为特定的列使用特定的渲染器。如果不需要,那么表格调用 table model 的 getColumnClass 方法,后者将会获取列中元素的数据类型,下壹步,表格中列的数据类型与壹個数据类型列表比较,数据类型列表中的元素渲染器已经注册过。这個列表由表格来完成初始化,但是你可以增加或者改变它。目前,表格将如下类型的数据放进列表中:

    ?
    1
    2
    3
    4
    5
    6
    Boolean — 被渲染成壹個复选框;
    Number — 被渲染成壹個靠右对齐的标签;
    Double, Float — 和 Number 类型壹样,但是壹個 NumberFormat 实例会完成对象到文本的转换,针对当前语言使用默认的数字格式;
    Date — 被渲染成壹個标签,但是壹個 DateFormat 实例会完成对象到文本的转换,针对时间和日期使用壹种简短的风格;
    ImageIcon, Icon — 被渲染成壹個居中对齐的标签;
    Object — 被渲染成壹個显示对象的字符串值的标签;

    单元格编辑器的选择使用类似的算法。

    记住,如果你允许壹個表格使用它自己的 model ,它就会使用 Object 作为每個列的类型。为了指定更加精确的列类型,table model 必须定义恰当的 getColumnClass() 方法。就像 TableDemo.java 的演示代码那样。牢记尽管渲染器决定每個单元格或者列头部如何展示,以及它们的 tool tip 文本是什么,但是渲染器不处理事件。如果你需要获取发生在表格中的事件,你需要使用技术不同的你感兴趣的事件排序:

    情况

    如何获取事件
    检测单元格被编辑的事件
    使用单元格编辑器,或者在单元格编辑器上注册壹個监听器
    检测行、列或者单元格的选择与反选事件
    使用 Detecting User Selections 中提到的选择监听器
    在列的头部检测鼠标事件
    在表格的 JTableHeader 对象中注册 mouse listener  的合适类型。请查阅 TableSorter.java 中的实例。
    检测其它事件
    为 JTable 对象注册合适的监听器
    下述几章会告诉你如何自定义显示,以及通过特殊的渲染器和编辑器进行编辑。你要么在列上指定单元格渲染器和编辑器,要么在数据类型上指定单元格渲染器和编辑器。

    使用自定义的渲染器

    这壹章节告诉我们如何创建和指定壹個单元格的渲染器。你可以使用 JTable 的方法 setDefaultRenderer() 来设置壹個指定类型的单元格渲染器。为了让某個特殊列里面的单元格使用指定的渲染器,你可以使用 TableColumn 的 setCellRenderer() 方法。你甚至可以通过创建 JTable 的子类来指定壹個特定于单元格的渲染器。
    使用 DefaultTableCellRenderer 可以很容易的自定义文本或者图片的默认渲染器,你只需要创建壹個子类并且实现 setValue() 方法以便它可以使用合适的字符串或者图片来调用 setText() 或者 setIcon() 方法。举個例子,这里是壹個默认的日期类渲染器的实现:

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static class DateRenderer extends DefaultTableCellRenderer {
         DateFormat formatter;
         public DateRenderer() { super (); }
     
         public void setValue(Object value) {
             if (formatter== null ) {
                 formatter = DateFormat.getDateInstance();
             }
             setText((value == null ) ? "" : formatter.format(value));
         }
    }

    如果扩展 DefaultTableCellRenderer 还不够,你可以使用另外壹個子类创建壹個渲染器。最简单的方法就是创建壹個已经存在的组件的子类,实现 TableCellRenderer 接口来创建你自己的子类。TableCellRenderer 只需要实现壹個方法:getTableCellRendererComponent()。你对这個方法的实现应该设置渲染组件,以反映传入的状态,然后返回组件。

    在 TableDialogEditDemo 的截图中,用于 Favorite Color 单元格上的渲染器是壹個 JLabel 的子类,被称作 ColorRenderer。这里有壹個来自于 ColorRenderer.java的片段用于展示它是如何实现的。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class ColorRenderer extends JLabel implements TableCellRenderer {
         ...
         public ColorRenderer( boolean isBordered) {
             this .isBordered = isBordered;
             //将背景设置为不透明,为了能够显示出来必须这样做
             setOpaque( true );
         }
     
         public Component getTableCellRendererComponent(
                                 JTable table, Object color,
                                 boolean isSelected, boolean hasFocus,
                                 int row, int column) {
             Color newColor = (Color)color;
             setBackground(newColor);
             if (isBordered) {
                 if (isSelected) {
                     ...
                     //selectedBorder 是颜色的固定边缘
                     //table.getSelectionBackground().
                     setBorder(selectedBorder);
                 } else {
                     ...
                     //unselectedBorder 是颜色的固定边缘
                     //table.getBackground().
                     setBorder(unselectedBorder);
                 }
             }
             
             setToolTipText(...); //留待后继讨论
             return this ;
         }
    }
    这里是来自  TableDialogEditDemo.java 的壹段代码注册了壹個 ColorRenderer 实例作为所有颜色的数据的默认渲染器:
    ?
    1
    table.setDefaultRenderer(Color. class , new ColorRenderer( true ));
    为了给特定单元格指定渲染器,你需要定义壹個 JTable 子类,重载 getCellRenderer() 方法。举個例子,下述的代码让表格中的第壹列的第壹個单元格使用特定的渲染器:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    TableCellRenderer weirdRenderer = new WeirdRenderer();
    table = new JTable(...) {
         public TableCellRenderer getCellRenderer( int row, int column) {
             if ((row == 0 ) && (column == 0 )) {
                 return weirdRenderer;
             }
             // else...
             return super .getCellRenderer(row, column);
         }
    };

    为单元格指定工具提示信息

    默认情况下,显示在表中单元格上的工具提示文本由单元格的渲染器决定。然而,有时候通过重载 JTable 的实现中的 getToolTipText(MonseEvent e) 方法可以更加简单指定工具提示文本。这壹章节向你展示如何使用这两個技术。
    为了在壹個单元格的渲染器上增加工具提示,你首先需要获得或者创建壹個单元格渲染器。然后,确保渲染器组件是壹個 JComponent,调用它的 setToolTipText() 方法即可。
    TableRenderDemo 中有壹個为单元格设置工具提示的例子。点击这里可以运行该样例代码,当然你也可以自己编译运行它的源代码。它使用如下代码在 Sport 列中为单元格添加了工具提示:

    ?
    1
    2
    3
    DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
    renderer.setToolTipText( "Click for combo box" );
    sportColumn.setCellRenderer(renderer);

    虽然上個例子里的工具提示文本样例是静态的,但是你也可以实现随着单元格状态改变而改变的工具提示文本程序,这里有两种实现的方法:
    1、在渲染器的 getTableCellRendererComponent() 方法实现中增加壹些代码;
    2、重载 JTable 的 getToolTipText(MonseEvent e)方法;

    壹個在单元格渲染器中添加代码的例子是 TableDialogEditDemo。点击启动按钮可以运行这個例子,或者你也可以自行编译运行这個例子。TableDialogEditDemo 使用了壹個用于颜色的渲染器,并且在 ColorRender.java 中实现了它。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class ColorRenderer extends JLabel
                                implements TableCellRenderer {
         ...
         public Component getTableCellRendererComponent(
                                 JTable table, Object color,
                                 boolean isSelected, boolean hasFocus,
                                 int row, int column) {
             Color newColor = (Color)color;
             ...
             setToolTipText( "RGB value: " + newColor.getRed() + ", "
                                          + newColor.getGreen() + ", "
                                          + newColor.getBlue());
             return this ;
         }
    }
    下图是工具栏提示运行时的效果:

    Swing 组件 JTable 详解_第4张图片

    通过重载 JTable 的 getToolTipText(MouseEvent e) 方法你可以指定工具栏提示文本,程序 TableToolTipsDemo 展示了该如何进行。Sport 和 Vegetarian 两個列中的元素具有工具栏提示,它们的演示效果如下:

    Swing 组件 JTable 详解_第5张图片

    这是来自 TableToolTipsDemo 中的壹段代码,它实现了在 Sport 和 Vegetarian 列中的元素上添加工具栏提示的功能。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    JTable table = new JTable( new MyTableModel()) {   
         //Implement table cell tool tips.
         public String getToolTipText(MouseEvent e) {
             String tip = null ;
             java.awt.Point p = e.getPoint();
             int rowIndex = rowAtPoint(p);
             int colIndex = columnAtPoint(p);
             int realColumnIndex = convertColumnIndexToModel(colIndex);
     
             if (realColumnIndex == 2 ) { //Sport column
                 tip = "This person's favorite sport to "
                        + "participate in is: "
                        + getValueAt(rowIndex, colIndex);
     
             } else if (realColumnIndex == 4 ) { //Veggie column
                 TableModel model = getModel();
                 String firstName = (String)model.getValueAt(rowIndex, 0 );
                 String lastName = (String)model.getValueAt(rowIndex, 1 );
                 Boolean veggie = (Boolean)model.getValueAt(rowIndex, 4 );
                 if (Boolean.TRUE.equals(veggie)) {
                     tip = firstName + " " + lastName
                           + " is a vegetarian" ;
                 } else {
                     tip = firstName + " " + lastName
                           + " is not a vegetarian" ;
                 }
     
             } else { //another column
                 //You can omit this part if you know you don't
                 //have any renderers that supply their own tool
                 //tips.
                 tip = super .getToolTipText(e);
             }
             return tip;
         }
         ...
    }
    该代码是相当简单的除非是调用convertColumnIndexToModel()该调用是必要的,因为如果用户移动周围的列,列的视图的索引将不匹配模型的索引例如,用户可以拖动Vegetarian 假设该模型的列索引4使得它可以显示作为第一列 - 此时视图索引为0由于prepareRenderer提供视图索引您需要转换视图索引到模型索引这样你才可以确信已选定

    为表头指定工具提示信息

    通过为你的表格的 JTableHeader设置工具提示文本,你可以增加壹個工具提示到列的头部。通常不同的列头部需要不同的工具提示信息。通过重载表格头的 getToolTipText() 方法你可以改变文本信息。你可以交替的调用 TableColumn.setHeaderRenderer() 方法来为表头提供自定义的渲染器。
    在 TableSorterDemo.java 中有壹個为所有列的头部使用工具提示文本的例子,如下是如何设置工具提示信息的代码:

    ?
    1
    table.getTableHeader().setToolTipText( "Click to sort; Shift-Click to sort in reverse order" );
    TableToolTipsDemo.java 中实现了壹個根据列的头部给出不同的工具提示信息的例子。除了最初的两個以外,当你的鼠标划过任何列的头部时,你会看见工具提示信息。因为他们似乎不言自明,所以没有为这些名称列提供工具提示信息。


    Swing 组件 JTable 详解_第6张图片

    接下来的代码实现了工具提示功能,基本上,它创建了壹個 JTableHeader 的子类并且重载了 getToolTipText(MonseEvent e)方法,以便它可以为当前列返回文本。为了关联修改表的表头,JTable 中的 createDefaultTableHeader() 方法被重载了,以便它返回壹個 JTableHeader 子类的实例。

    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    protected String[] columnToolTips = {
         null , // "First Name" assumed obvious
         null , // "Last Name" assumed obvious
         "The person's favorite sport to participate in" ,
         "The number of years the person has played the sport" ,
         "If checked, the person eats no meat" };
    ...
     
    JTable table = new JTable( new MyTableModel()) {
         ...
     
         //实现表头工具栏提示
         protected JTableHeader createDefaultTableHeader() {
             return new JTableHeader(columnModel) {
                 public String getToolTipText(MouseEvent e) {
                     String tip = null ;
                     java.awt.Point p = e.getPoint();
                     int index = columnModel.getColumnIndexAtX(p.x);
                     int realIndex =
                             columnModel.getColumn(index).getModelIndex();
                     return columnToolTips[realIndex];
                 }
             };
         }
    };

    排序与过滤数据

    表格的排序和过滤由壹個 sorter 对象进行管理,最简单的提供 sorter 对象的方法是设置 autoCreateRowSorter 绑定属性为 true:

    ?
    1
    2
    JTable table = new JTable();
    table.setAutoCreateRowSorter( true );

    这次我们定义壹個行排序 sorter,它是 javax.swing.table.TableRowSorter 的壹個实例。当用户点击列的头部时,排序工具提供给表格壹個简单的基于当地的排序。TableSortDemo.java 就是这样壹個演示的例子,下图是截屏:

    Swing 组件 JTable 详解_第7张图片

    要想获取更多关于排序的控制,你可以创建壹個 TableRowSorter 的实例并且指定它是你的表格中的排序对象。

    ?
    1
    2
    TableRowSorter sorter = new TableRowSorter(table.getModel());
    table.setRowSorter(sorter);
    TableRowSorter 使用 java.util.Comparator 对象来完成行排序。壹個实现了这样接口的类提供壹個 compare() 方法用于定义任意两個对象为了完成排序应该如何进行比较。举例来说,下述的代码创建了壹個比较器,用于对比壹系列的字符串通过对比每個字符串最后壹個字母来进行排序:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    Comparator comparator = new Comparator() {
         public int compare(String s1, String s2) {
             String[] strings1 = s1.split( "\\s" );
             String[] strings2 = s2.split( "\\s" );
             return strings1[strings1.length - 1 ]
                 .compareTo(strings2[strings2.length - 1 ]);
         }
    };
    这個例子是特意简化过的,更有代表性的,壹個比较器的实现是壹個 java.text.Collator 的子类。你可以定义你自己的子类,使用在 Collator 类中的工厂方法来根据指定的语言获得壹個 Comparator 对象,或者使用 java.text.RuleBasedCollator 类。

    这個例子是特地简化过的;更常见情况下,Comparator 的实现是 java.text.Collator 的壹個子类。你可以有你自己的子类,使用在 Collator 类的工厂方法可以获取壹個基于本地环境的 Comparator 实例,或者使用 java.text.RuleBasedCollator 。

    对于每壹列应该使用哪种 Comparator ,TableRowSorter 会尝试应用如下规则。规则会按照如下的顺序被遵循;第壹条规则如果已经使用,则后继的规则会被忽略。

    ?
    1
    2
    3
    4
    5
    1 、如果已经通过 setComparator() 指定了壹個比较器,则使用该比较器。
    2 、如果 table model 已经声明列数据由字符串组成,就是说对于指定列调用 TableModel.getColumnClass() 方法返回 String. class ,使用基于本地环境的字符串顺序进行排序。
    3

    你可能感兴趣的:(java基础)