用于自定义组件的JTree自定义单元编辑器不响应第一次单击

时间:2014-03-03 15:22:59

标签: java swing jtree treetablecelleditor

我有一个Java应用程序,它在树中显示用户文件系统。我显示带有复选框,图标和文本的文件夹。为此,我必须为树单元格创建一个自定义组件。该组件是JLabel的子类,包含JCheckBox和JLabel。我编写了自己的渲染器和编辑器来显示和编辑组件。当您第一次进入编辑模式时单击复选框,但不会发生关闭编辑的代码。随后对复选框的所有点击都可正常工作。我做了很多搜索,无法解决这个问题。

这是我的渲染器和编辑器的代码。树已设置为允许编辑,并且设置了渲染器和编辑器。

class MyRenderer implements TreeCellRenderer
{
    private CheckBoxPanel m_panel;

    public MyRenderer()
    {
        m_panel = new CheckBoxPanel();
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree,
                                                  Object value,
                                                  boolean selected,
                                                  boolean expanded,
                                                  boolean leaf,
                                                  int row,
                                                  boolean hasFocus) {

        DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
        MyState state = (MyState)node.getUserObject();

        m_panel.setState(state);

        return m_panel;
    }
}

class MyEditor extends AbstractCellEditor implements TreeCellEditor
{
    private CheckBoxPanel m_panel;
    private JCheckBox m_checkbox;
    private MyState m_state;

    public MyEditor()
    {
        m_panel = new CheckBoxPanel();
        // m_panel.setColor(Color.red);
    }

    @Override
    public Component getTreeCellEditorComponent(JTree tree,
                                                Object value,
                                                boolean isSelected,
                                                boolean expanded,
                                                boolean leaf,
                                                int row) {
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value;
        m_state = (MyState)treeNode.getUserObject();

        m_panel.setState(m_state);

        m_checkbox = m_panel.getCheckBox();

        m_checkbox.addItemListener(new ItemListener()
        {
            @Override
            public void itemStateChanged(ItemEvent e)
            {
                fireEditingStopped();
                m_checkbox.removeItemListener(this);
            }
        });

        return m_panel;
    }

    @Override
    public Object getCellEditorValue()
    {
        m_state.setSelected(m_checkbox.isSelected());
        return m_state;
    }

    @Override
    public boolean isCellEditable(EventObject anEvent)
    {
        if (anEvent instanceof MouseEvent)
        {
            return true;
        }

        return false;
    }        
}

class CheckBoxPanel extends JPanel
{
    private JCheckBox m_checkBox;
    private JLabel m_label;

    public CheckBoxPanel()
    {
        m_checkBox = new JCheckBox();
        m_checkBox.setBackground(UIManager.getColor("Tree.background"));
        m_checkBox.setBorder(null);
        m_checkBox.setFocusable(true);

        m_label = new JLabel();
        m_label.setFont(UIManager.getFont("Tree.font"));
        m_label.setFocusable(false);

        setOpaque(false);

        setLayout(new BorderLayout());
        add(m_checkBox, BorderLayout.WEST);
        add(m_label, BorderLayout.CENTER);
    }

    public JCheckBox getCheckBox()
    {
        return m_checkBox;
    }

    public void setState(MyState _state)
    {
        m_label.setText(_state.getText());

        m_checkBox.setSelected(_state.isSelected());
    }

    public void setColor(Color _color)
    {
        m_label.setForeground(_color);
    }
}

class MyState
{
    private String m_text;
    private boolean m_selected;

    public MyState(String _text, boolean _selected)
    {
        m_text = _text;
        m_selected = _selected;
    }

    public String getText()
    {
        return m_text;
    }

    public void setText(String _text)
    {
        m_text = _text;
    }

    public boolean isSelected()
    {
        return m_selected;
    }

    public void setSelected(boolean _selected)
    {
        m_selected = _selected;
    }
}

1 个答案:

答案 0 :(得分:1)

  • 我的环境:Windows 7 x64
    • Java 1.6.0_41:工作正常
    • Java 1.7.0_51:不能为我点击第一次
    • Java 1.8.0:工作正常

