Documentation

The Java™ Tutorials
Hide TOC
How to Use Tables如何使用表格
Trail: Creating a GUI With Swing
Lesson: Using Swing Components
Section: How to Use Various Components

How to Use Tables如何使用表格

With the JTable class you can display tables of data, optionally allowing the user to edit the data. 使用JTable类,您可以显示数据表,并允许用户编辑数据。JTable does not contain or cache data; it is simply a view of your data. 不包含或缓存数据;它只是一个数据视图。Here is a picture of a typical table displayed within a scroll pane:以下是滚动窗格中显示的典型表格的图片:

A snapshot of TableDemo, which displays a typical table.

The rest of this section shows you how to accomplish some common table-related tasks. 本节的其余部分将向您展示如何完成一些常见的表相关任务。Here are the topics this section covers:本节涵盖的主题如下:

Creating a Simple Table创建简单表


Try this: 
  1. Click the Launch button to run SimpleTableDemo using Java™ Web Start (download JDK 7 or later). 单击启动按钮,使用Java™Web启动运行SimpleTableDemo(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

    Launches the SimpleTableDemo example
  2. Click the cell that contains "Snowboarding".单击包含“单板滑雪”的单元格。
    The entire first row is selected, indicating that you have selected Kathy Smith's data. 选择整个第一行,表示您已选择Kathy Smith的数据。A special highlight indicates that the "Snowboarding" cell is editable. 一个特别的突出显示表示“单板滑雪”单元格是可编辑的。Generally, you begin editing a text cell by double-clicking it.通常,您可以通过双击文本单元格来开始编辑。

  3. Position the cursor over "First Name". 将光标放在“名字”上。Now press the mouse button and drag to the right.
    As you can see, users can rearrange columns in tables.
    现在按下鼠标按钮并向右拖动,如您所见,用户可以重新排列表中的列。

  4. Position the cursor just to the right of a column header. 将光标放置在列标题的右侧。Now press the mouse button and drag to the right or left.现在按下鼠标按钮并向右或向左拖动。
    The column changes size, and the other columns adjust to fill the remaining space.列将更改大小,其他列将调整以填充剩余空间。

  5. Resize the window containing the table so that it's bigger than necessary to display the whole table.调整包含表格的窗口的大小,使其大于显示整个表格所需的大小。
    All the table cells become wider, expanding to fill the extra horizontal space.所有表格单元格都变宽,扩展以填充额外的水平空间。

The table in SimpleTableDemo.java declares the column names in a String array:SimpleTableDemo.java中的表声明字符串数组中的列名:

String[] columnNames = {"First Name",
                        "Last Name",
                        "Sport",
                        "# of Years",
                        "Vegetarian"};

Its data is initialized and stored in a two-dimensional Object array:其数据被初始化并存储在二维对象数组中:

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

Then the Table is constructed using these data and columnNames:然后使用这些数据和列名构建表:

JTable table = new JTable(data, columnNames);

There are two JTable constructors that directly accept data (SimpleTableDemo uses the first):有两个JTable构造函数直接接受数据(SimpleTableDemo使用第一个):

The advantage of these constructors is that they are easy to use. However, these constructors also have disadvantages:这些构造函数的优点是易于使用。然而,这些构造器也有缺点:

If you want to get around these restrictions, you need to implement your own table model, as described in Creating a Table Model.如果要绕过这些限制,需要实现自己的表模型,如创建表模型中所述。

Adding a Table to a Container向容器添加表

Here is typical code for creating a scroll pane that serves as a container for a table:下面是创建用作表容器的滚动窗格的典型代码:

JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);

The two lines in this snippet do the following:此代码段中的两行执行以下操作:

The scroll pane automatically places the table header at the top of the viewport. 滚动窗格会自动将表格标题放置在视口的顶部。The column names remain visible at the top of the viewing area when the table data is scrolled.滚动表数据时,列名在查看区域的顶部保持可见。

If you are using a table without a scroll pane, then you must get the table header component and place it yourself. 如果使用的表没有滚动窗格,则必须获取表头组件并将其放置在自己的位置。For example:例如:

container.setLayout(new BorderLayout());
container.add(table.getTableHeader(), BorderLayout.PAGE_START);
container.add(table, BorderLayout.CENTER);

Setting and Changing Column Widths设置和更改列宽

By default, all columns in a table start out with equal width, and the columns automatically fill the entire width of the table. 默认情况下,表中的所有列以相等的宽度开始,列自动填充表的整个宽度。When the table becomes wider or narrower (which might happen when the user resizes the window containing the table), all the column widths change appropriately.当表格变得更宽或更窄时(当用户调整包含表格的窗口的大小时可能会发生),所有列宽都会相应改变。

When the user resizes a column by dragging its right border, then either other columns must change size, or the table's size must change. 当用户通过拖动列的右边框来调整列的大小时,其他列必须更改大小,或者表的大小必须更改。By default, the table's size remains the same, and all columns to the right of the drag point resize to accommodate space added to or removed from the column to the left of the drag point.默认情况下,表的大小保持不变,拖动点右侧的所有列都会调整大小,以适应添加到拖动点左侧列或从拖动点左侧的列中删除的空间。

To customize initial column widths, you can invoke setPreferredWidth on each of your table's columns. 要自定义初始列宽,可以对表的每一列调用setPreferredWidthThis sets both the preferred widths of the columns and their approximate relative widths. 这将设置列的首选宽度及其近似相对宽度。For example, adding the following code to SimpleTableDemo makes its third column bigger than the other columns:例如,向SimpleTableDemo添加以下代码会使其第三列大于其他列:

TableColumn column = null;
for (int i = 0; i < 5; i++) {
    column = table.getColumnModel().getColumn(i);
    if (i == 2) {
        column.setPreferredWidth(100); //third column is bigger
    } else {
        column.setPreferredWidth(50);
    }
}

As the preceding code shows, each column in a table is represented by a TableColumn object. 如前面的代码所示,表中的每一列都由TableColumn对象表示。TableColumn supplies getter and setter methods for the minimum, preferred, and maximum widths of a column, as well as a method for getting the current width. 提供列的最小、首选和最大宽度的getter和setter方法,以及获取当前宽度的方法。For an example of setting cell widths based on an approximation of the space needed to draw the cells' contents, see the initColumnSizes method in TableRenderDemo.java.有关根据绘制单元格内容所需空间的近似值设置单元格宽度的示例,请参阅TableRenderDemo.java中的initColumnSizes方法。

When the user explicitly resizes columns, the columns' preferred widths are set such that the user-specified sizes become the columns' new current widths. 当用户显式调整列的大小时,将设置列的首选宽度,以便用户指定的大小成为列的新当前宽度。However, when table itself is resized — typically because the window has resized —; the columns' preferred widths do not change. 然而,当表本身被调整大小时;通常是因为窗口已调整大小;列的首选宽度不变。Instead, the existing preferred widths are used to calculate new column widths to fill the available space.相反,现有的首选宽度用于计算新列宽以填充可用空间。

You can change a table's resize behavior by invoking setAutoResizeMode.您可以通过调用setAutoResizeMode来更改表的调整大小行为。

User Selections用户选择

