TreeCellEditor: необходимо выбрать ячейку для редактирования, даже если ShouldSelectCell возвращает false - PullRequest
1 голос
/ 14 ноября 2011

Мне нужно использовать собственный рендерер ячеек для моего JTree, чтобы добавить немного JLabel в каждую ячейку.А затем разрешите пользователю нажимать на эти метки без необходимости сначала выделять ячейку.

Итак, я создал рендер, который возвращает JPanel, который содержит DefaultTreeCellRenderer и 2 JLabel.

Затем я создал редактор, позволяющий пользователю нажимать на эти ярлыки благодаря MouseListener.Все работает хорошо, за исключением того, что пользователь должен выбрать ячейку, прежде чем нажать на ярлык.Я пытался вернуть «false» с помощью метода «ShouldSelectCell», но он не работает.

Кто-то знает почему?

Вот редактор:

public class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
{
    private TreeNodeRenderer renderer;

    public TreeNodeEditor(TreeNodeRenderer treeRenderer)
    {
        this.renderer = treeRenderer;

    //change the cursor when it's over a label  renderer.getDeleteButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    renderer.getUploadButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));       renderer.getDownloadButton().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

                 //add labels' mouse listeners 
        addLabelMouseListener(renderer.getDeleteButton());
        addLabelMouseListener(renderer.getUploadButton());
    }

    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)
    {
        ...

        return renderer.getTreeCellRendererComponent(
                tree, value, isSelected,
                expanded, leaf, row, true);
    }

    public boolean isCellEditable(EventObject anEvent) 
    {
        return true;
    }

    public boolean shouldSelectCell(EventObject anEvent) 
    {
        return false;
    }


    public boolean stopCellEditing() 
    {
        return super.stopCellEditing();
    }

    public void cancelCellEditing() 
    {
        super.cancelCellEditing();
    }

    public void addCellEditorListener(CellEditorListener l)
    {
        super.addCellEditorListener(l);
    }

    public void removeCellEditorListener(CellEditorListener l) 
    {
        super.removeCellEditorListener(l);
    }

    public Object getCellEditorValue() 
    {
        return null;
    }
}

РЕДАКТИРОВАТЬ - Здесь SSCCE:

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.EventObject;

import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.CellEditorListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;



public class EditJTreeCell extends JFrame
{
    /**
     * 
     */
    private static final long serialVersionUID = 4745146614430249610L;

    private JTree tree;
    private DefaultTreeModel treeModel;
    private DefaultMutableTreeNode root;

    public EditJTreeCell()
    {
        super("Sample");
        root = new DefaultMutableTreeNode("Root folder");
        treeModel = new DefaultTreeModel(root);
        tree = new JTree(treeModel);

        TreeNodeRenderer renderer = new TreeNodeRenderer();
        tree.setCellRenderer(renderer);
        tree.setCellEditor(new TreeNodeEditor());
        tree.setEditable(true);

        //tree creation
        DefaultMutableTreeNode folder = new DefaultMutableTreeNode("folder1");
        DefaultMutableTreeNode file = new DefaultMutableTreeNode("file1");
        folder.add(file);
        file = new DefaultMutableTreeNode("file2");
        folder.add(file);
        root.add(folder);
        folder = new DefaultMutableTreeNode("folder2");
        file = new DefaultMutableTreeNode("file1");
        folder.add(file);
        file = new DefaultMutableTreeNode("file2");
        folder.add(file);
        file = new DefaultMutableTreeNode("file3");
        folder.add(file);
        root.add(folder);

        this.setSize(400, 800);
        this.add(tree);
        this.setVisible(true);
    }

    public static void main(String[] args) 
    {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
        new EditJTreeCell();
    }
}

class TreeNodeRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private JPanel panel1 = new JPanel();
    private JLabel delete = new JLabel("DELETE");
    private JLabel upload = new JLabel("UPLOAD");

    public Component getTreeCellRendererComponent(JTree tree, Object value,
            boolean selected, boolean expanded, boolean leaf, int row,
            boolean hasFocus) 
    {   
        //
        // DELETE label
        //
        delete.setName("delete");
        delete.setIcon(new ImageIcon("trash.png"));
        //addLabelMouseListener(delete);
        //
        // UPLOAD label
        //
        upload.setName("upload");
        upload.setIcon(new ImageIcon("app_up.png"));
        //addLabelMouseListener(upload);


        DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
        Color backgroundSelectionColor = defaultRenderer.getBackgroundSelectionColor();
        Color backgroundNonSelectionColor = defaultRenderer.getBackgroundNonSelectionColor();

        DefaultTreeCellRenderer component = (DefaultTreeCellRenderer) super.getTreeCellRendererComponent(tree,
                value, selected, expanded, leaf, row, hasFocus);

        if(selected)
        {   
            panel1.setBackground(backgroundSelectionColor);
        }
        else
        {
            panel1.setBackground(backgroundNonSelectionColor);
        }

        panel1.add(component);
        panel1.add(delete);
        panel1.add(upload);

        return panel1;
    }
}

class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private JLabel button1;
    private JLabel button2;
    private JPanel panel1;
    private DefaultMutableTreeNode node = null;
    private DefaultTreeCellRenderer defaultRenderer;


    public TreeNodeEditor()
    {
        super();
        panel1 = new JPanel();
        defaultRenderer = new DefaultTreeCellRenderer();
        button1 = new JLabel("DELETE");
        button1.setOpaque(true);
        button1.setIcon(new ImageIcon("trash.png"));
        button1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button1.addMouseListener(new MouseListener() 
        {
            public void mouseClicked(MouseEvent arg0) {
                System.out.println("Delete clicked");
            }
            public void mouseEntered(MouseEvent arg0) {}
            public void mouseExited(MouseEvent arg0) {}
            public void mousePressed(MouseEvent arg0) {}
            public void mouseReleased(MouseEvent arg0) {}

        });
        button2 = new JLabel("UPLOAD");
        button2.setOpaque(true);
        button2.setIcon(new ImageIcon("app_up.png"));
        button2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2.addMouseListener(new MouseListener() 
        {
            public void mouseClicked(MouseEvent arg0) {
                System.out.println("Upload clicked");
            }
            public void mouseEntered(MouseEvent arg0) {}
            public void mouseExited(MouseEvent arg0) {}
            public void mousePressed(MouseEvent arg0) {}
            public void mouseReleased(MouseEvent arg0) {}

        });
    }


    public Component getTreeCellEditorComponent(JTree tree, Object value, 
            boolean isSelected, boolean expanded, boolean leaf, int row)
    {
        //in order to do some actions on a node
        if(value instanceof DefaultMutableTreeNode)
        {
            node = (DefaultMutableTreeNode) value;
        }


         defaultRenderer.getTreeCellRendererComponent(tree,
                    value, isSelected, expanded, leaf, row, true);

        panel1.add(defaultRenderer);
        panel1.add(button1);
        panel1.add(button2);
        return panel1;
    }

    public boolean isCellEditable(EventObject anEvent) 
    {
        return true;
    }

    public boolean shouldSelectCell(EventObject anEvent) 
    {
        return false;
    }


    public boolean stopCellEditing() 
    {
        return super.stopCellEditing();
    }

    public void cancelCellEditing() 
    {
        super.cancelCellEditing();
    }

    public void addCellEditorListener(CellEditorListener l)
    {
        super.addCellEditorListener(l);
    }

    public void removeCellEditorListener(CellEditorListener l) 
    {
        super.removeCellEditorListener(l);
    }

    public Object getCellEditorValue() 
    {
        return null;
    }
}

Ответы [ 3 ]

5 голосов
/ 26 ноября 2011

начало редактирования на mouseEnter является допустимым решением: -)

Ваш редактор, с другой стороны, не является допустимой реализацией: он не может уведомить своего слушателя, если редактирование прекращается из-за внутренних событий(как, например, нажатие на любую из кнопок) Ниже приведен пример того, как достичь цели и иметь правильную реализацию

