根据操作系统的不同,JTree单元编辑器获得的鼠标单击有所不同 [英] JTree cell editor receives mouse clicks differently depending on OS

查看:76
本文介绍了根据操作系统的不同,JTree单元编辑器获得的鼠标单击有所不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个树形单元渲染器/编辑器框架,该框架虽然有点黑,但在Windows和Linux上都可以完美运行.下图显示了示例设置.

I've created a tree cell renderer/editor framework that is admittedly a little bit hacky, but it works perfectly on Windows and Linux. The image below illustrates a sample setup.

目标是,如果用户单击图像(数字)1或2,则应用程序会对此点击做出响应,但不会选择树行.如果用户单击文本一两个,则应用程序对此单击做出响应,然后执行选择树行.同样,我实现这一点的方法有些古怪.基本上,当用户单击树行时,将显示编辑器组件(看起来与渲染器组件相同),并且编辑器组件具有鼠标侦听器,该侦听器可以确定用户在该行中单击的位置.

The goal is that if the user clicks on the image (numeral) 1 or 2, then the application responds to that click but does not select the tree row. If the user clicks on the text one or two, the application responds to that click and does select the tree row. The way I've implemented this, again, is a little hacky. Basically when the user clicks on the tree row, the editor component is displayed (which looks identical to the renderer component), and the editor component has a mouse listener which can determine where the user clicked in the row.

在Windows/Linux上可以运行的事实依赖于我一直认为脆弱的东西.基本上,如果单击一次该行,则该单击既会(a)调出编辑器,又(b)激活编辑器组件上的鼠标侦听器.这就是我想要的方式!但是,当您尝试在Mac OSX上运行该应用程序(如果重要的话,为10.6.2)时,上述脆弱的假设将不再成立.每当您想与树互动时,都必须单击两次(一次以激活编辑器,再一次以激活鼠标侦听器).

The fact that this works on Windows/Linux relies on something which I always thought seemed flimsy to rely on. Basically, if you click the row a single time, that single click both (a) brings up the editor and (b) activates the mouse listener on the editor component. This is the way I want it! However, when you try running the app on Mac OSX (10.6.2 if it matters), the aforementioned flimsy assumption no longer holds true. Any time you want to interact with the tree, you have to click twice (once to activate the editor, and again to activate the mouse listener).

下面的SSCCE可以重现行为.当然,如果您没有OSX,则无法重现不需要的行为,但是也许您仍然可以推荐一种更聪明的方法来实现我的目标.在控制台上查看sysout消息,该消息指示您单击树的各个部分时发生的情况.

The SSCCE below can reproduce the behavior. Of course, if you don't have OSX you can't reproduce the undesired behavior, but perhaps you can still recommend a smarter way to accomplish my goal. Watch the console for sysout messages indicated what's happening as you click the various parts of the tree.

哦,SSCCE引用了这两个图像:

Oh, and the SSCCE references these two images:

package TreeTest;

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.EventObject;
import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

@SuppressWarnings("serial")
public class TreeTest extends JComponent {

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

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(
                UIManager.getSystemLookAndFeelClassName());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    TreeTest window = new TreeTest();
                    window.frame.setVisible(true);
                    window.frame.requestFocusInWindow();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public TreeTest() {
        initialize();
    }

