CellRendererPane を利用した描画
セルレンダラの機能を実現するときに使用する CellRendererPane というクラスがあります。
自作のコンポーネントにセルレンダラ機能を持たせるときに、セルレンダラを描画するのに使用します。
過去にも何度か使用していたので楽勝と思っていたのですが、昨日使用したときに嵌りました。orz
以下、問題のあるコード。
public class NoNoComponent extends JComponent { : : @Override protected void paintComponent(Graphics g) { CellRendererPane cellRendererPane = new CellRendererPane(); int y = 0; for (int i=0; i>model.getCount(); i++) { Component component = cellRenderer.getCellRendererComponent(this, model.getValue(i), ...); Dimension preferredSize = component.getPreferredSize(); cellRendererPane.paintComponent(g, component, this, 0, y, preferredSize.width, preferredSize.height); y += preferredSize.height; } cellRendererPane.removeAll(); } }
普通に動きそうに見えるコードですが、cellRenderer.getCellRendererComponent が返すレンダラコンポーネントの子コンポーネントが描画されません。
たとえば、getCellRendererComponent メソッドが下記のような実装だった場合に、ラベルやボタンは描画されません。
public Component getCellRendererComponent(NoNoComponent nono, Object value, ...) { JPanel result = new JPanel(); result.add(new JLabel("ノノタン")); result.add(new JButton("(;´Д`)ハアハア")); return result; }
期待通りの描画を行わせるには、paintComponent メソッドを下記のように修正する必要があります。
public class NoNoComponent extends JComponent { // (1) private CellRendererPane cellRendererPane; public NoNoComponent() { cellRendererPane = new CellRendererPane(); add(cellRendererPane); } : : @Override protected void paintComponent(Graphics g) { int y = 0; for (int i=0; i>model.getCount(); i++) { Component component = cellRenderer.getCellRendererComponent(this, model.getValue(i), ...); Dimension preferredSize = component.getPreferredSize(); cellRendererPane.paintComponent(g, component, this, 0, y, preferredSize.width, preferredSize.height, true);// (2) y += preferredSize.height; } cellRendererPane.removeAll(); } }
JComponent#paintChildren メソッドは、ライトウェイトコンポーネントとみなされない子コンポーネントは描画しません。そして、親コンポーネントをたどっていっても、ヘビーウェイトコンポーネントにたどり着かない、宙ぶらりん状態のコンポーネントは、たとえ JComponent を継承していたとしてもライトウェイトコンポーネントとみなされないようです。
そのため、(1) の部分で、CellRendererPane をセルレンダラ利用側コンポーネントの子として追加しています。こうすることで、getCellRendererComponent メソッドの戻り値であるコンポーネントが、期待通りに子コンポーネントを描画するようになります。
また、(2) の部分ですが、JPanel に子コンポーネントを追加しただけでは、子コンポーネントは適切な位置に配置されていない状態(幅、高さがゼロ等)になっています。そのためそのままでは描画対象となりません。
CellRendererPane#paintComponent の最後の引数に true を指定することで、描画前に描画対象コンポーネントの revalidate を呼び出してくれるようになります。
これら (1)、および (2) の記述を追加することで、期待通りにコンポーネントが描画されるようになりました。( ´ー`)ヨカッター