The Java Tutorials -- How to Use Tablesの日本語訳(途中)

「The Java Tutorials」の「How to Use Tables」を日本語訳しています。
http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
すでに訳されているものがあるのじゃないか?とも思いつつ、勉強を兼ねて訳し始めました。

以下本編です。

このセクションは最新リリースであるJDK 6.0の機能と規約を反映して更新を行っているが、まだ完了していません。今の最新情報を見れるようにこの準備段階版を発行していますので、誤りや漏れや改善できる点がありましたら(ぜひ)お知らせください。

This section has been updated to reflect features and conventions of the latest release, JDK 6.0, but it is not yet final. We've published this preliminary version so you can get the most current information now, and so you can tell us (please!) about errors, omissions, or improvements we can make to this tutorial.

JTableクラスを使うとデータをテーブルに表示でき、必要ならばユーザにデータを編集させることもできます。JTableはデータを保持したりキャッシュしたりはしません。ただデータを表示するだけです。以下は、スクロールペイン内に表示されたよくあるテーブルの画像です。

With the JTable class you can display tables of data, optionally allowing the user to edit the data. 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:

よくあるテーブルを表示したTableDemoのスナップショット

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
  • テーブルをコンテナに追加する - Adding a Table to a Container
  • 列の幅を設定・変更する - Setting and Changing Column Widths
  • ユーザが選択する - User Selections
  • テーブルモデルを作成する - Creating a Table Model
  • データの変更を監視する - Listening for Data Changes
  • データ変更イベントを発生させる - Firing Data Change Events
  • 考え方: エディタとレンダラ - Concepts: Editors and Renderers
  • 自作のレンダラを使う - Using Custom Renderers
  • セルにツールチップを設定する - Specifying Tool Tips for Cells
  • 列ヘッダにツールチップを設定する - Specifying Tool Tips for Column Headers ←ここまでしか訳してない
  • ソートとフィルタ - Sorting and Filtering
  • エディタとしてコンボボックスを使う - Using a Combo Box as an Editor
  • その他のエディタを使う - Using Other Editors
  • ユーザが入力したテキストの妥当性を確認するエディタを使う - Using an Editor to Validate User-Entered Text
  • 印刷する - Printing
  • テーブルを使う例 - Examples that Use Tables

単純なテーブルを作成する

以下を試してみましょう:
※訳注:ドキュメントの原文で試してみてください。

  1. Java Web Startを使ってSimpleTableDemoを実行するために、Launchボタンを押してください。もしくは、例の索引からこの例を探し出して、コンパイルしてください。
  2. "Snowboarding"と表示されるセルをクリックしてください。一行目全体が選択され、Mary Campioneのデータを選択したことがわかります。特殊な強調表示は、"Snowboarding"セルを編集できることを示します。ほとんどの場合、ダブルクリックすることでテキストセルの編集を開始できます。
  3. カーソルを"First Name"の上に移動してください。そこでマウスのボタンを押し、右へドラッグしてください。ご覧のように、ユーザがテーブルの列の配置を変えることができます。
  4. カーソルを列ヘッダの右端に移動してください。そこでマウスのボタンを押し、右や左にドラッグしてください。ドラッグした列の幅が変わり、他の列は残りの空間に入るように調整されます。
  5. テーブル全体を表示するの必要なサイズ以上にウィンドウのサイズを大きくしてうださい。すべてのテーブルのセルの幅が広くなり、水平方向の余った空間を満たすように拡大されます。

Try this:

  1. Click the Launch button to run SimpleTableDemo using Java"! Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.
  2. Click the cell that contains "Snowboarding". The entire first row is selected, indicating that you have selected Mary Campione's data. 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.

SimpleTableDemo.javaのテーブルは、列名を文字列配列として宣言しています。

The table in SimpleTableDemo.java declares the column names in a String array:

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

データは二次元のObject配列として初期化および保存されます。

Its data is initialized and stored in a two-dimensional Object array:

Object data = {
	{"Mary", "Campione",
	 "Snowboarding", new Integer(5), new Boolean(false)},
	{"Alison", "Huml",
	 "Rowing", new Integer(3), new Boolean(true)},
	{"Kathy", "Walrath",
	 "Knitting", new Integer(2), new Boolean(false)},
	{"Sharon", "Zakhour",
	 "Speed reading", new Integer(20), new Boolean(true)},
	{"Philip", "Milne",
	 "Pool", new Integer(10), new Boolean(false)}
};

これらデータや列名を使ってテーブルを生成します。

Then the Table is constructed using these data and columnNames:

JTable table = new JTable(data, columnNames);