    private void initialize() {
        frame = new JFrame("Tree Test");
        frame.setBounds(400, 400, 250, 150);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        root = new DefaultMutableTreeNode("root");
        treeModel = new DefaultTreeModel(root);
        tree = new JTree(treeModel);
        tree.setEditable(true);
        tree.getSelectionModel().setSelectionMode(
            TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree.setRootVisible(false);
        tree.setShowsRootHandles(false);
        tree.setCellRenderer(new TreeRenderer());
        tree.setCellEditor(new TreeEditor());
        tree.putClientProperty("JTree.lineStyle", "None");
        tree.setBackground(Color.white);

        treeModel.insertNodeInto(new DefaultMutableTreeNode("two"), root, 0);
        treeModel.insertNodeInto(new DefaultMutableTreeNode("one"), root, 0);

        TreeNode[] nodes = treeModel.getPathToRoot(root);
        tree.expandPath(new TreePath(nodes));
        tree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                System.out.println("SELECTION CHANGED!");
            }
        });

        frame.getContentPane().add(tree);
    }

    public class TreeComponent extends JPanel {

        public TreeComponent(JLabel numIcon, JLabel numText) {
            this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            this.add(numIcon);
            this.add(numText);
        }
    }

    public class TreeRenderer implements TreeCellRenderer {

        private ImageIcon oneIcon;
        private ImageIcon twoIcon;

        public TreeRenderer() {
            try {
                oneIcon = new ImageIcon(ImageIO.read(
                    new URL("http://i.imgur.com/HtHJkfI.png")));
                twoIcon = new ImageIcon(ImageIO.read(
                    new URL("http://i.imgur.com/w5jAp5c.png")));
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value,
            boolean selected, boolean expanded, boolean leaf, int row,
            boolean hasFocus) {
            JLabel numIcon = new JLabel();
            numIcon.setAlignmentX(JLabel.CENTER_ALIGNMENT);
            numIcon.setAlignmentY(JLabel.CENTER_ALIGNMENT);
            numIcon.setBorder(new EmptyBorder(0, 0, 0, 4));

            JLabel numText = new JLabel();

            TreeComponent comp = new TreeComponent(numIcon, numText);

            String str = (String) ((DefaultMutableTreeNode) value).getUserObject();
            if (str.equals("one")) {
                numIcon.setIcon(oneIcon);
                numText.setText("one");
            } else if (str.equals("two")) {
                numIcon.setIcon(twoIcon);
                numText.setText("two");
            }

            numText.setOpaque(true);
            if (selected) {
                numText.setBackground(new Color(209, 230, 255));
                numText.setBorder(new LineBorder(
                    new Color(132, 172, 221), 1, false));
            } else {
                numText.setBackground(Color.white);
                numText.setBorder(new LineBorder(Color.white, 1, false));
            }
            comp.setFocusable(false);
            comp.setBackground(Color.white);
            return comp;
        }
    }

    public class TreeEditor implements TreeCellEditor {

        private TreeRenderer rend;
        private TreeComponent editorComponent;
        private JTree tree;
        private DefaultTreeModel treeModel;
        private DefaultMutableTreeNode node;
        private String str;

        public TreeEditor() {
            rend = new TreeRenderer();
        }

        @Override
        public Component getTreeCellEditorComponent(
            final JTree tree, final Object value, boolean isSelected,
            boolean expanded, boolean leaf, int row) {
            this.tree = tree;
            treeModel = (DefaultTreeModel) tree.getModel();
            node = (DefaultMutableTreeNode) value;
            Object userObject = node.getUserObject();
            this.str = (String) userObject;

            TreeNode[] nodes = treeModel.getPathToRoot(node);
            final TreePath path = new TreePath(nodes);

            editorComponent = (TreeComponent) rend.getTreeCellRendererComponent(
                tree, value, isSelected, expanded, leaf, row, false);
            editorComponent.addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    TreeEditor.this.stopCellEditing();
                    int x = e.getX();
                    if (x >= 0 && x <= 16) {
                        System.out.println(
                            "you clicked the image for row " + str);
                    } else if (x > 16) {
                        System.out.println(
                            "you clicked the text for row " + str);
                        tree.setSelectionPath(path);
                    }
                }
            });
            return editorComponent;
        }

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

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

        @Override
        public boolean stopCellEditing() {
            tree.cancelEditing();
            return false;
        }

        @Override
        public Object getCellEditorValue() {
            return null;
        }

        @Override
        public void cancelCellEditing() {
        }

        @Override
        public void addCellEditorListener(CellEditorListener l) {
        }

        @Override
        public void removeCellEditorListener(CellEditorListener l) {
        }
    }
}

推荐答案

有关如何实现复杂的cellEditor的快速概述.假定编辑器的所有交互元素确实确实在编辑节点的userObject的属性,就像在OP的完整代码中一样.

A quick outline of how to implement complex cellEditors. The assumption is that all interactive elements of the editor do indeed edit a property of the node's userObject - as is the case in the OP's full code.

合作者

  • 具有多个属性的模拟数据对象
  • 具有多个子级的渲染器,每个子级都绑定到数据对象的一个​​属性
  • 使用呈现组件的实时"实例的编辑器,即添加侦听器以根据需要遵守编辑器的合同

一些代码(显然,不是可用于实际用途,只是为了了解这个主意:)

