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) の記述を追加することで、期待通りにコンポーネントが描画されるようになりました。( ´ー`)ヨカッター