JTableにはデータを直接受け取るコンストラクタが二つあります(SimpleTableDemoはその一つを使っています)。

There are two JTable constructors that directly accept data (SimpleTableDemo uses the first):

  • JTable(Object rowData, Object[] columnNames)
  • JTable(Vector rowData, Vector columnNames)

これらのコンストラクタは使いやすいです。しかし、以下のような不便な点もあります。

The advantage of these constructors is that they are easy to use. However, these constructors also have disadvantages:

  • 自動的にすべてのセルが編集可能になります。
  • すべてデータ型が同じ(文字列)になります。例えば、テーブルの列がBooleanのデータを持つ場合、テーブルはチェックボックスでデータを表示できます。しかし、上記の二つのJTableのコンストラクタのどちらかを使った場合、Booleanデータは文字列として表示されます。最初のテーブルの画像の最後の列を見るとこの違いがわかります。
  • 配列やベクターが適さないとしても、すべてのテーブルのデータを配列かベクターに入れる必要があります。例えば、データベースを元に一連のオブジェクトをインスタンス化する場合、値を配列やベクターにコピーするのではなく、値のオブジェクトを直接要求したいこともあるでしょう。
  • They automatically make every cell editable.
  • They treat all data types the same (as strings). For example, if a table column has Boolean data, the table can display the data in a check box. However, if you use one of the two JTable constructors listed previously, your Boolean data will be displayed as a string. You can see this difference in the last column of the two previous pictures of tables.
  • They require that you put all of the table's data in an array or vector, which may not be appropriate for some data. For example, if you are instantiating a set of objects from a database, you might want to query the objects directly for their values, rather than copying all their values into an array or vector.

これらの制限をを避けるには、「テーブルモデルを作成する」で説明するテーブルモデルの実装を行う必要があります。

If you want to get around these restrictions, you need to implement your own table model, as described in Creating a Table Model.

テーブルをコンテナに追加する

以下は、テーブルのコンテナの役割を果たすスクロールペインを作成するためのよくあるコードです。

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:

  • テーブルオブジェクトへの参照を引数としてJScrollPaneのコンストラクタを呼んででいます。これによりスクロールペインはテーブルのコンテナになります。テーブルは自動的にコンテナに追加されます。
  • fillsViewportHeightプロパティを設定するために、JTable.setFillsViewportHeightを呼んでいます。このプロパティがtrueの場合コンテナの垂直方向の空間を埋めるのに十分な列がテーブルにない場合でも、テーブルはコンテナの高さを全て使用します。これにより、テーブルはドラッグアンドドロップの対象として使いやすくなります。
  • The JScrollPane constructor is invoked with an argument that refers to the table object. This creates a scroll pane as a container for the table; the table is automatically added to the container.
  • JTable.setFillsViewportHeight is invoked to set the fillsViewportHeight property. When this property is true the table uses the entire height of the container, even if the table doesn't have enough rows to use the whole vertical space. This makes it easier to use the table as a drag-and-drop target.

スクロールペインは自動的にビューポートの先頭にテーブルヘッダを配置します。テーブルのデータをスクロールしても、カラム名は先頭の領域に表示されたままでいます。

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

列の幅を設定・変更する

デフォルトで、テーブルのすべての列は同じ幅になり、自動的にテーブルの幅と列の幅の合計は同じになります。(ユーザによるテーブルを含むウィンドウのサイズ変更などにより)テーブルが広くなったり狭くなったりすると、すべてのカラムの幅はうまく変わります。

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.

初期のカラムの幅を変更するには、テーブルの列毎にsetPreferredWidthを呼び出すことができます。これにより列の希望幅と大体の相対幅の両方が設定されます。例えば、以下のSimpleTabeDemoに以下のコードを追加すると、三番目の列が他の列より大きくなります。

To customize initial column widths, you can invoke setPreferredWidth on each of your table's columns. This 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:

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

上記のコードからわかるように、テーブルのそれぞれの列はTableColumnオブジェクトで表されます。TableColumnには、最小、希望および最大の列の幅を設定するためのgetterとsetterのメソッドと、現在の幅を取得するメソッドがあります。セルの内容を表示するのに必要な大体の空間に基づいてセルの幅を設定する例は、TableRenderDemo.javaのinitColumnSizesメソッドを見てください。

As the preceding code shows, each column in a table is represented by a TableColumn object. 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. 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.

ユーザが明示的に列の幅を変更した場合、希望幅を設定することで列をユーザ指定の幅に変更しています。しかし、テーブルのサイズを変更した場合(通常はウィンドウのサイズが変更による)、列の希望幅は変化しません。代わりに、利用可能な空間を埋める新しい列幅を計算するために、既存の希望幅が使われます。

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.