In its default configuration, a table supports a selection that consists of one or more rows. 在默认配置中,表支持由一行或多行组成的选择。The user can select a contiguous range of rows or an arbitrary set of rows. 用户可以选择连续范围的行或任意行集。The last cell that the user indicated gets a special indication; in the Metal look and feel, the cell is outlined. 用户指示的最后一个小区获得特殊指示;在金属外观和感觉中,细胞被勾勒出来。This cell is known as the lead selection; it is sometimes called "the cell with the focus" or "the current cell".该单元称为引线选择;它有时被称为“具有焦点的单元格”或“当前单元格”。

The user uses the mouse and/or keyboard to make selections, as described in the following table:用户使用鼠标和/或键盘进行选择,如下表所述:

Operation活动 Mouse Action鼠标动作 Keyboard Action键盘动作
Select single row.选择一行。 Click.点击 Up Arrow or Down Arrow.向上箭头或向下箭头。
Extend contiguous selection.扩展连续选择。 Shift-Click or Drag over rows.按住Shift键单击或拖动行。 Shift-Up Arrow or Shift-Down Arrow.上移箭头或下移箭头。
Add row to selection/toggle row selection.将行添加到选择/切换行选择。 Control-Click控制点击 Move lead selection with Control-Up Arrow or Control-Down Arrow, then use Space Bar to add to selection or Control-Space Bar to toggle row selection.使用控制向上箭头或控制向下箭头移动引线选择,然后使用空格键添加到选择,或使用控制空格键切换行选择。

To see how selections work, click the Launch button to run TableSelectionDemo using Java™ Web Start (download JDK 7 or later). 要查看选择的工作方式,请单击启动按钮,使用Java™Web启动运行TableSelectionDemo下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableSelectionDemo example

This example program presents the familiar table, and allows the user to manipulate certain JTable options. 这个示例程序提供了熟悉的表,并允许用户操作某些JTable选项。There is also a text pane that logs selection events.还有一个记录选择事件的文本窗格。

In the screenshot below, a user has run the program, clicked in the first row, then control-clicked in the third row. 在下面的屏幕截图中,用户运行程序,单击第一行,然后单击第三行中的控件。Notice the outline around the last cell clicked; this is how the Metal look and feel highlights the lead selection.注意最后单击的单元格周围的轮廓;这是金属外观和感觉如何突出前导选区。

TableSelectionDemo with a non-contiguous row selection.

Under "Selection Mode" there are a set of radio buttons. 在“选择模式”下,有一组单选按钮。Click the one labelled "Single Selection". Now you can only select one row at a time. 单击标记为“单选”的选项。现在一次只能选择一行。If you click on the "Single Interval Selection" radio button, you can select a set of rows that must be contiguous.如果单击“单间隔选择”单选按钮,则可以选择一组必须连续的行。

All of the radio buttons under "Selection Mode" invoke JTable.setSelectionMode. “选择模式”下的所有单选按钮调用JTable.setSelectionModeThis method takes a single argument, which must be one of the following constants defined in javax.swing.ListSelectionModel: MULTIPLE_INTERVAL_SELECTION, SINGLE_INTERVAL_SELECTION, and SINGLE_SELECTION.该方法采用一个参数,该参数必须是javax.swing.ListSelectionModel中定义的以下常量之一:MULTIPLE_INTERVAL_SELECTIONSINGLE_INTERVAL_SELECTIONSINGLE_SELECTION

Returning to TableSelectionDemo, notice the three option check boxes under "Selection Options." 返回TableSelectionDemo,注意“选择选项”下的三个选项复选框。Each of check box controls the state of a boolean bound variable defined by JTable:每个复选框控制JTable定义的boolean绑定变量的状态:


NOTE: JTable uses a very simple concept of selection, managed as an intersection of rows and columns. 使用非常简单的选择概念,作为行和列的交集进行管理。It was not designed to handle fully independent cell selections. 它不是为处理完全独立的细胞选择而设计的。

If you clear all three check boxes (setting all three bound properties to false), there is no selection; only the lead selection is shown.如果清除所有三个复选框(将所有三个绑定属性设置为false),则没有选择;仅显示导线选择。

You may notice that the "Cell Selection" check box is disabled in multiple interval selection mode. 您可能会注意到,“单元格选择”复选框在多间隔选择模式下被禁用。This is because cell selection is not supported in this mode in the demo. 这是因为演示中此模式不支持单元格选择。You can specify selection by cell in multiple interval selection mode, but the result is a table that does not produce useful selections.可以在多间隔选择模式下指定“按单元格选择”,但结果是表格无法生成有用的选择。

You may also notice that changing any of the three selection options can affect the others. 您可能还注意到,更改三个选择选项中的任何一个都会影响其他选项。This is because allowing both row selection and column selection is exactly the same as enabling cell selection. 这是因为允许行选择和列选择与启用单元格选择完全相同。JTable automatically updates the three bound variables as necessary to keep them consistent.JTable根据需要自动更新三个绑定变量,以保持它们的一致性。


NOTE: Setting cellSelectionEnabled to a value has the side effect of also setting both rowSelectionEnabled and columnSelectionEnabled to that value. cellSelectionEnabled设置为值的副作用是同时将rowSelectionEnabledcolumnSelectionEnabled设置为该值。Setting both rowSelectionEnabled and columnSelectionEnabled to a value has the side effect of also setting cellSelectionEnabled to that value. rowSelectionEnabledcolumnSelectionEnabled设置为一个值会产生将cellSelectionEnabled设置为该值的副作用。Setting rowSelectionEnabled and columnSelectionEnabled to different values has the side effect of also setting cellSelectionEnabled to false. rowSelectionEnabledcolumnSelectionEnabled设置为不同的值也会产生将cellSelectionEnabled设置为false的副作用。

To retrieve the current selection, use JTable.getSelectedRows which returns an array of row indexes, and JTable.getSelectedColumns which returns an array of column indexes. 要检索当前选择,请使用JTable.getSelectedRows返回行索引数组,使用JTable.getSelectedColumns返回列索引数组。To retrieve the coordinates of the lead selection, refer to the selection models for the table itself and for the table's column model. 要检索潜在客户选择的坐标,请参考表本身和表列模型的选择模型。The following code formats a string containing the row and column of the lead selection:以下代码格式化包含潜在客户选择的行和列的字符串:

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

User selections generate a number of events. 用户选择会生成许多事件。For information on these, refer to How to Write a List Selection Listener in the Writing Event Listeners lesson.有关这些的信息,请参阅“编写事件侦听器”课程中的“如何编写列表选择侦听器”。


NOTE: Selection data actually describes selected cells in the "view" (table data as it appears after any sorting or filtering) rather than in the table model. 选择数据实际上是在“视图”(任何排序或筛选后显示的表数据)中描述选定单元格,而不是在表模型中。This distinction does not matter unless your viewed data has been rearranged by sorting, filtering, or user manipulation of columns. 除非已通过排序、筛选或用户对列的操作重新排列了已查看的数据,否则这种区别并不重要。In that case, you must convert selection coordinates using the conversion methods described in Sorting and Filtering. 在这种情况下,必须使用排序和筛选中描述的转换方法转换选择坐标。

Creating a Table Model创建表模型

Every table object uses a table model object to manage the actual table data. 每个表对象都使用一个表模型对象来管理实际的表数据。A table model object must implement the TableModel interface. 表模型对象必须实现TableModel接口。If the programmer does not provide a table model object, JTable automatically creates an instance of DefaultTableModel. 如果程序员没有提供表模型对象,JTable会自动创建DefaultTableModel的实例。This relationship is illustrated below.这种关系如下所示。