пара комментариев:

  • , если вы хотитечто-то вроде кнопки ... используйте кнопку: в противном случае пользователи могут запутаться
  • в вашем редакторе, при необходимости установите действие для кнопок
  • выполните все основные настройки панели (например, добавление дочерних элементов)в конструкторе)
  • , чтобы начать редактирование / определить, какая кнопка нажата, переотправить событие, полученное в shouldSelect.Сделайте это в SwingUtilities.invokeLater, чтобы убедиться, что все ожидающие события (в дереве) готовы
  • не изменяют узел дерева внутри редактора: a) эти изменения не уведомят модель, b)быть отмененным поведением завершения редактирования по умолчанию дерева.DefaultTreeTable сбрасывает userObject дерева с editorValue, что сделано в valueForPathChanged: для реализации пользовательского поведения переопределите этот метод в модели

в коде:

static class TreeNodeEditor extends AbstractCellEditor implements TreeCellEditor {
    private static final long serialVersionUID = 1L;
    private JButton button1;
    private JButton button2;
    private JPanel panel1;
    // JW: do not modify the node inside the editor 
    //        private DefaultMutableTreeNode node = null;
    private DefaultTreeCellRenderer defaultRenderer;

    private Object editorValue;

    public TreeNodeEditor() {
        super();
        panel1 = new JPanel();
        defaultRenderer = new DefaultTreeCellRenderer();
        button1 = new JButton("DELETE");
        button1.setOpaque(true);
        button1.setIcon(new ImageIcon("trash.png"));
        button1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2 = new JButton("UPLOAD");
        button2.setOpaque(true);
        button2.setIcon(new ImageIcon("app_up.png"));
        button2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        button2.setAction(createAction("upload", "UPLOAD"));
        button1.setAction(createAction("delete", "DELETE"));
        panel1.add(defaultRenderer);
        panel1.add(button1);
        panel1.add(button2);
    }

    private Action createAction(final String actionCommand, String display) {
        Action action = new AbstractAction(display) {

            @Override
            public void actionPerformed(ActionEvent e) {
                stopEditing(actionCommand);
            }

        };
        return action;

    }
    /**
     * @param actionCommand
     */
    protected void stopEditing(String actionCommand) {
        editorValue = actionCommand;
        stopCellEditing();
    }

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value,
            boolean isSelected, boolean expanded, boolean leaf, int row) {
        // in order to do some actions on a node
        //            if (value instanceof DefaultMutableTreeNode) {
        //                node = (DefaultMutableTreeNode) value;
        //            }

        defaultRenderer.getTreeCellRendererComponent(tree, value,
                isSelected, expanded, leaf, row, true);

        return panel1;
    }

    /**
     * 
     */
    private void reset() {
        editorValue = null;
    }

    /**
     * At this point in time the component is added to the tree (not documented!) but
     * tree's internal cleanup might not yet be ready
     */ 
    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        reset();
        if (anEvent instanceof MouseEvent) {
            redirect((MouseEvent) anEvent);
        }
        return false;
    }

    private void redirect(final MouseEvent anEvent) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MouseEvent ev = SwingUtilities.convertMouseEvent(anEvent.getComponent(), anEvent, panel1);
                panel1.dispatchEvent(ev);

            }
        });
    }

    @Override
    public Object getCellEditorValue() {
        return editorValue;
    }
}
1 голос
/ 16 ноября 2011

Наконец, я решил свою проблему с MouseMotionListener и методом: myTree.startEditingAtPath(path).Теперь узел находится в режиме редактирования, когда курсор находится над ним.

tree.addMouseMotionListener(new MouseMotionListener() {
            public void mouseMoved(MouseEvent e) 
            {
                if (tree.getRowForLocation(e.getX(), e.getY()) != -1)
                {
                    tree.startEditingAtPath(tree.getPathForLocation(e.getX(), e.getY()));
                }               
            }
            public void mouseDragged(MouseEvent e) {}
        });

Однако, если у кого-то есть идея получше, пожалуйста, дайте мне знать.

0 голосов
/ 14 ноября 2011

Я думаю, вам нужно добавить слушателей мыши внутри самого treeNodeRenderer.Скорее всего, курсор мыши добавляется только после того, как вы войдете в «режим редактирования» и редактор будет помещен в ячейку.

...