テーブルのサイズ変更の動作はsetAutoResizeModeを呼ぶことで変更できます。

You can change a table's resize behavior by invoking setAutoResizeMode.

ユーザが選択する

デフォルト設定では、テーブルは一行または複数行の選択をサポートします。ユーザは連続する行や任意の組み合わせの行を選択することができます。ユーザが指定した最後のセルは特殊な表示になります。Metalルック・アンド・フィールでは、セルが縁どられます。このセルは選択の先頭です。いわゆる「フォーカスのあるセル」や「現在のセル」です。

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:

作業 マウス操作 キーボード操作
一行を選択する。 クリックする。 上キーか下キー
連続する選択範囲を広げる。 Shiftキーを押しながらクリックする、または、複数行をドラッグする。 Shiftキーを押しながら上キーか下キー.
選択行の追加/行の選択の切り替え Controlキーを押しながらクリックする。 Controlキーを押しながら上キーか下キーで選択の先頭を移動し、スペースバーで選択範囲の追加を、Controlキーを押しながらスペースバーで行の選択の切り替えを行う。
Operation Mouse Action Keyboard Action
Select single row. Click. Up Arrow or Down Arrow.
Extend contiguous selection. Shift-Click or Drag over rows. 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.

Launchボタンを押してJava Web StartでTableSelectionDemoを実行すると、選択がどのように動作するかを確認できます。もしくは、例の索引からこの例を探し出して、コンパイルしてください。

To see how selections work, click the Launch button to run TableSelectionDemo using Java Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.

この例のプログラムでは、見慣れたテーブルが表示され、ユーザはJTableオプションいくつかのオプションを操作できます。また、テキストパネルには選択イベントのログが出力されます。

This example program presents the familiar table, and allows the user to manipulate certain JTable options. There is also a text pane that logs selection events.

下のスクリーンショットは、ユーザがこのプログラムを実行し、最初の行をクリックし、三番目の行でコントロールキーを押しながらクリックしたものです。最後にクリックされたセルが縁どられていることに注目してください。Metalルックアンドフィールはこのように選択の先頭を強調します。

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での不連続な行の選択

TableSelectionDemo with a non-contiguous row selection.

「選択モード」の下にラジオボタンが並んでいます。「Single Selection」と表示されたものをクリックしてください。すると一度に一行だけ選択できるようになります。「Single Interval 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.

「選択モード」の下のラジオボタンはすべてJTable.setSelectionModeを呼び出しています。このメソッドは引数を一つとりますが、この引数は以下のjavax.swing.ListSelectionModeの定数のいずれかででなければなりません。MULTIPLE_INTERVAL_SELECTION、SINGLE_INTERVAL_SELECTION、そして、SINGLE_SELECTIONです。

All of the radio buttons under "Selection Mode" invoke JTable.setSelectionMode. This 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.

TableSelectionDemoに戻って、「Selection Options」の下の三つのオプションに注目してください。それぞれのチェックボックスはJTableが定義するbooleanの束縛変数の状態を制御します。

Returning to TableSelectionDemo, notice the three option checkboxes under "Selection Options." Each of checkbox controls the state of a boolean bound variable defined by JTable:

  • 「Row Selection」は、setterメソッドsetRowSelectionAllowedとgetterメソッドgetRowSelectionAllowedを持つrowSelectionAllowedを制御します。この束縛プロパティがtrue(columnSelectionAllowedプロパティがfalse)の場合、行ごとに選択できるようになります。
  • 「Column Selection」は、setterメソッドsetColumnSelectionAllowedとgetterメソッドgetColumnSelectionAllowedを持つcolumnSelectionAllowedを制御します。この束縛プロパティがtrue(rowSelectionAllowedプロパティがfalse)の場合、列ごとに選択できるようになります。
  • 「Cell Selection」は、setterメソッドsetCellSelectionEnabledとgetterメソッドgetCellSelectionEnabledを持つcellSelectionEnabledを制御します。この束縛プロパティがtrueの場合、単一のセルまたは矩形のセル範囲を選択できるようになります。
  • "Row Selection" controls rowSelectionAllowed which has setter method setRowSelectionAllowed and getter method getRowSelectionAllowed. When this bound property is true (and the columnSelectionAllowed property is false), the user can select by row.
  • "Column Selection" controls columnSelectionAllowed which has setter method setColumnSelectionAllowed and getter method getColumnSelectionAllowed. When this bound property is true (and the rowSelectionAllowed bound property is false), the user can select by column.
  • "Cell Selection" controls cellSelectionEnabled, which has setter method setCellSelectionEnabled and getter method getCellSelectionEnabled. When this bound property is true, the user can select a single cell or rectangular block of cells.

