JTree cell editor receives mouse clicks differently depending on OS

前端 未结 3 1789
伪装坚强ぢ
伪装坚强ぢ 2021-01-22 02:08

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 set

3条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2021-01-22 02:49

    I've come up with something that creates my desired behavior and does not use a TreeCellEditor at all. Instead, the tree is uneditable, and is instantiated with a custom extension of JTree which has processMouseEvent overridden. I got this idea here.

    It seems to work perfect, but it is still a little bit hacky (it does a loop of calculations to determine where the start of the tree cell is, since that can vary based on indentation). Also I pretty much disabled mouseClicked and mouseReleased type events, and am controlling the JTreeMod only with mousePressed events. Not sure if this will bite me later or is bad practice, but I didn't like my custom code running 3 times in a row for all the events. I also haven't been able to test on a non-Windows OS just yet.

    Here is the console output after clicking, in series (1) the image one (2) the text one (3) the image two (4) the text two. Again, this perfectly implements my desired behavior.

    you clicked the image for row 1. this was detected, but no selection will happen!
    you clicked the text for row 1. this was detected, and selection WILL happen!
    SELECTION CHANGED!
    you clicked the image for row 2. this was detected, but no selection will happen!
    you clicked the text for row 2. this was detected, and selection WILL happen!
    SELECTION CHANGED!
    

    And here is the new SSCCE:

    package TreeTest;
    
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.EventQueue;
    import java.awt.event.MouseEvent;
    import java.net.URL;
    
    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.TreeSelectionEvent;
    import javax.swing.event.TreeSelectionListener;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
    import javax.swing.tree.TreeCellRenderer;
    import javax.swing.tree.TreeNode;
    import javax.swing.tree.TreePath;
    import javax.swing.tree.TreeSelectionModel;
    
    @SuppressWarnings("serial")
    public class TreeTest2 extends JComponent {
    
        private JFrame frame;
        private DefaultTreeModel treeModel;
        private DefaultMutableTreeNode root;
        private JTreeMod 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 {
                        TreeTest2 window = new TreeTest2();
                        window.frame.setVisible(true);
                        window.frame.requestFocusInWindow();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    
        public TreeTest2() {
            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 JTreeMod(treeModel);
            tree.setEditable(false);
            tree.getSelectionModel().setSelectionMode(
                TreeSelectionModel.SINGLE_TREE_SELECTION);
            tree.setRootVisible(false);
            tree.setShowsRootHandles(true);
            tree.setCellRenderer(new TreeRenderer());
            tree.putClientProperty("JTree.lineStyle", "None");
            tree.setBackground(Color.white);
    
            DefaultMutableTreeNode one = new DefaultMutableTreeNode("one");
            DefaultMutableTreeNode two = new DefaultMutableTreeNode("two");
    
            treeModel.insertNodeInto(one, root, 0);
            treeModel.insertNodeInto(two, one, 0);
    
            TreeNode[] nodes = treeModel.getPathToRoot(root);
            tree.expandPath(new TreePath(nodes));
            nodes = treeModel.getPathToRoot(one);
            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 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();
    
                JPanel comp = new JPanel();
                comp.setLayout(new BoxLayout(comp, BoxLayout.X_AXIS));
                comp.add(numIcon);
                comp.add(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 JTreeMod extends JTree {
            public JTreeMod(DefaultTreeModel treeModel) {
                super(treeModel);
            }
    
            @Override
            protected void processMouseEvent(MouseEvent e) {
                int type = e.getID();
                if (type == MouseEvent.MOUSE_CLICKED || type == MouseEvent.MOUSE_RELEASED) {
                    // do nothing
                } else if (type == MouseEvent.MOUSE_PRESSED) {
                    int x = e.getX();
                    int y = e.getY();
    
                    int row = this.getRowForLocation(x, y);
                    if (row == -1) {
                        super.processMouseEvent(e);
                        return;
                    }
    
                    int xOffset = x;
                    int row1 = row;
                    while (row1 == row) {
                        xOffset--;
                        row1 = this.getRowForLocation(xOffset, y);
                    }
                    xOffset++;
    
                    if (x - xOffset <= 16) {
                        System.out.println("you clicked the image for row " + (row + 1) +
                                ". this was detected, but no selection will happen!");
                        return;
                    } else {
                        System.out.println("you clicked the text for row " + (row + 1) + 
                                ". this was detected, and selection WILL happen!");
                        super.processMouseEvent(e);
                    }
                } else {
                    super.processMouseEvent(e);
                }
            }
        }
    }
    

提交回复
热议问题