Some code (obviously not usable for real-world usage, just to get the idea :)

public static class ViewProvider extends AbstractCellEditor 
    implements TreeCellEditor, TreeCellRenderer {

    private JCheckBox firstBox;
    private JButton colorButton;
    private JComponent nodePanel;
    private JButton nameButton;

    private Data data;
    private boolean ignore;

    public ViewProvider(boolean asEditor) {
        initComponents();
        if (asEditor)
          installListeners();
    }

    protected void initComponents() {
        nodePanel = new JPanel();
        nodePanel.setOpaque(false);
        firstBox = new JCheckBox();
        colorButton = new JButton();
        // if we need something clickable use something ... clickable :-)
        nameButton = new JButton();
        nameButton.setContentAreaFilled(false);
        nameButton.setOpaque(true);
        nameButton.setBorderPainted(false);
        nameButton.setMargin(new Insets(0, 0, 0, 0));
        nodePanel.add(firstBox);
        nodePanel.add(colorButton);
        nodePanel.add(nameButton);
    }

    protected void installListeners() {
        ActionListener cancel = new ActionListener() {

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

        };
        nameButton.addActionListener(cancel);
        ActionListener stop = new ActionListener() {

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

        };
        firstBox.addActionListener(stop);
        // Note: code for using a button to trigger opening a dialog
        // is in the tutorial, should replace this
        colorButton.addActionListener(stop);
    }

    @Override
    public Component getTreeCellRendererComponent(JTree tree, Object value,
            boolean selected, boolean expanded, boolean leaf, int row,
            boolean hasFocus) {
        Data data = (Data) ((DefaultMutableTreeNode) value).getUserObject();
        firstBox.setSelected(data.isVisible);
        colorButton.setBackground(data.color);
        nameButton.setText(data.name);
        nameButton.setBackground(selected ? Color.YELLOW : tree.getBackground());
        nameButton.setFont(tree.getFont());
        return nodePanel;
    }

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value,
            boolean isSelected, boolean expanded, boolean leaf, int row) {
        // copy to not fiddle with the original
        data = new Data((Data) ((DefaultMutableTreeNode) value).getUserObject());
        ignore = true;
        getTreeCellRendererComponent(tree, value, isSelected, expanded, leaf, row, false);
        ignore = false;
        return nodePanel;
    }

    @Override
    public Object getCellEditorValue() {
        return data;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        // at this point the editing component is added to the tree
        // and the mouse coordinates still in tree coordinates
        if (anEvent instanceof MouseEvent) {
            MouseEvent me = (MouseEvent) anEvent;
            Point loc = SwingUtilities.convertPoint(me.getComponent(), 
                    me.getPoint(), nodePanel);
            return loc.x >= nameButton.getX();
        }
        return false;
    }

    @Override
    public boolean stopCellEditing() {
        if (ignore) return false;
        // real-world data will have setters
        data.isVisible = firstBox.isSelected();
        return super.stopCellEditing();
    }

    @Override
    public void cancelCellEditing() {
        if (ignore) return;
        data = null;
        super.cancelCellEditing();
    }

}

// simple Data - obviously not for production 
public static class Data {
    boolean isVisible;
    Color color;
    String name;

    public Data(boolean isVisible, Color color,
            String name) {
        this.isVisible = isVisible;
        this.color = color;
        this.name = name;
    }

    /**
     * A copy constructor to allow editors to manipulate its
     * properties without changing the original.
     * 
     * @param original
     */
    public Data(Data original) {
        this.isVisible = original.isVisible;
        this.color = original.color;
        this.name = original.name;
    }
}

// usage:
DefaultMutableTreeNode root = new DefaultMutableTreeNode(
    new Data(true, Color.RED, "someName"));
root.add(new DefaultMutableTreeNode(new Data(true, Color.GREEN, "other")));
root.add(new DefaultMutableTreeNode(new Data(false, Color.BLUE, "whatagain")));
root.add(new DefaultMutableTreeNode(new Data(false, Color.YELLOW, "dummy")));

JTree tree = new JTree(root);
tree.setCellRenderer(new ViewProvider(false));
tree.setCellEditor(new ViewProvider(true));
tree.setEditable(true);

这篇关于根据操作系统的不同,JTree单元编辑器获得的鼠标单击有所不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