注意: JTableにおいて選択は、非常に単純に考えられて、行と列の交差部分として管理されいます。完全に独立したセルの選択を扱うようには設計されていません。

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.

三つのチェックボックスをすべてクリアすると(三つの束縛プロパティをすべてfalseにすると)、選択できなくなります。選択の先頭のみが表示されます。

If you clear all three check boxes (setting all three bound properties to false), there is no selection; only the lead selection is shown.

複数区間選択モードでは、「Cell Selection」は使用不能であることに注意したほうがよいかもしれません。デモにおいては、このモードでのセル選択をサポートしていないからです。複数区間選択モードでセルによる選択を指定することは可能ですが、結局、意味のある選択にはなりません。

You may notice that the "Cell Selection" checkbox 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.

三つの選択オプションのどれかを変更すると他のオプションに影響があることにも注意したほうが良いかもしれません。行選択と列選択の両方を許可は、セル選択を許可とまったくおなじになるからです。JTableは、一貫性を保つため、必要に応じて自動的に三つの束縛変数を更新します。

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.

注意: cellSelectionEnabledに値を設定すると、rowSelectionEnabledとcolumnSelectionEnabledにも値が設定される副作用があります。rowSelectionEnabledとcolumnSelectionEnabledに値を設定すると、cellSelectionEnabledにも値を設定する副作用があります。rowSelectionEnabledとcolumnSelectionEnabledにそれぞれ異なる値を設定すると、cellSelectionEnabledがfalseに設定される副作用があります。

NOTE: Setting cellSelectionEnabled to a value has the side effect of also setting both rowSelectionEnabled and columnSelectionEnabled to that value. Setting both rowSelectionEnabled and columnSelectionEnabled to a value has the side effect of also setting cellSelectionEnabled to that value. Setting rowSelectionEnabled and columnSelectionEnabled to different values has the side effect of also setting cellSelectionEnabled to false.

現在の選択を取得するには、行のインデックスの配列を返すJTable.getSelectedRowと列のインデックスの配列を返すJTable.getSelectedColumnsを使います。選択の先頭の座標を取得するには、テーブル自身の選択モデルとテーブルの列のモデルを参照します。以下のコードは行と列を含む文字列をフォーマットしています。

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

ユーザの選択はいくつかのイベントを生成します。これらの情報については、レッスン「Writing Event Listeners」の「How to Write a List Selection Listener」を参照してください。

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 .

テーブルモデルを作成する

すべてのテーブルオブジェクトは、テーブルデータの実際の管理にテーブルモデルオブジェクトを使用します。テーブルモデルオブジェクトはTableModelインタフェースを実装する必要があります。プログラマがテーブルモデルオブジェクトを作成しない場合、JTableは自動的にDefaultTableModelのインスタンスを作成します。この関連を以下に示します。

Every table object uses a table model object to manage the actual table data. A table model object must implement the TableModel interface. If the programmer does not provide a table model object, JTable automatically creates an instance of DefaultTableModel. This relationship is illustrated below.

テーブルとテーブルオブジェクトとモデルオブジェクトの関連

Relation between table, table object, model object

SimpleTableDemoが使用するJTableのコンストラクタは以下のようなコードでテーブルモデルを作成します。

The JTable constructor used by SimpleTableDemo creates its table model with code like this:

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

このコードからわかるように、テーブルモデルの実装はとても単純です。通常、テーブルモデルはAbstractTableModelクラスのサブクラスとして実装します。

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.

モデルでは、配列やベクター、ハッシュマップの中にデータを持つことができますし、データベースのような外部ソースからデータを取得することもできます。実行時にデータを生成することもできます。

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.

このテーブルはSimpleTableDemoのテーブルとは以下のような点で異なります。

This table is different from the SimpleTableDemo table in the following ways:

  • TableDemoの自作のテーブルモデルは単純ですが、データの型を決めることができ、JTableがデータを最適なフォーマットで表示します。一方、SimpleTableDemoで自動的に作成されたテーブルモデルは# of years列に数値(通常右詰めで何らかのフォーマットを持つ)が入ることを知りません。Vegetarian列に、チェックボックスとして表示できる、booleanの値が入ることも知りません。
  • TableDemoで実装されている自作のテーブルモデルは名前の列を編集できません。しかし、その他の列は編集可能です。SimpleTableDemoではすべてのセルが編集可能です。
  • TableDemo's custom table model, even though it is simple, can easily determine the data's type, helping the JTable display the data in the best format. SimpleTableDemo's automatically created table model, on the other hand, does not know that the # of Years column contains numbers (which should generally be right aligned and have a particular format). It also does not know that the Vegetarian column contains boolean values, which can be represented by check boxes.
  • The custom table model implemented in TableDemo does not let you edit the name columns; it does, however, let you edit the other columns. In SimpleTableDemo, all cells are editable.