以下是一种可能的实现,以避免Java 1.7.0_51上的此问题(覆盖TreeCellEditor#isCellEditable(...)):

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;

public final class TreeCellEditorFirstClickTest {
  public JComponent makeUI() {
    JTree tree = new JTree();
    TreeModel model = tree.getModel();
    DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
    Enumeration e = root.breadthFirstEnumeration();
    while (e.hasMoreElements()) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
      Object o = node.getUserObject();
      if (o instanceof String) {
        node.setUserObject(new CheckBoxNode((String) o, false));
      }
    }
    tree.setEditable(true);
    tree.setCellRenderer(new CheckBoxNodeRenderer());
    tree.setCellEditor(new CheckBoxNodeEditor());
    tree.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
    tree.expandRow(0);
    return new JScrollPane(tree);
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new TreeCellEditorFirstClickTest().makeUI());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

class CheckBoxNode {
  public final String text;
  public final boolean selected;
  public CheckBoxNode(String text, boolean selected) {
    this.text = text;
    this.selected = selected;
  }
  @Override public String toString() {
    return text;
  }
}

class CheckBoxNodeRenderer implements TreeCellRenderer {
  private DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
  private final JCheckBox check = new JCheckBox();
  private final JPanel p = new JPanel(new BorderLayout());
  public CheckBoxNodeRenderer() {
    p.setFocusable(false);
    p.setRequestFocusEnabled(false);
    p.setOpaque(false);
    p.add(check, BorderLayout.WEST);
    check.setOpaque(false);
  }
  @Override public Component getTreeCellRendererComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row, boolean hasFocus) {
    JLabel l = (JLabel) renderer.getTreeCellRendererComponent(
        tree, value, selected, expanded, leaf, row, hasFocus);
    if (value instanceof DefaultMutableTreeNode) {
      check.setEnabled(tree.isEnabled());
      check.setFont(tree.getFont());
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        l.setText(node.text);
        check.setSelected(node.selected);
      }
      p.add(l);
      return p;
    }
    return l;
  }
}

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
  private DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
  private final JCheckBox check = new JCheckBox();
  private final JPanel p = new JPanel(new BorderLayout());
  private String str = null;
  public CheckBoxNodeEditor() {
    super();
    check.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        stopCellEditing();
      }
    });
    p.setFocusable(false);
    p.setRequestFocusEnabled(false);
    p.setOpaque(false);
    p.add(check, BorderLayout.WEST);
    check.setOpaque(false);
  }
  @Override public Component getTreeCellEditorComponent(
      JTree tree, Object value, boolean isSelected, boolean expanded,
      boolean leaf, int row) {
    JLabel l = (JLabel) renderer.getTreeCellRendererComponent(
        tree, value, true, expanded, leaf, row, true);
    if (value instanceof DefaultMutableTreeNode) {
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        l.setText(node.text);
        check.setSelected(node.selected);
        str = node.text;
      }
      p.add(l);
      return p;
    }
    return l;
  }
  @Override public Object getCellEditorValue() {
    return new CheckBoxNode(str, check.isSelected());
  }
  ////1.6.0_41`: work OK
  ////1.7.0_51`: not work to first click
  ////1.8.0`: work OK
  //@Override public boolean isCellEditable(EventObject e) {
  //  if (e instanceof MouseEvent && e.getSource() instanceof JTree) {
  //    return true;
  //  }
  //  return false;
  //}
  @Override public boolean isCellEditable(EventObject e) {
    if (e instanceof MouseEvent && e.getSource() instanceof JTree) {
      MouseEvent me = (MouseEvent) e;
      JTree tree = (JTree) e.getSource();
      TreePath path = tree.getPathForLocation(me.getX(), me.getY());
      Rectangle r = tree.getPathBounds(path);
      if (r == null) {
        return false;
      }
      Dimension d = check.getPreferredSize();
      r.setSize(new Dimension(d.width, r.height));
      if (r.contains(me.getX(), me.getY())) {
        if (str == null && System.getProperty("java.version").startsWith("1.7.0")) {
          System.out.println("XXX: Java 7, only on first run\n" + p.getBounds());
          check.setBounds(new Rectangle(0, 0, d.width, r.height));
        }
        return true;
      }
    }
    return false;
  }
}