レンダラーとコンポーネントの関係
これまで、いくつかの「複雑なコンポーネント」について取り上げてきました。JList、JTree、そしてJTable。これらのものでは、コンポーネント本体と「表示するデータ」が分離して管理されていました。それぞれのデータ類は「モデル」と呼ばれるクラスとして定義され、コンポーネントに組み込まれていましたね。このように、コンポーネントそのものとデータを切り離すことで、より柔軟に扱えるようにしていたのですね。
この「コンポーネントから切り離す」という発想は、実は表示データだけのものではありません。その他にも、コンポーネントから独立して扱えるようにしてあるものがあるのです。それが今回取り上げる「項目の表示コンポーネント」です。
JListやJTree、JTableといったものは、そのコンポーネント自体にはさして重要な働きはありません。そこに表示されるデータがあって、初めて意味を成すものなのですね。これらのコンポーネントは、コンポーネントそのものの表示より、その中に表示されるデータが重要なのです。
このデータは、コンポーネントの種類によって表示のされかたが異なってきます。JListなら一覧リストとして表示しますし、JTreeは階層構造で表示されます。またJTableは一覧表のように表示されますね。が、いずれも「1つ1つのデータの表示を、それぞれのコンポーネントにあわせてレイアウトされる」という形になっているのは同じです。
この「それぞれのデータの表示」は、デフォルトでは単に白地の背景に黒いテキストで表示されるだけですね。が、もっと凝った表示をしたいと思うこともあるでしょう。こうしたとき、この「データの表示」部分だけが、独立したクラスとして用意されていればずいぶんと便利になると思いませんか?
そのような考え方から、Swingに用意されたのが「レンダラー」と呼ばれるものです。レンダラーは、「データの表示を管理するクラス」です。JList、JTree、JTableといったコンポーネントには、あらかじめ「それぞれのデータをどのような形で表示するか」を処理するレンダラークラスが用意されています。そして、そのレンダラーを使って、それぞれのコンポーネント内に項目を表示するようになっているのです。
このレンダラークラスを独自に定義し、それをコンポーネントに設定すれば、データの表示方法をカスタマイズすることができます。これは、なかなか面白そうですね。では、実際にやってみましょう。
レンダラーは、コンポーネントごとに独自のものが用意されています。まずは、JList用のレンダラーを定義し、表示を代えてみることにしましょう。
package jp.allabout;
import java.awt.*;
import javax.swing.*;
public class SampleApp extends JFrame {
JList list;
public SampleApp(){
this.setSize(new Dimension(300,200));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] items = {"Windows","Linux","Mac OS X","TRON"};
list = new JList(items);
list.setCellRenderer(new MyListCellRenderer());
this.add(list,BorderLayout.CENTER);
}
public static void main(String[] args) {
new SampleApp().setVisible(true);
}
}
class MyListCellRenderer implements ListCellRenderer {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(
JList list, Object object,int index,
boolean isSelected, boolean hasFocus) {
JLabel label = new JLabel((String)object);
label.setOpaque(true);
if (isSelected){
label.setBackground(new Color(255,220,220));
} else {
label.setBackground(new Color(220,220,255));
}
label.setFont(new Font("Serif",Font.BOLD,16));
label.setForeground(new Color(0,0,100));
return label;
}
}