Relation between table, table object, model object

The JTable constructor used by SimpleTableDemo creates its table model with code like this:SimpleTableDemo使用的JTable构造函数使用如下代码创建其表模型:

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

As the preceding code shows, implementing a table model can be simple. 如前面的代码所示,实现表模型非常简单。Generally, you implement your table model in a subclass of the AbstractTableModel class.通常,您在AbstractTableModel类的子类中实现表模型。

Your model might hold its data in an array, vector, or hash map, or it might get the data from an outside source such as a database. 您的模型可能将其数据保存在数组、向量或哈希映射中,也可能从外部源(如数据库)获取数据。It might even generate the data at execution time.它甚至可能在执行时生成数据。

This table is different from the SimpleTableDemo table in the following ways:该表与SimpleTableDemo表的不同之处如下:

See below the code taken from TableDemo.java that is different from the SimpleTableDemo.java. 下面是从SimpleTableDemo.java中获取的代码,它不同于SimpleTableDemo.javaBold font indicates the code that makes this table's model different from the table model defined automatically for SimpleTableDemo.粗体表示使此表的模型不同于为SimpleTableDemo自动定义的表模型的代码。

public TableDemo() {
    ...
    JTable table = new JTable(new MyTableModel());
    ...
}

class MyTableModel extends AbstractTableModel {
    private String[] columnNames = ...//same as before...
    private Object[][] data = ...//same as before...

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

    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
        //Note that the data/cell address is constant,
        //no matter where the cell appears onscreen.
if (col < 2) {
            return false;
        } else {
            return true;
        }
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    public void setValueAt(Object value, int row, int col) {
        data[row][col] = value;
        fireTableCellUpdated(row, col);
    }
    ...
}

Listening for Data Changes监听数据更改

A table model can have a set of listeners that are notified whenever the table data changes. 表模型可以有一组监听器,每当表数据发生变化时都会通知监听器。Listeners are instances of TableModelListener. 监听器是TableModelListener的实例。In the following example code, SimpleTableDemo is extended to include such a listener. 在下面的示例代码中,SimpleTableDemo被扩展为包含这样一个监听器。New code is in bold.新代码以粗体显示。

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

        ...// Do something with the data...
    }
    ...
}

Firing Data Change Events触发数据更改事件

In order to fire data change events the table model must know how to construct a TableModelEvent object. 为了触发数据更改事件,表模型必须知道如何构造TableModelEvent对象。This can be a complex procedure, but is already implemented in DefaultTableModel. 这可能是一个复杂的过程,但已在DefaultTableModel中实现。You can either allow JTable to use its default instance of DefaultTableModel, or create your own custom subclass of DefaultTableModel.您可以允许JTable使用其默认的DefaultTableModel实例,也可以创建自己的DefauctTableModel自定义子类。

If DefaultTableModel is not a suitable base class for your custom table model class, consider subclassing AbstractTableModel. 如果DefaultTableModel不是自定义表模型类的合适基类,请考虑将AbstractTableModel子类化。This class implements a simple framework for constructing TableModelEvent objects. 此类实现了一个用于构造TableModelEvent对象的简单框架。Your custom class simply needs to invoke one the following AbstractTableModel methods each time table data is changed by an external source.每次外部源更改表数据时,自定义类只需调用以下AbstractTableModel方法之一。

Method方法 Change更改
fireTableCellUpdated Update of specified cell.指定单元格的更新。
fireTableRowsUpdated Update of specified rows指定行的更新
fireTableDataChanged Update of entire table (data only).更新整个表(仅数据)。
fireTableRowsInserted New rows inserted.插入新行。
fireTableRowsDeleted Existing rows Deleted已删除现有行
fireTableStructureChanged   Invalidate entire table, both data and structure.使整个表(数据和结构)无效。

Concepts: Editors and Renderers概念:编辑器和渲染器

Before you go on to the next few tasks, you need to understand how tables draw their cells. 在继续下几个任务之前,您需要了解表如何绘制单元格。You might expect each cell in a table to be a component. 您可能希望表中的每个单元格都是一个组件。However, for performance reasons, Swing tables are implemented differently.然而,出于性能原因,Swing表的实现方式不同。

Instead, a single cell renderer is generally used to draw all of the cells that contain the same type of data. 相反,通常使用单个单元格渲染器来绘制包含相同类型数据的所有单元格。You can think of the renderer as a configurable ink stamp that the table uses to stamp appropriately formatted data onto each cell. 您可以将渲染器视为一个可配置的墨迹标记,表使用它将适当格式化的数据标记到每个单元格上。When the user starts to edit a cell's data, a cell editor takes over the cell, controlling the cell's editing behavior.当用户开始编辑单元格数据时,单元格编辑器将接管单元格,控制单元格的编辑行为。

For example, each cell in the # of Years column in TableDemo contains Number data — specifically, an Integer object. 例如,TableDemo# of Years列中的每个单元格包含数字数据;特别是Integer对象。By default, the cell renderer for a Number-containing column uses a single JLabel instance to draw the appropriate numbers, right-aligned, on the column's cells. 默认情况下,包含Number的列的单元格渲染器使用单个JLabel实例在列的单元格上绘制适当的数字(右对齐)。If the user begins editing one of the cells, the default cell editor uses a right-aligned JTextField to control the cell editing.如果用户开始编辑其中一个单元格,默认单元格编辑器将使用右对齐的JTextField来控制单元格编辑。

To choose the renderer that displays the cells in a column, a table first determines whether you specified a renderer for that particular column. 要选择显示列中单元格的渲染器,表首先确定是否为该特定列指定了渲染器。If you did not, then the table invokes the table model's getColumnClass method, which gets the data type of the column's cells. 如果没有,则表调用表模型的getColumnClass方法,该方法获取列单元格的数据类型。Next, the table compares the column's data type with a list of data types for which cell renderers are registered. 接下来,该表将列的数据类型与单元格渲染器注册的数据类型列表进行比较。This list is initialized by the table, but you can add to it or change it. 此列表由表初始化,但您可以添加或更改它。Currently, tables put the following types of data in the list:目前,表中列出了以下类型的数据:

Cell editors are chosen using a similar algorithm.使用类似的算法选择单元格编辑器。

Remember that if you let a table create its own model, it uses Object as the type of every column. 请记住,如果让一个表创建自己的模型,它将使用Object作为每个列的类型。To specify more precise column types, the table model must define the getColumnClass method appropriately, as demonstrated by TableDemo.java.要指定更精确的列类型,表模型必须适当地定义getColumnClass方法,如TableDemo.java所示。

Keep in mind that although renderers determine how each cell or column header looks and can specify its tool tip text, a renderer does not handle events. 请记住,尽管渲染器确定每个单元格或列标题的外观,并可以指定其工具提示文本,但渲染器不处理事件。If you need to pick up the events that take place inside a table, the technique you use varies by the sort of event you are interested in:如果您需要提取表中发生的事件,您使用的技术因您感兴趣的事件类型而异:

Situation情况 How to Get Events如何获取活动
To detect events from a cell that is being edited...要检测正在编辑的单元格中的事件…… Use the cell editor (or register a listener on the cell editor).使用单元编辑器(或在单元编辑器上注册监听器)。
To detect row/column/cell selections and deselections...要检测行/列/单元格选择和取消选择…… Use a selection listener as described in Detecting User Selections.使用检测用户选择中所述的选择侦听器。
To detect mouse events on a column header...要检测列标题上的鼠标事件…… Register the appropriate type of mouse listener on the table's JTableHeader object. 在表的JTableHeader对象上注册适当类型的鼠标侦听器(See TableSorter.java for an example.)(有关示例,请参阅表TableSorter.java。)
To detect other events...要检测其他事件…… Register the appropriate listener on the JTable object.JTable对象上注册适当的侦听器。

The next few sections tell you how to customize display and editing by specifying renderers and editors. 接下来的几节将告诉您如何通过指定渲染器和编辑器来自定义显示和编辑。You can specify cell renderers and editors either by column or by data type.可以按列或按数据类型指定单元格渲染器和编辑器。

Using Custom Renderers使用自定义渲染器

This section tells you how to create and specify a cell renderer. 本节介绍如何创建和指定单元格渲染器。You can set a type-specific cell renderer using the JTable method setDefaultRenderer. 可以使用JTable方法setDefaultRenderer设置特定于类型的单元格渲染器。To specify that cells in a particular column should use a renderer, you use the TableColumn method setCellRenderer. 要指定特定列中的单元格应使用渲染器,请使用TableColumn方法setCellRendererYou can even specify a cell-specific renderer by creating a JTable subclass.您甚至可以通过创建JTable子类来指定特定于单元的渲染器。

It is easy to customize the text or image rendered by the default renderer, DefaultTableCellRenderer. 自定义默认渲染器DefaultTableCellRenderer渲染的文本或图像很容易。You just create a subclass and implement the setValue method so that it invokes setText or setIcon with the appropriate string or image. 您只需创建一个子类并实现setValue方法,以便它使用适当的字符串或图像调用setTextsetIconFor example, here is how the default date renderer is implemented:例如,以下是如何实现默认日期渲染器:

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

If extending DefaultTableCellRenderer is insufficient, you can build a renderer using another superclass. 如果扩展DefaultTableCellRenderer不够,可以使用另一个超类构建渲染器。The easiest way is to create a subclass of an existing component, making your subclass implement the TableCellRenderer interface. 最简单的方法是创建现有组件的子类,使子类实现TableCellRenderer接口。TableCellRenderer requires just one method: getTableCellRendererComponent. TableCellRenderer只需要一个方法:getTableCellRendererComponentYour implementation of this method should set up the rendering component to reflect the passed-in state, and then return the component.此方法的实现应设置呈现组件以反映传入状态,然后返回组件。

In the snapshot of TableDialogEditDemo, the renderer used for Favorite Color cells is a subclass of JLabel called ColorRenderer. TableDialogEditDemo快照中,用于常用颜色单元格的渲染器是JLabel的一个子类,称为ColorRendererHere are excerpts from ColorRenderer.java that show how it is implemented.下面是ColorRenderer.java的摘录,展示了它是如何实现的。

public class ColorRenderer extends JLabel
                           implements TableCellRenderer {
    ...
    public ColorRenderer(boolean isBordered) {
        this.isBordered = isBordered;
        setOpaque(true); //MUST do this for background to show up.
    }

    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 is a solid border in the color
                //table.getSelectionBackground().
                setBorder(selectedBorder);
            } else {
                ...
                //unselectedBorder is a solid border in the color
                //table.getBackground().
                setBorder(unselectedBorder);
            }
        }
        
        setToolTipText(...); //Discussed in the following section
        return this;
    }
}

Here is the code from TableDialogEditDemo.java that registers a ColorRenderer instance as the default renderer for all Color data:以下是TableDialogEditDemo.java中的代码,它将ColorRenderer实例注册为所有Color数据的默认渲染器:

table.setDefaultRenderer(Color.class, new ColorRenderer(true));

To specify a cell-specific renderer, you need to define a JTable subclass that overrides the getCellRenderer method. 要指定特定于单元的渲染器,需要定义一个JTable子类,该子类重写getCellRenderer方法。For example, the following code makes the first cell in the first column of the table use a custom renderer:例如,以下代码使表的第一列中的第一个单元格使用自定义渲染器:

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

Specifying Tool Tips for Cells为单元格指定工具提示

By default, the tool tip text displayed for a table cell is determined by the cell's renderer. 默认情况下,为表格单元格显示的工具提示文本由单元格的渲染器确定。However, sometimes it can be simpler to specify tool tip text by overriding JTable's implementation of the getToolTipText(MouseEvent) method. 然而,有时通过重写JTablegetToolTipText(MouseEvent)方法实现来指定工具提示文本可能更简单。This section shows you how to use both techniques.本节向您展示如何使用这两种技术。

To add a tool tip to a cell using its renderer, you first need to get or create the cell renderer. 要使用渲染器向单元格添加工具提示,首先需要获取或创建单元格渲染器。Then, after making sure the rendering component is a JComponent, invoke the setToolTipText method on it.然后,在确保呈现组件是JComponent之后,调用其上的setToolTipText方法。

An example of setting tool tips for cells is in TableRenderDemo. 为单元格设置工具提示的示例在TableRenderDemo中。Click the Launch button to run it using Java™ Web Start (download JDK 7 or later). 单击启动按钮,使用Java™Web启动运行它(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableRenderDemo example

The source code is in TableRenderDemo.java. 源代码在TableRenderDemo.java中。It adds tool tips to the cells of the Sport column with the following code:它使用以下代码向“运动”列的单元格添加工具提示:

//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
        new DefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
sportColumn.setCellRenderer(renderer);

Although the tool tip text in the previous example is static, you can also implement tool tips whose text changes depending on the state of the cell or program. 尽管上一示例中的工具提示文本是静态的,但您也可以实现工具提示,其文本根据单元格或程序的状态而变化。Here are a couple ways to do so:以下是几种方法:

An example of adding code to a cell renderer is in TableDialogEditDemo. 将代码添加到单元格渲染器的示例在TableDialogEditDemo中。Click the Launch button to run it using Java™ Web Start (download JDK 7 or later). 单击启动按钮,使用Java™Web启动运行它(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableDialogEditDemo example

TableDialogEditDemo uses a renderer for colors, implemented in ColorRenderer.java, that sets the tool tip text using the boldface code in the following snippet:TableDialogEditDemo使用ColorRenderer.java中实现的颜色渲染器,该渲染器使用以下代码段中的粗体代码设置工具提示文本:

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

Here is an example of what the tool tip looks like:以下是工具提示的示例:

TableDialogEditDemo with a tool tip describing the moused-over cell's RGB value

You can specify tool tip text by overriding JTable's getToolTipText(MouseEvent) method. 您可以通过重写JTablegetToolTipText(MouseEvent)方法来指定工具提示文本。The program TableToolTipsDemo shows how. Click the Launch button to run it using Java™ Web Start (download JDK 7 or later). 程序TableToolTipsDemo显示了怎样做。单击启动按钮,使用Java™Web启动运行它(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableToolTipsDemo example

The cells with tool tips are in the Sport and Vegetarian columns. Here is a picture of its tool tip:带有工具提示的单元格在运动素食列中。以下是其工具提示的图片:

TableToolTipsDemo with a tool tip for a cell in the Sport column

Here is the code from TableToolTipsDemo.java that implements tool tips for cells in the Sport and Vegetarian columns:以下是TableToolTipsDemo.java中的代码,它为运动和素食专栏中的单元格实现了工具提示:

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;
    }
    ...
}