以下のTableDemo.javaのコードでSimpleTableDemo.javaとの違いを見てください。このテーブルモデルのうち、SimpleTableDemoで自動的に定義されたテーブルモデルと異なるコードを太字にしています。

See below the code taken from TableDemo.java that is different from the SimpleTableDemo.java. Bold font indicates the code that makes this table's model different from the table model defined automatically for 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);
	}
	...
}

データの変更を監視する

テーブルデータの変更通知を受けるリスナーを、テーブルモデルに設定できます。リスナーはTableModelListenerのインスタンスです。以下のコードの例では、SimpleTableDemoにリスナーを追加しました。太字が新たに追加したコードです。

A table model can have a set of listeners that are notified whenever the table data changes. Listeners are instances of TableModelListener. In the following example code, SimpleTableDemo is extended include such a listener. 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...
	}
	...
}

データ変更イベントを発生させる

データ変更イベントを発生させるためには、テーブルモデルにTableModelEventオブジェクトを作成する処理を書く必要があります。複雑な処理が必要ですが、この処理はすでにDefaultTableModelに実装されています。JTableのデフォルトのDefaultTableModelのインスタンスを使うか、DefaultTableModelの独自のサブクラスを作ることができます。

In order to fire data change events the table model must know how to construct a TableModelEvent object. This can be a complex procedure, but is already implemented in DefaultTableModel. You can either allow JTable to use its default instance of DefaultTableModel, or create your own custom subclass of DefaultTableModel.

DefaultTableModelが独自のテーブルモデルクラスの基底クラスに適さなければ、AbstractTableModelのサブクラス化を検討してください。このクラスはTableModelEventオブジェクトの作成用の簡単なフレームワークを実装しています。独自のクラスでは、テーブルデータが外部から変更されるたびにAbstractTableModelのメソッドを呼び出すだけで済みます。以下がAbstractTableModelのメソッドです。

If DefaultTableModel is not a suitable base class for your custom table model class, consider subclassing AbstractTableModel. This class implements a simple framework for constructing TableModelEvent objects. Your custom class simply needs to invoke one the following AbstractTableModel methods each time table data is changed by an external source.

メソッド 変更
fireTableCellUpdated 特定のセルの更新。
fireTableRowsUpdated 特定の列の更新
fireTableDataChanged テーブル全体の更新(データのみ)。
fireTableRowsInserted 新たな行の挿入。
fireTableRowsDeleted 既存の行の削除
fireTableStructureChanged テーブル全体(データと構造の両方)を再描画する
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.

考え方: エディタとレンダラ

次に進む前に、テーブルがセルを描画する方法を理解することが必要です。テーブルの各セルはコンポーネントである思っている人もいるでしょう。しかし、パフォーマンスの理由から、Swingのテーブルは別の方法で実装されています。

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.

実際には、同じ種類のデータを含むセルはすべて、単一のセルレンダラを使って描画されます。レンダラは、テーブルがデータをきちんとフォーマットして各セルに押すための、設定が可能なスタンプと考えることができます。ユーザがセルの編集を開始すると、セルエディタはセルを引き取り、セルの編集を制御します。

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.

例えば、TableDemoの# of Years列のセルにはNumberデータ、具体的にはIntegerオブジェクトが含まれます。デフォルトでは、Numberを含む列のセルレンダラは、単一のJLabelインスタンスを使って数値を右詰めでセルに描画します。ユーザがこれらのセルの編集を始めると、デフォルトのセルエディタは右詰めのJTextFieldを使ってセルの編集を制御します。

For example, each cell in the # of Years column in TableDemo contains Number data -- specifically, an Integer object. 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. If the user begins editing one of the cells, the default cell editor uses a right-aligned JTextField to control the cell editing.

