概要

JTreeのセルエディタとしてクリック可能なJButtonを複数したJPanelを設定します。

サンプルコード

class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor {
  private final ButtonPanel panel = new ButtonPanel();
  protected ButtonCellEditor() {
    super();
    panel.b1.addActionListener(e -> {
      System.out.println("b1: " + panel.renderer.getText());
      stopCellEditing();
    });
    panel.b2.addActionListener(e -> {
      System.out.println("b2: " + panel.renderer.getText());
      stopCellEditing();
    });
    panel.b3.addActionListener(e -> {
      System.out.println("b3: " + panel.renderer.getText());
      stopCellEditing();
    });
  }

  @Override public Component getTreeCellEditorComponent(
      JTree tree, Object value, boolean isSelected, boolean expanded,
      boolean leaf, int row) {
    Component c = panel.renderer.getTreeCellRendererComponent(
        tree, value, true, expanded, leaf, row, true);
    return panel.remakePanel(c);
  }

  @Override public Object getCellEditorValue() {
    return panel.renderer.getText();
  }

  @Override public boolean isCellEditable(EventObject e) {
    Object source = e.getSource();
    if (!(source instanceof JTree) || !(e instanceof MouseEvent)) {
      return false;
    }
    JTree tree = (JTree) source;
    Point p = ((MouseEvent) e).getPoint();
    TreePath path = tree.getPathForLocation(p.x, p.y);
    if (Objects.isNull(path)) {
      return false;
    }
    Rectangle r = tree.getPathBounds(path);
    if (Objects.isNull(r)) {
      return false;
    }
    // r.width = panel.getButtonAreaWidth();
    // return r.contains(p);
    if (r.contains(p)) {
      TreeNode node = (TreeNode) path.getLastPathComponent();
      int row = tree.getRowForLocation(p.x, p.y);
      Component c = tree.getCellRenderer().getTreeCellRendererComponent(
          tree, " ", true, true, node.isLeaf(), row, true);
      c.setBounds(r);
      c.setLocation(0, 0);
      // tree.doLayout();
      tree.revalidate();
      p.translate(-r.x, -r.y);
      Component o = SwingUtilities.getDeepestComponentAt(c, p.x, p.y);
      if (o instanceof JButton) {
        return true;
      }
    }
    return false;
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、3つのJButtonJLabelを継承するDefaultTreeCellRendererFlowLayoutで配置するJPanel2つ作成し、それぞれをJTreeのセルエディタとセルレンダラーとして設定しています。

  • セルレンダラーのJButtonは表示にのみ使用するためActionListenerなどは設定しない
  • セルエディタのJButtonにはJTreeのノードが編集可能になって自身がクリックされたときに実行するActionListenerを設定
    • このActionListenerが実行されたあとAbstractCellEditor#stopCellEditing()を実行してJTreeのノード編集を終了する
    • JTreeのノード編集を終了しないと編集中のノードの各ボタンにフォーカスが描画されたり、別ノードのボタンを2回クリックしないとそのボタンのイベントが実行できない
  • TreeCellEditor#isCellEditable(...)をオーバーライドしてマウスで各JButtonの表示領域がクリックされた場合のみ編集可能に設定
    • JButtonの表示領域がクリックされたかどうかの判断は編集開始前なのでセルエディタではなく、セルレンダラー内をSwingUtilities.getDeepestComponentAt(...)で検索してJButtonが返されるかを調査している
  • 編集中のノードのJButtonのみマウスでクリック可能

参考リンク

コメント