The code is fairly straightforward, except perhaps for the call to convertColumnIndexToModel. 代码相当简单,除了调用convertColumnIndexToModel之外。That call is necessary because if the user moves the columns around, the view's index for the column will not match the model's index for the column. 该调用是必要的,因为如果用户移动列,则该列的视图索引将与该列的模型索引不匹配。For example, the user might drag the Vegetarian column (which the model considers to be at index 4) so it is displayed as the first column — at view index 0. 例如,用户可以拖动Vegetarian列(模型认为该列位于索引4),以便将其显示为第一列—在视图索引0处。Since prepareRenderer provides the view index, you need to translate the view index to a model index so you can be sure the intended column has been selected.由于prepareRenderer提供了视图索引,因此需要将视图索引转换为模型索引,以便确保已选择所需的列。

Specifying Tool Tips for Column Headers为列标题指定工具提示

You can add a tool tip to a column header by setting the tool tip text for the table's JTableHeader. 通过为表的JTableHeader设置工具提示文本,可以将工具提示添加到列标题。Often, different column headers require different tool tip text. 通常,不同的列标题需要不同的工具提示文本。You can change the text by overriding the table header's getToolTipText method. 您可以通过重写表头的getToolTipText方法来更改文本。Alternately, you can invoke TableColumn.setHeaderRenderer to provide a custom renderer for the header.或者,您可以调用TableColumn.setHeaderRenderer为标头提供自定义渲染器。

An example of using the same tool tip text for all column headers is in TableSorterDemo.java. TableSorterDemo.java中,为所有列标题使用相同的工具提示文本。Here is how it sets the tool tip text:以下是如何设置工具提示文本:

table.getTableHeader().setToolTipText(
        "Click to sort; Shift-Click to sort in reverse order");

TableToolTipsDemo.java has an example of implementing column header tool tips that vary by column. 具有实现列标题工具提示的示例,这些提示随列而异。If you run TableToolTipsDemo (click the Launch button) using Java™ Web Start (download JDK 7 or later). 如果使用Java™Web启动运行TableToolTipsDemo(单击启动按钮)(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableToolTipsDemo example

You will see the tool tips when you mouse over any column header except for the first two. 当您将鼠标移到除前两个之外的任何列标题上时,您将看到工具提示。No tool tips were supplied for the name columns since they seemed self-explanatory. 没有为名称列提供工具提示,因为它们似乎是自解释的。Here is a picture of one of the column header tool tips:以下是列标题工具提示之一的图片:

TableToolTipsDemo with a tool tip for a column header

The following code implements the tool tips. 以下代码实现了工具提示。Basically, it creates a subclass of JTableHeader that overrides the getToolTipText(MouseEvent) method so that it returns the text for the current column. 基本上,它创建了JTableHeader的子类,该子类重写了getToolTipText(MouseEvent)方法,以便返回当前列的文本。To associate the revised table header with the table, the JTable method createDefaultTableHeader is overridden so that it returns an instance of the JTableHeader subclass.要将修改后的表头与表关联,将重写JTable方法createDefaultTableHeader,以便它返回JTableHeader子类的实例。

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()) {
    ...

    //Implement table header tool tips.
    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];
            }
        };
    }
};

Sorting and Filtering排序和筛选

Table sorting and filtering is managed by a sorter object. 表排序和筛选由一个排序器对象管理。The easiest way to provide a sorter object is to set autoCreateRowSorter bound property to true:提供排序器对象的最简单方法是将autoCreateRowSorter绑定属性设置为true

JTable table = new JTable();
table.setAutoCreateRowSorter(true);

This action defines a row sorter that is an instance of javax.swing.table.TableRowSorter. 此操作定义了一个行排序器,该行排序器是javax.swing.table.TableRowSorter的实例。This provides a table that does a simple locale-specific sort when the user clicks on a column header. 这提供了一个表,当用户单击列标题时,该表执行简单的特定于区域设置的排序。This is demonstrated in TableSortDemo.java, as seen in this screen shot:这在TableSortDemo.java中演示,如本屏幕截图所示:

TableSortDemo after clicking Last Name

To have more control over sorting, you can construct an instance of TableRowSorter and specify that it is the sorter object for your table.为了更好地控制排序,可以构造TableRowSorter的实例,并指定它是表的排序器对象。

TableRowSorter<TableModel> sorter 
    = new TableRowSorter<TableModel>(table.getModel());
table.setRowSorter(sorter);

TableRowSorter uses java.util.Comparator objects to sort its rows. TableRowSorter使用java.util.Comparator对象对其行进行排序。A class that implements this interface must provide a method called compare that defines how any two objects are compared for the purpose of sorting. 实现此接口的类必须提供一个名为compare的方法,该方法定义如何比较任何两个对象以进行排序。For example, the following code creates a Comparator that sorts a set of strings by the last word in each string:例如,以下代码创建了一个Comparator,该比较器按照每个字符串中的最后一个单词对一组字符串进行排序:

Comparator<String> comparator = new Comparator<String>() {
    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]);
    }
};

This example is fairly simplistic; more typically, a Comparator implementation is a subclass of java.text.Collator. 这个例子相当简单;更典型的是,比较器实现是java.text.Collator的子类。You can define your own subclass, use the factory methods in Collator to obtain a Comparator for a specific locale, or use java.text.RuleBasedCollator.您可以定义自己的子类,使用Collator中的工厂方法获得特定区域设置的Comparator,或者使用java.text.RuleBasedCollator

To determine which Comparator to use for a column, TableRowSorter attempts to apply each of the following rules in turn. 为了确定列要使用哪个ComparatorTableRowSorter尝试依次应用以下规则。Rules are followed in the order listed below; the first rule that provides the sorter with a Comparator is used, and the remaining rules ignored.按下列顺序遵守规则;使用为分拣机提供Comparator的第一条规则,忽略其余规则。

  1. If a comparator has been specified by invoking setComparator, use that comparator.如果通过调用Comparator指定了比较器,则使用该比较器。
  2. If the table model reports that the column data consists of strings (TableModel.getColumnClass returns String.class for that column), use a comparator that sorts the strings based on the current locale.如果表模型报告列数据由字符串组成(TableModel.getColumnClass返回该列的String.class),则使用比较器根据当前区域设置对字符串进行排序。
  3. If the column class returned by TableModel.getColumnClass implements Comparable, use a comparator that sorts the strings based on the values returned by Comparable.compareTo.如果TableModel.getColumnClass返回的列类实现了Comparable,则使用比较器根据Comparable.compareTo返回的值对字符串进行排序。
  4. If a string convertor has been specified for the table by invoking setStringConverter, use a comparator that sorts the resulting string representations based on the current locale.如果通过调用setStringConverter为表指定了字符串转换器,请使用比较器,该比较器根据当前区域设置对结果字符串表示进行排序。
  5. If none of the previous rules apply, use a comparator that invokes toString on the column data and sorts the resulting strings based on the current locale.如果前面的任何规则都不适用,则使用比较器调用列数据上的toString,并根据当前区域设置对结果字符串进行排序。