テーブルは列のセルを表示を行うレンダラを決めるためにまずその列にレンダラが設定されているか確認します。もし、設定されていなければ、テーブルはテーブルモデルのgetColumnClassメソッドを呼び出します。このメソッドにより列のセルのデータ型を取得します。次に、テーブルは列のデータ型を、登録されたセルレンダラのデータ型一覧と見比べます。この一覧はテーブルよって初期化されますが、項目を追加したり変更することもできます。今は、テーブルは一覧に以下のデータ型を追加します:

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. 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:

  • Boolean ― チェックボックスで描画します。
  • Number ― 右詰めのラベルで描画します。
  • Double, Float ― Numberと同じですが、NumberFormatインスタンスを使ってオブジェクトを文字列に変換します(現在のロケールにおけるデフォルトの数値フォーマットを使います)。
  • Date ― ラベルで描画されますが、DateFormatインスタンスを使ってオブジェクトを文字列に変換します(日付と時間の短い形式を使います)。
  • ImageIcon, Icon ― 中央揃えのラベルで描画します。
  • Object ― オブジェクトの文字列値をラベルで表示します。
  • Boolean ― rendered with a check box.
  • Number ― rendered by a right-aligned label.
  • Double, Float ― same as Number, but the object-to-text translation is performed by a NumberFormat instance (using the default number format for the current locale).
  • Date ― rendered by a label, with the object-to-text translation performed by a DateFormat instance (using a short style for the date and time).
  • ImageIcon, Icon ― rendered by a centered label.
  • Object ― rendered by a label that displays the object's string value.

セルレンダラも似たようなアルゴリズムで決まります。

Cell editors are chosen using a similar algorithm.

データモデルの作成をテーブルに任せると、すべての列の型にObjectが使用されます。列の型を指定するには、TableDemo.javaでやっているように、テーブルモデルでgetColumnClassメソッドを正しく定義する必要があります。

Remember that if you let a table create its own model, it uses Object as the type of every column. To specify more precise column types, the table model must define the getColumnClass method appropriately, as demonstrated by 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:

状況 イベントの取得方法
セルの編集イベントを検出する セルエディタを使う(つまり、セルエディタにリスナを登録する)。
行/列/セルの選択と非選択を検出する ユーザが選択する説明した選択リスナを使用する。
列ヘッダへのマウスイベントを検出する 適切な種類のマウスリスナをテーブルのJTableHeaderオブジェクトに登録する(TableSorter.javaの例を参照)。
その他のイベントを検出する JTableオブジェクトに適切なリスナを登録する。
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. (See TableSorter.java for an example.)
To detect other events... Register the appropriate listener on the JTable object.

次からのいくつかの節では、レンダラとエディタによる表示と編集のカスタマイズのしかたを説明します。セルレンダラとエディタは列ごとやデータ型ごとに指定できます。

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.

自作のレンダラを使う

この節ではセルレンダラの作り方と設定の仕方を説明します。型ごとのセルレンダラを設定するには、JTableのsetDefaultRendererメソッドを使います。特定の列のセルで使うセルレンダラを設定するには、TableColumnのsetCellRendererメソッドを使います。セルに個別のレンダラを設定することもでき、これにはJTableのサブクラスを作ります。

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. To specify that cells in a particular column should use a renderer, you use the TableColumn method setCellRenderer. You can even specify a cell-specific renderer by creating a JTable subclass.

デフォルトのレンダラであるDefaultTableCellRendererで描画する文字列と画像は、簡単にカスタマイズできます。サブクラスを作り、setValueメソッドを実装するだけです。setValueメソッドの実装で、描画したい文字列や画像を指定してsetTextまたはsetIconを呼び出します。例えば、以下はデフォルトの日付レンダラの実装方法です。

It is easy to customize the text or image rendered by the default renderer, DefaultTableCellRenderer. You just create a subclass and implement the setValue method so that it invokes setText or setIcon with the appropriate string or image. For 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));
    }
}

DefaultTableCellRendererの継承では不十分な場合、別のクラスをスーパクラスとしてレンダラを作ることもできます。最も簡単な方法は、既存のコンポーネントのサブクラスを作り、そのサブクラスにTableCellRendererインタフェースを実装させることです。TableCellRendererを実装するには、メソッドを一つだけ実装する必要があります。getTableCellRendererComponentです。このメソッドは、渡された状態を反映してコンポーネントの描画を行い、そのコンポーネントを返すように実装します。

If extending DefaultTableCellRenderer is insufficient, you can build a renderer using another superclass. The easiest way is to create a subclass of an existing component, making your subclass implement the TableCellRenderer interface. TableCellRenderer requires just one method: getTableCellRendererComponent. Your implementation of this method should set up the rendering component to reflect the passed-in state, and then return the component.

TableDialogEditDemoのスナップショットにおけるFavorite Colorセルのレンダラは、ColorRendererというJLabelのサブクラスです。以下は、ColorRenderer.javaからTableCellRendererの実装方法を抜粋しています。

