レンダラー処理の流れ
このSampleAppでは、JListを1つ作成し、JFrameに組み込んで表示させています。このJListに組み込んでいるレンダラーがColorRendererクラスです。また色の値にColorを使用すると表示テキスト(toStringされる値)が少々長くなるので、ColorをextendsしたMyColorを用意し、これを使って値を用意することにしました。
getListCellRendererComponentメソッドで行っていることを見てみましょう。ここでは、new JLabelでJLabelのインスタンスを作成した後、この表示を必要に応じて変更しreturnする、という処理を行っています。引数で渡されたObjectをColorにキャストし、そのインスタンスを元に表示色を設定しています。
Color c = (Color)object; label.setText(c.toString()); label.setOpaque(true); label.setFont(new Font("Serif",Font.BOLD,16)); label.setBackground(c);
setOpaqueでJLabelを不透明にし、setBackgroundすれば指定の色でコンポーネントが表示されます。MyColorではなくColorにキャストしているのは、通常のColorでも問題なく表示できるようにしておくためです。その後、引数のisSelectedを使い、セルが選択されているかどうかによってsetForegroundの値を変更しておきます。
if (isSelected){ label.setForeground(c); } else { label.setForeground(n > 384 ? Color.BLACK : Color.WHITE); }
こうすることで、選択されたときとされていないときの表示を変更するようにしてあるわけです。この他、hasFocusを使って、このセルにフォーカスがあるかどうかによって表示を変更することもできます(ここでは省略してあります)。
このColorListRendererは、次のようにしてJListに設定されています。たったこれだけで、JListのセルの表示をがらりと変えてしまうことができるのです。
list.setCellRenderer(new ColorRenderer());
もっと複雑なセル描画をさせたい
ここではJLabelを使って表示をさせましたが、もっと複雑な描画を行わせたいような場合にはどうすればよいのでしょう。先の例では、JLabelを使ってセルの表示を行いました。しかし、レンダラーで作成する表示はComponentであればいいのですから、例えばJPanelなどを使い、コンポーネント内で細かな描画を行わせるようにすれば、より柔軟な表示を行えるようになるはずです。
では、先ほどのColorRendererを修正して、もう少し細かな表示を行うようにしてみましょう。今度はJPanelを継承してクラスを定義してみます。
class ColorRenderer extends JPanel implements ListCellRenderer { private int index; private boolean isSelected,hasFocus; private MyColor object; @Override public Component getListCellRendererComponent(JList list, Object object, int index, boolean isSelected, boolean hasFocus) { this.index = index; this.isSelected = isSelected; this.hasFocus = hasFocus; try { this.object = (MyColor)object; } catch (ClassCastException e) { this.object = null; } this.setPreferredSize(new Dimension(100,20)); return this; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (isSelected){ g.setColor(object.darker()); } else { g.setColor(object); } g.fillRect(0,0,this.getWidth(),this.getHeight()); if (hasFocus){ g.setColor(object.brighter()); g.drawRect(0,0,this.getWidth() - 1,this.getHeight() - 1); g.drawRect(2,2,this.getWidth() - 5,this.getHeight() - 5); } if (isSelected){ g.setColor(object.brighter()); g.fillOval(4,4,this.getHeight() - 9,this.getHeight() - 9); g.fillOval(this.getWidth() - (this.getHeight() - 4),4, this.getHeight() - 9,this.getHeight() - 9); g.setFont(new Font("Serif",Font.PLAIN,13)); g.drawString(object.toString(),20,this.getHeight() - 7); } } }

ここでは、選択されたセルには、両端に●マーク、内部にRGBの値が表示されるようになります。またフォーカスがあるセルには二重の輪郭線が表示されます。「複雑な表示」という割にはシンプルですが、まぁ描画を使ったサンプルということで。
ここでは、getListCellRendererComponentで新たにコンポーネントなどは作成しません。単に、thisを返しているだけです。今回は、このクラス自身がJPanelですから、新たにコンポーネントを用意して返す必要はないのです。そして、引数に渡された値をそれぞれprivateフィールドに保存しておきます。
実際の描画は、paintComponentで行っています。AWTで使っていたおなじみのpaintメソッドも使えますが、Swingですからこちらを利用するのが良いでしょう(描画の仕組みについては本記事とは直接関係ないので割愛します)。ここで、isSelectedとhasFocusの値ごとに描画する図形を変えています。コンポーネントをextendsする形でレンダラーを定義すれば、こうした直接描画による表示の操作が可能になるのです。
なお、ここでは描画にColorのbrighter/darkerメソッドを利用していますが、Colorの値によってはこれらはうまく機能しないことがあります(例えば、Color.WHITEをbrighterでさらに明るくしようとすると失敗します)。ここではサンプルということで特にそのあたりの処理はしていませんので注意してください。
データ管理をするモデルについて
JListでは、多くの値を使ってセルが作成されていきます。この値は、どこでどうやって管理されているのでしょうか。
データの管理を行っているのが「モデル」と呼ばれるクラスです。これもコンポーネントの種類に応じてさまざまなものが用意されています。モデルは、データの保管だけでなく、その構造の管理なども行います。JListなどは単に一覧で表示するだけですが、コンポーネントによってはデータが構造を持っている場合もあります。こうした管理もモデルの仕事なのです。
JListの場合、モデルはjavax.swing.ListModelインターフェイスを実装したクラスとして定義されます。標準でこれを実装済みのDefaultListModelクラスが用意されており、モデルの作成にはこれを利用するのが最も簡単でしょう。
DefaultListModelは、内部的にVectorを使ってオブジェクトを管理しており、それらを扱うためのメソッドが用意されています。それらをオーバーライドすることでモデルの機能をカスタマイズすることができます。
JListを実際に使ったことがあるなら、標準で表示するデータを追加・削除する機能がないことに気づいたはずです。一応、newする際に配列を渡すことで表示するデータを設定できますが、「これ、どうやってデータを操作するんだろう?」疑問に思った人は多いことでしょう。これも、DefaultListModelを利用することで解決します。すなわち、モデルをJListに設定し、そのモデルに対してデータを追加・削除していけばいいのです。
では、先ほどのサンプルで、DefaultListModelを使った形にJListを修正してみましょう。JListを作成しているcreateListメソッドを、次のように修正してみてください。
public JList createList(){ MyColor[] array = { new MyColor(255,255,255), new MyColor(255,0,0), new MyColor(0,255,0), new MyColor(0,0,255), new MyColor(255,255,0), new MyColor(0,255,255), new MyColor(255,0,255), new MyColor(0,0,0)}; JList list = new JList(); list.setCellRenderer(new ColorRenderer()); // DefaultListModelの作成 DefaultListModel model = new DefaultListModel(); // モデルの設定 list.setModel(model); for(MyColor c:array) model.addElement(c); return list; }
ここでは、DefaultListModelを作成し、setModelでモデルに設定しています。そして、このモデルに対してaddElementでMyColorを追加しています。このように、モデルを利用することでJListの表示リストを自由に扱えるようになります。