For more sophisticated kinds of sorting, subclass TableRowSorter or its parent class javax.swing.DefaultRowSorter.对于更复杂的排序,子类TableRowSorter或其父类javax.swing.DefaultRowSorter

To specify the sort order and sort precedence for columns, invoke setSortKeys. 要指定列的排序顺序和排序优先级,请调用setSortKeysHere is an example that sorts the table used in the examples by the first two columns. 下面的示例按前两列对示例中使用的表进行排序。The precedence of the columns in the sort is indicated by the order of the sort keys in the sort key list. 排序中列的优先级由排序键列表中排序键的顺序指示。In this case, the second column has the first sort key, so they rows are sorted by first name, then last name.在本例中,第二列具有第一个排序键,因此这些行按名字排序,然后按姓氏排序。

List <RowSorter.SortKey> sortKeys 
    = new ArrayList<RowSorter.SortKey>();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
sorter.setSortKeys(sortKeys);

In addition to reordering the results, a table sorter can also specify which rows will be displayed. 除了对结果重新排序之外,表排序器还可以指定将显示哪些行。This is known as filtering. 这被称为筛选TableRowSorter implements filtering using javax.swing.RowFilter objects. TableRowSorter使用javax.swing.RowFilter对象实现筛选。RowFilter implements several factory methods that create common kinds of filters. 实现多个工厂方法,用于创建常见类型的筛选器。For example, regexFilter returns a RowFilter that filters based on a regular expression.例如,regexFilter返回基于正则表达式进行筛选的RowFilter

In the following example code, you explicitly create a sorter object so you can later use it to specify a filter:在下面的示例代码中,您显式创建了一个排序器对象,以便以后可以使用它指定筛选器:

MyTableModel model = new MyTableModel();
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model);
table.setRowSorter(sorter);

Then you filter based on the current value of a text field:然后根据文本字段的当前值进行筛选:

private void newFilter() {
    RowFilter<MyTableModel, Object> rf = null;
    //If current expression doesn't parse, don't update.
    try {
        rf = RowFilter.regexFilter(filterText.getText(), 0);
    } catch (java.util.regex.PatternSyntaxException e) {
        return;
    }
    sorter.setRowFilter(rf);
}

In a subsequent example, newFilter() is invoked every time the text field changes. 在随后的示例中,每次文本字段更改时都会调用newFilter()When the user enters complicated regular expressions, the try...catch prevents the syntax exception from interfering with input.当用户输入复杂的正则表达式时,try...catch可以防止语法异常干扰输入。

When a table uses a sorter, the data the users sees may be in a different order than that specified by the data model, and may not include all rows specified by the data model. 当表使用排序器时,用户看到的数据的顺序可能与数据模型指定的顺序不同,并且可能不包括数据模型所指定的所有行。The data the user actually sees is known as the view, and has its own set of coordinates. 用户实际看到的数据称为视图,并有自己的坐标集。JTable provides methods that convert from model coordinates to view coordinates — convertColumnIndexToView and convertRowIndexToView — and that convert from view coordinates to model coordinates — convertColumnIndexToModel and convertRowIndexToModel.JTable提供了从模型坐标转换为视图坐标的方法;convertColumnIndexToViewconvertRowIndexToView;并将视图坐标转换为模型坐标;convertColumnIndexToModelconvertRowIndexToModel


NOTE: When using a sorter, always remember to translate cell coordinates. 使用排序器时,请始终记住转换单元格坐标。

The following example brings together the ideas discussed in this section. 下面的示例将本节中讨论的想法结合在一起。TableFilterDemo.java adds a small number of changes to TableDemo. TableDemo添加少量更改。These include the code snippets earlier in this section, which provide a sorter for the main table, and use a text field to supply the filtering regular expression. 其中包括本节前面的代码片段,它为主表提供了一个排序器,并使用文本字段提供筛选正则表达式。The following screen shot shows TableFilterDemo before any sorting or filtering has been done. 以下屏幕截图显示了完成任何排序或筛选之前的TableFilterDemoNotice that row 3 in the model is still the same as row 3 in the view:请注意,模型中的第3行仍然与视图中的第三行相同:

TableFilterDemo without sorting

If the user clicks twice on the second column, the fourth row becomes the first row — but only in the view:如果用户在第二列上单击两次,则第四行变为第一行;但仅在以下视图中:

TableFilterDemo with reverse sorting in second column

As previously noted, the text the user enters in the "Filter Text" text field defines a filter that determines which rows are shown. 如前所述,用户在“筛选器文本”文本字段中输入的文本定义了一个筛选器,用于确定显示哪些行。As with sorting, filtering can cause view coordinates to diverge from model coordinates:与排序一样,筛选可能导致视图坐标偏离模型坐标:

TableFilterDemo with filtering

Here is the code that updates the status field to reflect the current selection:下面是更新状态字段以反映当前选择的代码:

table.getSelectionModel().addListSelectionListener(
        new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent event) {
                int viewRow = table.getSelectedRow();
                if (viewRow < 0) {
                    //Selection got filtered away.
                    statusText.setText("");
                } else {
                    int modelRow = 
                        table.convertRowIndexToModel(viewRow);
                    statusText.setText(
                        String.format("Selected Row in view: %d. " +
                            "Selected Row in model: %d.", 
                            viewRow, modelRow));
                }
            }
        }
);

Using a Combo Box as an Editor使用组合框作为编辑器

Setting up a combo box as an editor is simple, as the following example shows. 组合框设置为编辑器很简单,如下例所示。The bold line of code sets up the combo box as the editor for a specific column.粗体代码行将组合框设置为特定列的编辑器。

TableColumn sportColumn = table.getColumnModel().getColumn(2);
...
JComboBox comboBox = new JComboBox();
comboBox.addItem("Snowboarding");
comboBox.addItem("Rowing");
comboBox.addItem("Chasing toddlers");
comboBox.addItem("Speed reading");
comboBox.addItem("Teaching high school");
comboBox.addItem("None");
sportColumn.setCellEditor(new DefaultCellEditor(comboBox));

Here is a picture of the combo box editor in use:下面是正在使用的组合框编辑器的图片:

A combo box cell editor in use