In the snapshot of TableDialogEditDemo, the renderer used for Favorite Color cells is a subclass of JLabel called ColorRenderer. Here are excerpts from ColorRenderer.java that show how it is implemented.

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は実線で、
                //色はtable.getSelectionBackground()です。
                //selectedBorder is a solid border in the color
                //table.getSelectionBackground().
                setBorder(selectedBorder);
            } else {
                ...
                //unselectedBorderは実線で、
                //色はtable.getBackground()です。
                //unselectedBorder is a solid border in the color
                //table.getBackground().
                setBorder(unselectedBorder);
            }
        }
        
        setToolTipText(...); //続く節で説明します- Discussed in the following section
        return this;
    }
}

以下は、全てのColorデータのデフォルトのレンダラとしてColorRendererインスタンスを登録する、TableDialogEditDemo.javaのコードです。

Here is the code from TableDialogEditDemo.java that registers a ColorRenderer instance as the default renderer for all Color data:

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

セルに個別のレンダラを設定するには、JTableのサブクラスを定義してgetCellRendererメソッドをオーバライドする必要があります。たとえば、以下のコードではテーブルの最初の列の最初のセルに自作のレンダラを使っています。

To specify a cell-specific renderer, you need to define a JTable subclass that overrides the getCellRenderer method. 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 *1 {
            return weirdRenderer;
        }
        // それ以外... - else...
        return super.getCellRenderer(row, column);
    }
};

セルにツールチップを設定する

テーブルセルのツールチップの文字列は、デフォルトではセルレンダラで決めます。しかし、ツールチップの文字列を決めるために、JTableのgetToolTipText(MouseEvent)メソッドをオーバライドするほうが簡単なこともあります。この節では、これらの二つの方法を説明します。

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. This section shows you how to use both techniques.

レンダラを使ってセルにチップを追加するには、まずセルレンダラの取得か作成が必要です。次に、レンダリングコンポーネントは必ずJComponentなので、そのsetToolTipTextメソッドを呼び出します。

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.

セルのツールチップを設定する例はTableRenderDemoにあります。LaunchボタンをクリックすればJava Web Startを使って実行できます。あるいは、例をコンパイルして実行する場合は、例の索引から探してください。

An example of setting tool tips for cells is in TableRenderDemo. Click the Launch button to run it using Java Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.

ソースコードはTableRenderDemo.javaです。以下のコードでは、Sport列のセルにツールチップを追加します。

The source code is in TableRenderDemo.java. It adds tool tips to the cells of the Sport column with the following code:

//sportセルにツールチップを追加する。
//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:

  • レンダラのgetTableCellRendererComponentメソッドを実装してコードを追加する。
  • JTableのgetToolTipText(MouseEvent)メソッドをオーバライドする。
  • Add a bit of code to the renderer's implementation of the getTableCellRendererComponent method.
  • Override the JTable method getToolTipText(MouseEvent).

セルレンダラにコードを追加する例は、TableDialogEditDemoにあります。LaunchボタンをクリックすればJava Web Startを使って実行できます。あるいは、例をコンパイルして実行する場合は、例の索引から探してください。

An example of adding code to a cell renderer is in TableDialogEditDemo. Click the Launch button to run it using Java Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.

TableDialogEditDemoはColorRenderere.javaで色のレンダラを使っています。以下のコードでツールチップの文字列を設定している箇所はボールド体になっています。

TableDialogEditDemo uses a renderer for colors, implemented in ColorRenderer.java, that sets the tool tip text using the boldface code in the following snippet:

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:

ツールチップの文字列を設定するには、JTableのgetToolTipText(MouseEvent)メソッドをオーバライドします。TableToolTipsDemoプログラムでやり方を説明します。LaunchボタンをクリックすればJava Web Startを使って実行できます。あるいは、例をコンパイルして実行する場合は、例の索引から探してください。

You can specify tool tip text by overriding JTable's getToolTipText(MouseEvent) method. The program TableToolTipsDemo shows how. Click the Launch button to run it using Java Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.

Sport列とVegetarian列のセルにツールチップがあります。以下は、実際のツールチップです。

The cells with tool tips are in the Sport and Vegetarian columns. Here is a picture of its tool tip:

以下はTableToolTipsDemo.javaから抜粋したコードです。Sport列とVegetarian列のセルのツールチップを実装しています。

