文字列以外のデータをテーブルに表示する

広告

今までは文字列だけを使っていましたが、コンストラクタの引数を見て分かる通り、Object型のものであればテーブルのデータとして追加することができます。ここでは試しに画像を使ってみましょう。

今回は画像としてImageIconクラスを使います。ImageIconクラスの使い方としては下記の様になります。

ImageIcon icon = new ImageIcon("画像ファイル名");

国旗のアイコンは『世界の国旗アイコン』を使わせて頂きました。

まずはJTableのコンストラクタに渡すデータに、画像も含めて試してみます。

import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.table.DefaultTableModel;

public class SwingTest extends JFrame{

  private Object[][] tabledata = {
    {new ImageIcon("./img/japan.gif"), "日本", "3勝", "0敗", "1分"},
    {new ImageIcon("./img/croatia.gif"), "クロアチア", "3勝", "1敗", "0分"},
    {new ImageIcon("./img/brazil.gif"), "ブラジル", "1勝", "2敗", "1分"},
    {new ImageIcon("./img/australia.gif"), "オーストラリア", "2勝", "2敗", "0分"}};

  private String[] columnNames = {"FLAG", "COUNTRY", "WIN", "LOST", "EVEN"};

  public static void main(String[] args){
    SwingTest test = new SwingTest("SwingTest");

    test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    test.setVisible(true);
  }

  SwingTest(String title){
    setTitle(title);
    setBounds( 10, 10, 300, 200);

    JTable table = new JTable(tabledata, columnNames);

    JScrollPane sp = new JScrollPane(table);
    sp.setPreferredSize(new Dimension(250, 90));

    JPanel p = new JPanel();
    p.add(sp);

    getContentPane().add(p, BorderLayout.CENTER);
  }
}

実行結果は下記の通りです。

テーブルのデータに画像を使う

画像として表示されず、ファイルの位置を表すような文字列として表示されてしまいました。

この理由なのですが、JTableは各セルを表示する際に、TableModelで定義されている"getColumnClass"メソッドを使って、その列のクラス名を調べるようにしているようです。ただ、DefaultTableModelクラス(及びAbstractTableModel)ではこのメソッドは下記のように実装されています。

columnIndex にかかわらず Object.class を返します。 

パラメータ:
  columnIndex - 照会される列 @return Object.class 
戻り値:
  モデルの上位クラスに共通のオブジェクト値

本来は引数で指定した列に定義されているクラス名を返せばいいのですが、引数がなんであれ"Object"クラスを返すようになっています。"Object"クラスの場合は、そのオブジェクトに対して"toString"メソッドを実行した結果が表示されるようなので、文字列であれば問題ありませんが、ImageIconの場合は画像ファイルの位置が文字列として表示されてしまうようです。

これを修正するにはDefaultTableModelクラスを継承し、"getColumnClass"メソッドをオーバーライドしたクラスを用意して利用するようにします。

class MyTableModel extends DefaultTableModel{
  MyTableModel(String[] columnNames, int rowNum){
    super(columnNames, rowNum);
  }

  public Class getColumnClass(int col){
    /* この部分で正しいクラスを返すようにする */
  }
}

まず、指定の列のどこかの値(実際には同じ列に含まれるデータは全て同じデータ型ですので、0行目のデータ)を取得します。その為には、DefaultTableModelクラスで用意されている"getValueAt"メソッドを使います。

row および column にあるセルの属性値を返します。

パラメータ:
  row - 値が照会される行
  column - 値が照会される列 
戻り値:
  指定されたセルの Object 値 
例外: 
  ArrayIndexOutOfBoundsException - 指定された行または列が無効だった場合

getColumnClassメソッドの引数で列のインデックスが渡されてきますので、その値を使ってその列に含まれる0行目の値を取り出します。

class MyTableModel extends DefaultTableModel{
  MyTableModel(String[] columnNames, int rowNum){
    super(columnNames, rowNum);
  }

  public Class getColumnClass(int col){
    Object obj = getValueAt(0, col);
  }
}

次にこのオブジェクトのクラス名を調べるには、Objectクラスの"getClass"メソッドを使います。

オブジェクトの実行時クラスを返します。この Class オブジェクトは、表された
クラスの static synchronized メソッドによってロックされるオブジェクトです。

戻り値:
  オブジェクトの実行時クラスを表す java.lang.Class オブジェクト。結果
    は、Class<? extends X> 型になる。ここで、X は getClass が呼び出され
    る static 型のイレイジャ

実際にはまとめて下記のように書かれている場合が多いようです。

class MyTableModel extends DefaultTableModel{
  MyTableModel(String[] columnNames, int rowNum){
    super(columnNames, rowNum);
  }

  public Class getColumnClass(int col){
    return getValueAt(0, col).getClass();
  }
}

このように新しく作成したDefaultTableModelクラスのサブクラスを使うことで、テーブルのデータが正しく表示されるようになります。

では以上を踏まえてサンプルコードを書き換えてみます。

import javax.swing.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.table.DefaultTableModel;

public class SwingTest extends JFrame{

  private Object[][] tabledata = {
    {new ImageIcon("./img/japan.gif"), "日本", "3勝", "0敗", "1分"},
    {new ImageIcon("./img/croatia.gif"), "クロアチア", "3勝", "1敗", "0分"},
    {new ImageIcon("./img/brazil.gif"), "ブラジル", "1勝", "2敗", "1分"},
     {new ImageIcon("./img/australia.gif"), "オーストラリア", "2勝", "2敗", "0分"}};

  private String[] columnNames = {"FLAG", "COUNTRY", "WIN", "LOST", "EVEN"};

  class MyTableModel extends DefaultTableModel{
    MyTableModel(String[] columnNames, int rowNum){
      super(columnNames, rowNum);
    }

    public Class getColumnClass(int col){
      return getValueAt(0, col).getClass();
    }
  }

  public static void main(String[] args){
    SwingTest test = new SwingTest("SwingTest");

    test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    test.setVisible(true);
  }

  SwingTest(String title){
    setTitle(title);
    setBounds( 10, 10, 300, 200);

    DefaultTableModel tableModel
     = new MyTableModel(columnNames, 0);

    JTable table = new JTable(tableModel);
    table.setRowHeight(50);

    for(int i = 0 ; i < 4 ; i++){
      tableModel.addRow(tabledata[i]);
    }

    JScrollPane sp = new JScrollPane(table);
    sp.setPreferredSize(new Dimension(250, 130));

    JPanel p = new JPanel();
    p.add(sp);

    getContentPane().add(p, BorderLayout.CENTER);
  }
}

実行結果は下記の通りです。

DefaultTableModelのサブクラスを使う

今度は無事表示されました。

( Written by Tatsuo Ikura )

プロフィール画像

著者 / TATSUO IKURA

これから IT 関連の知識を学ばれる方を対象に、色々な言語でのプログラミング方法や関連する技術、開発環境構築などに関する解説サイトを運営しています。