The preceding code is from TableRenderDemo.java. 前面的代码来自TableRenderDemo.javaYou can run TableRenderDemo (click the Launch button) using Java™ Web Start (download JDK 7 or later). 您可以使用Java™Web启动运行TableRenderDemo(单击启动按钮)(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableRenderDemo example

Using Other Editors使用其他编辑器

Whether you are setting the editor for a single column of cells (using the TableColumn setCellEditor method) or for a specific type of data (using the JTable setDefaultEditor method), you specify the editor using an argument that adheres to the TableCellEditor interface. 无论是为单列单元格(使用TableColumn setCellEditor方法)还是为特定类型的数据(使用JTable setDefaultEditor方法)设置编辑器,都可以使用符合TableCellEditor接口的参数指定编辑器。Fortunately, the DefaultCellEditor class implements this interface and provides constructors to let you specify an editing component that is a JTextField, JCheckBox, or JComboBox. 幸运的是,DefaultCellEditor类实现了这个接口,并提供了构造函数,让您可以指定一个编辑组件,即JTextFieldJCheckBoxJComboBoxUsually you do not have to explicitly specify a check box as an editor, since columns with Boolean data automatically use a check box renderer and editor.通常,您不必显式指定复选框作为编辑器,因为具有Boolean数据的列会自动使用复选框渲染器和编辑器。

What if you want to specify an editor other than a text field, check box, or combo box? 如果要指定文本字段、复选框或组合框以外的编辑器,该怎么办?As DefaultCellEditor does not support other types of components, you must do a little more work. 由于DefaultCellEditor不支持其他类型的组件,您必须做更多的工作。You need to create a class that implements the TableCellEditor interface. 您需要创建一个实现TableCellEditor接口的类。The AbstractCellEditor class is a good superclass to use. AbstractCellEditor类是一个很好的超类。It implements TableCellEditor's superinterface, CellEditor, saving you the trouble of implementing the event firing code necessary for cell editors.它实现了TableCellEditor的超级界面CellEditor,从而省去了实现单元格编辑器所需的事件触发代码的麻烦。

Your cell editor class needs to define at least two methods — getCellEditorValue and getTableCellEditorComponent. 单元格编辑器类需要定义至少两个方法;getCellEditorValuegetTableCellEditorComponentThe getCellEditorValue method, required by CellEditor, returns the cell's current value. CellEditor所需的getCellEditorValue方法返回单元格的当前值。The getTableCellEditorComponent method, required by TableCellEditor, should configure and return the component that you want to use as the editor.TableCellEditor所需的getTableCellEditorComponent方法应配置并返回您要用作编辑器的组件。

Here is a picture of a table with a dialog that serves, indirectly, as a cell editor. 下面是一张带有对话框的表格的图片,该对话框间接用作单元格编辑器。When the user begins editing a cell in the Favorite Color column, a button (the true cell editor) appears and brings up the dialog, with which the user can choose a different color.当用户开始编辑“最喜爱的颜色”列中的单元格时,会出现一个按钮(真正的单元格编辑器),并打开对话框,用户可以使用该对话框选择不同的颜色。

The cell editor brings up a dialog

You can run TableDialogEditDemo (click the Launch button) using Java™ Web Start (download JDK 7 or later). 您可以使用Java™Web启动运行TableDialogEditDemo(单击启动按钮)(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableDialogEditDemo example

Here is the code, taken from ColorEditor.java, that implements the cell editor.下面是从ColorEditor.java中获取的代码,它实现了单元格编辑器。

public class ColorEditor extends AbstractCellEditor
                         implements TableCellEditor,
                                    ActionListener {
    Color currentColor;
    JButton button;
    JColorChooser colorChooser;
    JDialog dialog;
    protected static final String EDIT = "edit";

    public ColorEditor() {
        button = new JButton();
        button.setActionCommand(EDIT);
        button.addActionListener(this);
        button.setBorderPainted(false);

        //Set up the dialog that the button brings up.
        colorChooser = new JColorChooser();
        dialog = JColorChooser.createDialog(button,
                                        "Pick a Color",
                                        true,  //modal
                                        colorChooser,
                                        this,  //OK button handler
                                        null); //no CANCEL button handler
    }

    public void actionPerformed(ActionEvent e) {
        if (EDIT.equals(e.getActionCommand())) {
            //The user has clicked the cell, so
            //bring up the dialog.
            button.setBackground(currentColor);
            colorChooser.setColor(currentColor);
            dialog.setVisible(true);

            fireEditingStopped(); //Make the renderer reappear.

        } else { //User pressed dialog's "OK" button.
            currentColor = colorChooser.getColor();
        }
    }

    //Implement the one CellEditor method that AbstractCellEditor doesn't.
    public Object getCellEditorValue() {
        return currentColor;
    }

    //Implement the one method defined by TableCellEditor.
    public Component getTableCellEditorComponent(JTable table,
                                                 Object value,
                                                 boolean isSelected,
                                                 int row,
                                                 int column) {
        currentColor = (Color)value;
        return button;
    }
}

As you can see, the code is pretty simple. 正如您所看到的,代码非常简单。The only part that is a bit tricky is the call to fireEditingStopped at the end of the editor button's action handler. 唯一有点棘手的部分是在编辑器按钮的操作处理程序末尾调用fireEditingStoppedWithout this call, the editor would remain active, even though the modal dialog is no longer visible. 如果没有此调用,编辑器将保持活动状态,即使模式对话框不再可见。The call to fireEditingStopped lets the table know that it can deactivate the editor, letting the cell be handled by the renderer again.fireEditingStopped的调用让表知道它可以停用编辑器,让单元格再次由渲染器处理。

Using an Editor to Validate User-Entered Text使用编辑器验证用户输入的文本

If a cell's default editor allows text entry, you get some error checking for free if the cell's type is specified as something other than String or Object. 如果单元格的默认编辑器允许文本输入,则如果单元格的类型指定为StringObject以外的其他类型,则可以免费进行一些错误检查。The error checking is a side effect of converting the entered text into an object of the proper type.错误检查是将输入的文本转换为正确类型的对象的副作用。

The automatic checking of user-entered strings occurs when the default editor attempts to create a new instance of the class associated with the cell's column. 当默认编辑器尝试创建与单元格列关联的类的新实例时,会自动检查用户输入的字符串。The default editor creates this instance using a constructor that takes a String as an argument. 默认编辑器使用以String作为参数的构造函数创建此实例。For example, in a column whose cells have type Integer, when the user types in "123" the default editor creates the corresponding Integer using code equivalent to new Integer("123"). 例如,在单元格类型为Integer的列中,当用户键入“123”时,默认编辑器使用与new Integer("123")等效的代码创建相应的整数。If the constructor throws an exception, the cell's outline turns red and refuses to let focus move out of the cell. 如果构造函数抛出异常,单元格的轮廓将变为红色,并拒绝将焦点移出单元格。If you implement a class used as a column data type, you can use the default editor if your class supplies a constructor that takes a single argument of type String.如果实现用作列数据类型的类,则可以使用默认编辑器,前提是类提供了一个采用String类型的单个参数的构造函数。

If you like having a text field as the editor for a cell, but want to customize it — perhaps to check user-entered text more strictly or to react differently when the text is invalid — you can change the cell editor to use a formatted text field. 如果您喜欢将文本字段用作单元格的编辑器,但希望自定义它—也许是为了更严格地检查用户输入的文本,或者在文本无效时做出不同的反应;可以将单元格编辑器更改为使用格式化文本字段The formatted text field can check the value either continuously while the user is typing or after the user has indicated the end of typing (such as by pressing Enter).格式化文本字段可以在用户键入时或在用户指示键入结束后(如按Enter键)连续检查值。

The following code, taken from a demo named TableFTFEditDemo.java, sets up a formatted text field as an editor that limits all integer values to be between 0 and 100. 以下代码取自名为TableFTFEditDemo.java的演示,将格式化文本字段设置为编辑器,将所有整数值限制在0和100之间。You can run TableFTFEditDemo (click the Launch button) using Java™ Web Start (download JDK 7 or later). 您可以使用Java™Web启动运行TableFTFEditDemo(单击启动按钮)(下载JDK 7或更高版本)。Or, to compile and run the example yourself, consult the example index.或者,要自己编译和运行示例,请参考示例索引

Launches the TableFTFEditDemo example

The following code makes the formatted text field the editor for all columns that contain data of type Integer.以下代码使格式化文本字段成为包含Integer类型数据的所有列的编辑器。

table.setDefaultEditor(Integer.class,
                       new IntegerEditor(0, 100));

The IntegerEditor class is implemented as a subclass of DefaultCellEditor that uses a JFormattedTextField instead of the JTextField that DefaultCellEditor supports. IntegerEditor类实现为DefaultCellEditor的子类,它使用JFormattedTextField而不是DefaultCellEditor支持的JTextFieldIt accomplishes this by first setting up a formatted text field to use an integer format and have the specified minimum and maximum values, using the API described in How to Use Formatted Text Fields. 为此,它首先使用如何使用格式化文本字段中描述的API设置格式化文本字段以使用整数格式并具有指定的最小值和最大值。It then overrides the DefaultCellEditor implementation of the getTableCellEditorComponent, getCellEditorValue, and stopCellEditing methods, adding the operations that are necessary for formatted text fields.然后,它覆盖了getTableCellEditorComponentgetCellEditorValuestopCellEditing方法的DefaultCellEditor实现,添加了格式化文本字段所需的操作。

The override of getTableCellEditorComponent sets the formatted text field's value property (and not just the text property it inherits from JTextField) before the editor is shown. getTableCellEditorComponent的重写在显示编辑器之前设置格式化文本字段的属性(而不仅仅是它从JTextField继承的文本属性)。The override of getCellEditorValue keeps the cell value as an Integer, rather than, say, the Long value that the formatted text field's parser tends to return. getCellEditorValue的覆盖将单元格值保持为Integer,而不是格式化文本字段的解析器倾向于返回的Long值。Finally, overriding stopCellEditing lets you check whether the text is valid, possibly stopping the editor from being dismissed. 最后,重写stopCellEditing可以检查文本是否有效,可能会阻止编辑器被关闭。If the text isn't valid, your implementation of stopCellEditing puts up a dialog that gives the user the option of continuing to edit or reverting to the last good value. 如果文本无效,则stopCellEditing的实现将弹出一个对话框,用户可以选择继续编辑或恢复到上一个有效值。The source code is a bit too long to include here, but you can find it in IntegerEditor.java.源代码太长,无法包含在这里,但您可以在IntegerEditor.java中找到它。

Printing印刷

JTable provides a simple API for printing tables. 提供用于打印表格的简单API。The easiest way to print out a table is to invoke JTable.print with no arguments:打印表的最简单方法是调用JTable.print而不带参数:

try {
    if (! table.print()) {
        System.err.println("User cancelled printing");
    }
} catch (java.awt.print.PrinterException e) {
    System.err.format("Cannot print %s%n", e.getMessage());
}

Invoking print on a normal Swing application brings up a standard printing dialog box. 在普通Swing应用程序上调用打印将打开标准print对话框。(On a headless application, the table is simply printed.) (在无头应用程序中,只需打印表格。)The return value indicates whether the user went ahead with the print job or cancelled it. 返回值指示用户是继续打印作业还是取消打印作业。JTable.print can throw java.awt.print.PrinterException, which is a checked exception; that's why the above example uses a try ... catch.可以抛出java.awt.print.PrinterException,这是一个已检查的异常;这就是为什么上面的示例使用try ... catch

JTable provides several overloads of print with various options. 提供多个带有各种选项的print重载。The following code from TablePrintDemo.java shows how to define a page header:TablePrintDemo.java中的以下代码显示了如何定义页眉:

MessageFormat header = new MessageFormat("Page {0,number,integer}");
try {
    table.print(JTable.PrintMode.FIT_WIDTH, header, null);
} catch (java.awt.print.PrinterException e) {
    System.err.format("Cannot print %s%n", e.getMessage());
}

For more sophisticated printing applications, use JTable.getPrintable to obtain a Printable object for the table. 对于更复杂的打印应用程序,请使用JTable.getPrintable获取表的可打印对象。For more on Printable, refer to the Printing lesson in the 2D Graphics trail.有关Printable的更多信息,请参阅2D图形轨迹中的打印课程。

Examples that Use Tables使用表格的示例

This table lists examples that use JTable and where those examples are described.下表列出了使用JTable的示例以及这些示例的描述位置。

Example示例 Where Described描述位置 Notes备注
SimpleTableDemo Creating a Simple Table创建简单表 A basic table with no custom model. Does not include code to specify column widths or detect user editing.没有自定义模型的基本表。不包括用于指定列宽检测用户编辑的代码
SimpleTable-
SelectionDemo
Detecting User Selections检测用户选择 Adds single selection and selection detection to SimpleTableDemo. 将单选和选择检测添加到SimpleTableDemoBy modifying the program's ALLOW_COLUMN_SELECTION and ALLOW_ROW_SELECTION constants, you can experiment with alternatives to the table default of allowing only rows to be selected.通过修改程序的ALLOW_COLUMN_SELECTIONALLOW_ROW_SELECTION常量,您可以尝试使用表默认值(仅允许选择行)的替代方案。
TableDemo Creating a Table Model创建表模型 A basic table with a custom model.具有自定义模型的基本表。
TableFTFEditDemo Using an Editor to Validate User-Entered Text使用编辑器验证用户输入的文本 Modifies TableDemo to use a custom editor (a formatted text field variant) for all Integer data.修改TableDemo以对所有Integer数据使用自定义编辑器(格式化文本字段变量)。
TableRenderDemo Using a Combo Box as an Editor使用组合框作为编辑器 Modifies TableDemo to use a custom editor (a combo box) for all data in the Sport column. 修改TableDemo以使用自定义编辑器(组合框)来处理“运动”列中的所有数据。Also intelligently picks column sizes. Uses renderers to display tool tips for the sport cells.它还智能地选择列大小。使用渲染器显示运动单元的工具提示。
TableDialogEditDemo Using Other Editors使用其他编辑器 Modifies TableDemo to have a cell renderer and editor that display a color and let you choose a new one, using a color chooser dialog.修改TableDemo,使其具有显示颜色的单元格渲染器和编辑器,并允许您使用颜色选择器对话框选择新颜色。
TableToolTipsDemo Specifying Tool Tips for Cells为单元格指定工具提示, Specifying Tool Tips for Column Headers为列标题指定工具提示, Demonstrates how to use several techniques to set tool tip text for cells and column headers.演示如何使用多种技术为单元格和列标题设置工具提示文本。
TableSortDemo Sorting and Filtering排序和筛选 Demonstrates the default sorter, which allows the user to sort columns by clicking on their headers.演示默认排序器,它允许用户通过单击列标题对列进行排序。
TableFilterDemo Sorting and Filtering排序和筛选 Demonstrates sorting and filtering, and how this can cause the view coordinates to diverge from the model coordinates.演示排序和筛选,以及这如何导致视图坐标偏离模型坐标。
TablePrintDemo Printing印刷 Demonstrates table printing.演示表格打印。
ListSelectionDemo How to Write a List Selection Listener如何编写列表选择侦听器 Shows how to use all list selection modes, using a list selection listener that's shared between a table and list.显示如何使用表和列表之间共享的列表选择侦听器使用所有列表选择模式。
SharedModelDemo Nowhere无处 Builds on ListSelectionDemo making the data model be shared between the table and list. 建立在ListSelectionDemo上,使数据模型在表和列表之间共享。If you edit an item in the first column of the table, the new value is reflected in the list.如果编辑表格第一列中的项目,则新值将反映在列表中。

Previous page: How to Use Tabbed Panes
Next page: How to Use Text Areas