Here is the code from TableToolTipsDemo.java that implements tool tips for cells in the Sport and Vegetarian columns:

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列 - Sport column
            tip = "This person's favorite sport to "
                   + "participate in is: "
                   + getValueAt(rowIndex, colIndex);

        } else if (realColumnIndex == 4) { //Veggie列 - 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の呼び出しは例外的です。もしユーザが列を移動した場合、列のビューのインデックスは列のモデルのインデックスと一致しません。したがって、この呼び出しが必須になります。たとえば、ユーザが(モデルのインデックスが4である)Vegetarian列を移動して、最初の列に表示するとします。つまりビューのインデックスを0にします。prepareRendererによりビューのインデックスが取得できます。意図した列を選択するには、このビューのインデックスをモデルのインデックスに変換することが必要です。

The code is fairly straightforward, except perhaps for the call to 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. 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.

列ヘッダにツールチップを設定する

列ヘッダにツールチップを追加するには、テーブルのJTableHeaderにチップテキストを設定します。たいていの場合、列ごとにツールチップテキストを設定する必要があります。テキストを変更するには、テーブルヘッダのgetToolTipTextメソッドをオーバライドするか、または、TableColumn.setHeaderRendererを呼び出してヘッダーに独自のレンダラを設定します。

You can add a tool tip to a column header by setting the tool tip text for the table's JTableHeader. Often, different column headers require different tool tip text. You can change the text by overriding the table header's getToolTipText method. Alternately, you can invoke TableColumn.setHeaderRenderer to provide a custom renderer for the header.

TableToolTipsDemo.javaには、すべての列のヘッダで同じツールチップを使う例と、列ごとに異なるツールチップを実装する例があります。TableToolTipsDemoを実行するには(Launchボタンをクリックして)Java Web Startを使います。もしくは、例の索引からこの例を見つけて、自分でコンパイルして実行してください。

An example of using the same tool tip text for all column headers is in 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 6). Or, to compile and run the example yourself, consult the example index.

1列目と2列目以外の列ヘッダにマウスを載せると、ツールチップが表示されます。名前の列にツールチップを表示していないのは、見ればわかると思ったからです。以下は、列ヘッダのツールチップの例です。

You will see the tool tips when you mouse over any column header except for the first two. No tool tips were suppled for the name columns since they seemed self-explanatory. Here is a picture of one of the column header tool tips:


ツールチップは以下のコードで実装しています。JTableHeaderのサブクラスを作り、マウスの載っている列に対応するテキストを返すように、getTookTipText(MouseEvent)をオーバライドするのが基本です。テーブルに独自のテーブルヘッダを関連付けるには、JTableHeaderサブクラスのインスタンスを返すように、JTableのcreateDefaultTableHeaderメソッドをオーバライドします。

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

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

今はまだここまで。。。2009/05/24

ソートとフィルタ

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:

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

This action defines a row sorter that is an instance of 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 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 sorter
= new TableRowSorter(table.getModel());
table.setRowSorter(sorter);

TableRowSorter uses java.util.Comparator objects to sort its rows. 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. For example, the following code creates a Comparator that sorts a set of strings by the last word in each string:

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

This example is fairly simplistic; more typically, a Comparator implementation is a subclass of 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.

To determine which Comparator to use for a column, TableRowSorter attempts to apply each of the following rules in turn. Rules are followed in the order listed below; the first rule that provides the sorter with a Comparator is used, and the remainining rules ignored.

1. If a comparator has been specified by invoking setComparator, use that 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.
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.
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.
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.

For more sophisticated kinds of sorting, subclass TableRowSorter or its parent class javax.swing.DefaultRowSorter.

To specify the sort order and sort precedence for columns, invoke setSortKeys. Here 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 sortKeys
= new ArrayList();
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. RowFilter implements several factory methods that create common kinds of filters. For example, regexFilter returns a RowFilter that filters based on a regular expression.

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(model);
table = new JTable(model);
table.setRowSorter(sorter);

Then you filter based on the current value of a text field:

private void newFilter() {
RowFilter 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. When the user enters complicated regular expressions, the try...catch prevents the syntax exception from interfering with input.

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.

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. 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. Notice that row 3 in the model is still the same as row 3 in the view:

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

エディタとしてコンボボックスを使う

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. You can run TableRenderDemo (click the Launch button) using Java"! Web Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.

Launches the TableRenderDemo example

その他のエディタを使う

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. Fortunately, the DefaultCellEditor class implements this interface and provides constructors to let you specify an editing component that is a JTextField, JCheckBox, or JComboBox. Usually 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.

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. You need to create a class that implements the TableCellEditor interface. The AbstractCellEditor class is a good superclass to use. It implements TableCellEditor's superinterface, CellEditor, saving you the trouble of implementing the event firing code necessary for cell editors.

Your cell editor class needs to define at least two methods -- getCellEditorValue and getTableCellEditorComponent. The getCellEditorValue method, required by CellEditor, returns the cell's current value. The getTableCellEditorComponent method, required by TableCellEditor, should configure and return the component that you want to use as the editor.

Here is a picture of a table with a dialog that serves, indirectly, as a cell editor. When the user

*1:row == 0) && (column == 0