Java JTree节点是可点击的URL链接

时间:2021-07-27 12:36:04

I'm trying to make a node be a clickable URL, but I just can't seem to figure out how.

我正在尝试将节点设置为可点击的URL,但我似乎无法弄清楚如何。

I've searched high and low, and I can't seem to find a solution.

我搜索了高低,我似乎无法找到解决方案。

This is my code:

这是我的代码:

public class NyttigeLinks {

    private static JFrame nyttigeLinks;

    public static void main(String[] args) {

                    initialize();
            }

    public NyttigeLinks() {

    }

    private static void initialize() {
        nyttigeLinks = new JFrame();
        nyttigeLinks.setBounds(new Rectangle(0, 0, 350, 650));
        nyttigeLinks.getContentPane().setBounds(new Rectangle(0, 0, 350, 650));
        nyttigeLinks.getContentPane().setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        nyttigeLinks.getContentPane().setLayout(null);

        JLabel logoLabel = new JLabel("");
        logoLabel.setIcon(new ImageIcon(NyttigeLinks.class.getResource("/images/ssiLogo.jpg")));
        logoLabel.setBounds(0, 0, 350, 60);

        JTree tree = new JTree();

        nyttigeLinks.getContentPane().add(logoLabel);

        JScrollPane scrollPane = new JScrollPane(tree);
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollPane.setBounds(10, 71, 324, 508);
        nyttigeLinks.getContentPane().add(scrollPane);

        tree.setModel(new DefaultTreeModel(
            new DefaultMutableTreeNode("Nyttige Links\t") {
                {
                    DefaultMutableTreeNode node_1;
                    node_1 = new DefaultMutableTreeNode("Projekt Wiki");
                        node_1.add(new DefaultMutableTreeNode("AO"));
                        node_1.add(new DefaultMutableTreeNode("Attends"));
                        node_1.add(new DefaultMutableTreeNode("Carlsberg"));
                        node_1.add(new DefaultMutableTreeNode("COOP"));
                        node_1.add(new DefaultMutableTreeNode("Dafgaard"));
                        node_1.add(new DefaultMutableTreeNode("Jysk DK"));
                        node_1.add(new DefaultMutableTreeNode("Jysk SE"));
                        node_1.add(new DefaultMutableTreeNode("Kvadrat"));
                        node_1.add(new DefaultMutableTreeNode("Solar"));
                        node_1.add(new DefaultMutableTreeNode("Stockmann"));
                        node_1.add(new DefaultMutableTreeNode("Tine"));
                        node_1.add(new DefaultMutableTreeNode("Unicef"));
                        node_1.add(new DefaultMutableTreeNode("Vectura"));
                    add(node_1);
                    node_1 = new DefaultMutableTreeNode("Helpdesk Norcic");
                        node_1.add(new DefaultMutableTreeNode("Test"));
                    add(node_1);
                    node_1 = new DefaultMutableTreeNode("Test");
                        node_1.add(new DefaultMutableTreeNode("AO"));
                        node_1.add(new DefaultMutableTreeNode("Attends"));
                        node_1.add(new DefaultMutableTreeNode("Carlsberg"));
                        node_1.add(new DefaultMutableTreeNode("COOP"));
                        node_1.add(new DefaultMutableTreeNode("Dafgaard"));
                        node_1.add(new DefaultMutableTreeNode("Jysk DK"));
                        node_1.add(new DefaultMutableTreeNode("Jysk SE"));
                        node_1.add(new DefaultMutableTreeNode("Kvadrat"));
                        node_1.add(new DefaultMutableTreeNode("Solar"));
                        node_1.add(new DefaultMutableTreeNode("Stockmann"));
                        node_1.add(new DefaultMutableTreeNode("Tine"));
                        node_1.add(new DefaultMutableTreeNode("Unicef"));
                        node_1.add(new DefaultMutableTreeNode("VecturaAO"));
                        node_1.add(new DefaultMutableTreeNode("Attends"));
                        node_1.add(new DefaultMutableTreeNode("Carlsberg"));
                        node_1.add(new DefaultMutableTreeNode("COOP"));
                        node_1.add(new DefaultMutableTreeNode("Dafgaard"));
                        node_1.add(new DefaultMutableTreeNode("Jysk DK"));
                        node_1.add(new DefaultMutableTreeNode("Jysk SE"));
                        node_1.add(new DefaultMutableTreeNode("Kvadrat"));
                        node_1.add(new DefaultMutableTreeNode("Solar"));
                        node_1.add(new DefaultMutableTreeNode("Stockmann"));
                        node_1.add(new DefaultMutableTreeNode("Tine"));
                        node_1.add(new DefaultMutableTreeNode("Unicef"));
                        node_1.add(new DefaultMutableTreeNode("VecturaAO"));
                        node_1.add(new DefaultMutableTreeNode("Attends"));
                        node_1.add(new DefaultMutableTreeNode("Carlsberg"));
                        node_1.add(new DefaultMutableTreeNode("COOP"));
                        node_1.add(new DefaultMutableTreeNode("Dafgaard"));
                        node_1.add(new DefaultMutableTreeNode("Jysk DK"));
                        node_1.add(new DefaultMutableTreeNode("Jysk SE"));
                        node_1.add(new DefaultMutableTreeNode("Kvadrat"));
                        node_1.add(new DefaultMutableTreeNode("Solar"));
                        node_1.add(new DefaultMutableTreeNode("Stockmann"));
                        node_1.add(new DefaultMutableTreeNode("Tine"));
                        node_1.add(new DefaultMutableTreeNode("Unicef"));
                        node_1.add(new DefaultMutableTreeNode("Vectura"));
                    add(node_1);
                }
            }
        ));
        tree.setBounds(10, 71, 324, 540);
        tree.setRootVisible(false);

        JLabel bottomLabelTop = new JLabel("                   Nyttige Links Version 1.0");
        bottomLabelTop.setBounds(0, 590, 230, 14);
        nyttigeLinks.getContentPane().add(bottomLabelTop);

        JLabel bottomLabelBot = new JLabel("                              Made by xxx");
        bottomLabelBot.setBounds(0, 605, 230, 15);
        nyttigeLinks.getContentPane().add(bottomLabelBot);

        JButton btnNewButton = new JButton("Admin");
        btnNewButton.setIcon(new ImageIcon(NyttigeLinks.class.getResource("/images/appIcon.ico")));
        btnNewButton.setBounds(240, 590, 80, 20);
        nyttigeLinks.getContentPane().add(btnNewButton);
        nyttigeLinks.setPreferredSize(new Dimension(350, 650));
        nyttigeLinks.setSize(new Dimension(350, 650));
        nyttigeLinks.setResizable(false);
        nyttigeLinks.setTitle("Nyttige Links");
        nyttigeLinks.setIconImage(Toolkit.getDefaultToolkit().getImage(NyttigeLinks.class.getResource("/images/appIcon.ico")));
        nyttigeLinks.setBackground(Color.YELLOW);
        nyttigeLinks.setBounds(100, 100, 350, 650);
        nyttigeLinks.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        nyttigeLinks.setVisible(true);
    }
}

I apologize for being a bit of a noob, but you gotta start somewhere, right?

我为有点像菜鸟而道歉,但你必须从某个地方开始,对吗?

If someone can point me in the right direction, I would appreciate it a lot! Have a good night everyone!

如果有人能指出我正确的方向,我会非常感激!大家晚安!

2 个解决方案

#1


2  

I don't believe there is a way to make the node itself a URL. However, you could accomplish the same thing by using listeners to open the browser when the node is clicked. To open the browser, you can use the Desktop#browse(URI) method.

我不相信有一种方法可以使节点本身成为URL。但是,您可以通过使用侦听器在单击节点时打开浏览器来完成相同的操作。要打开浏览器,您可以使用Desktop#browse(URI)方法。

On the topic of clicking, I would recommend not opening a URL when a node is single-clicked. For a user that would be very annoying (IMO). Instead, I'd recommend using a double-click. This answer provides a good way to distinguish between a single and double-click, and how to add it to the Tree.

关于点击的主题,我建议不要在单击节点时打开URL。对于非常烦人的用户(IMO)。相反,我建议使用双击。这个答案提供了区分单击和双击以及如何将其添加到树的好方法。


[...] but you gotta start somewhere, right?

[...]但你必须从某个地方开始,对吗?

Exactly! Allow me to give a bit more detail here that will hopefully be helpful for learning a bit more.

究竟!请允许我在此提供更多细节,希望对学习更多有所帮助。

One challenge you will face is how to know which URL to open for a given node. Currently you only know the String that is displayed, and I think it's a safe assumption that you don't want to be displaying a full URL for each node. Instead of creating the DefaultMutableTreeNode objects with a String parameter, I would recommend creating an object to pass in to the DefaultMutableTreeNode constructor instead. This object can also have a URI attribute so it knows which URL to open.

您将面临的一个挑战是如何知道为给定节点打开哪个URL。目前您只知道显示的字符串,我认为您可以安全地假设您不希望为每个节点显示完整的URL。我建议创建一个对象以传递给DefaultMutableTreeNode构造函数,而不是使用String参数创建DefaultMutableTreeNode对象。此对象还可以具有URI属性,以便它知道要打开的URL。

For example:

public class LeafNodeObject {
    private final URI uri;

    public LeafNodeObject(final String display, final URI uri) {
        this.display = display;
        this.uri = uri;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    public void onDoubleClick() {
        try {
            Desktop.getDesktop().browse(uri);
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

}

Nodes can be added like so:

可以像这样添加节点:

node_1.add(new DefaultMutableTreeNode(new LeafNodeObject("Test", new URI("www.eclipse.org"))));

And in our listener (slightly modified from the link above):

在我们的听众中(从上面的链接略微修改):

final MouseListener ml = new MouseAdapter() {
    public void mousePressed(final MouseEvent e) {
        final int selRow = tree.getRowForLocation(e.getX(), e.getY());
        final TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
        if (selRow != -1) {
            final DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent();
            if (e.getClickCount() == 1) {
                // Single click
            } else if (e.getClickCount() == 2) {
                // Double click
                ((LeafNodeObject) node.getUserObject()).onDoubleClick();
            }
        }
    }
};

Now when there is a double-click event, we call the onDoubleClick() method on the LeafNodeObject that we've created.

现在当有双击事件时,我们在我们创建的LeafNodeObject上调用onDoubleClick()方法。

This looks great, except that we will run into class casting issues with nodes that are not leaf nodes! To fix this, we can make a similar class for those nodes (eg. ParentNodeObject). In the interest of good OOP, we should recognize the common behavior of these two classes (onDoubleClick(), and also an onSingleClick()), and create an interface for the shared ability.

这看起来很棒,除了我们将遇到非叶节点的节点的类转换问题!为了解决这个问题,我们可以为这些节点创建一个类似的类(例如,ParentNodeObject)。为了好的OOP,我们应该认识到这两个类的常见行为(onDoubleClick(),以及onSingleClick()),并为共享能力创建一个接口。

For example:

public interface NodeObject {
    public void onSingleClick();
    public void onDoubleClick();
}

Now we can have our two classes implement this interface:

现在我们可以让我们的两个类实现这个接口:

public class ParentNodeObject implements NodeObject {
    private final String display;

    public ParentNodeObject(final String display) {
        this.display = display;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    @Override
    public void onSingleClick() {
        // Do nothing
    }

    @Override
    public void onDoubleClick() {
        // Do nothing
    }
}

public class LeafNodeObject implements NodeObject {
    private final URI uri;

    public LeafNodeObject(final String display, final URI uri) {
        this.display = display;
        this.uri = uri;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    @Override
    public void onSingleClick() {
        // Do nothing
    }

    @Override
    public void onDoubleClick() {
        try {
            Desktop.getDesktop().browse(uri);
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

}

As a result, in our listener we no longer need to know (and no longer care) whether the node is a parent or leaf node. We can simply call either onSingleClick() or onDoubleClick(), and the implementation handles the rest!

因此,在我们的监听器中,我们不再需要知道(并且不再关心)节点是父节点还是叶节点。我们可以简单地调用onSingleClick()或onDoubleClick(),实现处理剩下的!

if (e.getClickCount() == 1) {
    // Single click
    ((NodeObject) node.getUserObject()).onSingleClick();
} else if (e.getClickCount() == 2) {
    // Double click
    ((NodeObject) node.getUserObject()).onDoubleClick();
}

Back in your code, the parent and child nodes can be added like so:

回到代码中,可以添加父节点和子节点,如下所示:

node_1 = new DefaultMutableTreeNode(new ParentNodeObject("Helpdesk Norcic"));
node_1.add(new DefaultMutableTreeNode(new LeafNodeObject("Test", new URI("www.eclipse.org"))));

Now, when you single or double-click any node, either the onSingleClick() or onDoubleClick() methods will also be called. In this case we only care about what happens when there is a double-click on the leaf nodes, so we only need to fill in that method.

现在,当您单击或双击任何节点时,也将调用onSingleClick()或onDoubleClick()方法。在这种情况下,我们只关心在叶子节点上双击时会发生什么,所以我们只需要填写该方法。

#2


0  

While I haven't checked the above mentioned code, here is how I got around the issue while I was waiting for a reply to this thread:

虽然我没有检查上面提到的代码,但是在我等待回复此线程时,我是如何解决这个问题的:

class SelectionListener implements TreeSelectionListener {

    public void valueChanged(TreeSelectionEvent se) {
        JTree tree = (JTree) se.getSource();
        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
        String selectedNodeName = selectedNode.toString();
        if (selectedNode.isLeaf()) {
            if (selectedNodeName == "Unicef") {
                try {
                    java.awt.Desktop.getDesktop().browse(java.net.URI.create("http://www.eb.dk"));
                } catch (IOException e1) {
                    // make a error pop up appear here
                    JOptionPane.showMessageDialog(null, "Something went wrong, please report this to the developer!", "Something went wrong", 0);
                    e1.printStackTrace();

                }
            }

            if (selectedNodeName == "Vectura") {
                try {
                    java.awt.Desktop.getDesktop().browse(java.net.URI.create("http://www.google.com"));
                } catch (IOException e1) {
                    // make a error pop up appear here
                    JOptionPane.showMessageDialog(null, "Something went wrong, please report this to the developer!", "Something went wrong", 0);
                    e1.printStackTrace();

                }
            }

        }

    }
}

It's definitely not a nice way of doing it, since it will lead to A LOT of if statements, but it was simple enough for me to understand by myself.

这绝对不是一个很好的方式,因为它会导致很多if语句,但这对我来说很容易理解。

#1


2  

I don't believe there is a way to make the node itself a URL. However, you could accomplish the same thing by using listeners to open the browser when the node is clicked. To open the browser, you can use the Desktop#browse(URI) method.

我不相信有一种方法可以使节点本身成为URL。但是,您可以通过使用侦听器在单击节点时打开浏览器来完成相同的操作。要打开浏览器,您可以使用Desktop#browse(URI)方法。

On the topic of clicking, I would recommend not opening a URL when a node is single-clicked. For a user that would be very annoying (IMO). Instead, I'd recommend using a double-click. This answer provides a good way to distinguish between a single and double-click, and how to add it to the Tree.

关于点击的主题,我建议不要在单击节点时打开URL。对于非常烦人的用户(IMO)。相反,我建议使用双击。这个答案提供了区分单击和双击以及如何将其添加到树的好方法。


[...] but you gotta start somewhere, right?

[...]但你必须从某个地方开始,对吗?

Exactly! Allow me to give a bit more detail here that will hopefully be helpful for learning a bit more.

究竟!请允许我在此提供更多细节,希望对学习更多有所帮助。

One challenge you will face is how to know which URL to open for a given node. Currently you only know the String that is displayed, and I think it's a safe assumption that you don't want to be displaying a full URL for each node. Instead of creating the DefaultMutableTreeNode objects with a String parameter, I would recommend creating an object to pass in to the DefaultMutableTreeNode constructor instead. This object can also have a URI attribute so it knows which URL to open.

您将面临的一个挑战是如何知道为给定节点打开哪个URL。目前您只知道显示的字符串,我认为您可以安全地假设您不希望为每个节点显示完整的URL。我建议创建一个对象以传递给DefaultMutableTreeNode构造函数,而不是使用String参数创建DefaultMutableTreeNode对象。此对象还可以具有URI属性,以便它知道要打开的URL。

For example:

public class LeafNodeObject {
    private final URI uri;

    public LeafNodeObject(final String display, final URI uri) {
        this.display = display;
        this.uri = uri;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    public void onDoubleClick() {
        try {
            Desktop.getDesktop().browse(uri);
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

}

Nodes can be added like so:

可以像这样添加节点:

node_1.add(new DefaultMutableTreeNode(new LeafNodeObject("Test", new URI("www.eclipse.org"))));

And in our listener (slightly modified from the link above):

在我们的听众中(从上面的链接略微修改):

final MouseListener ml = new MouseAdapter() {
    public void mousePressed(final MouseEvent e) {
        final int selRow = tree.getRowForLocation(e.getX(), e.getY());
        final TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
        if (selRow != -1) {
            final DefaultMutableTreeNode node = (DefaultMutableTreeNode) selPath.getLastPathComponent();
            if (e.getClickCount() == 1) {
                // Single click
            } else if (e.getClickCount() == 2) {
                // Double click
                ((LeafNodeObject) node.getUserObject()).onDoubleClick();
            }
        }
    }
};

Now when there is a double-click event, we call the onDoubleClick() method on the LeafNodeObject that we've created.

现在当有双击事件时,我们在我们创建的LeafNodeObject上调用onDoubleClick()方法。

This looks great, except that we will run into class casting issues with nodes that are not leaf nodes! To fix this, we can make a similar class for those nodes (eg. ParentNodeObject). In the interest of good OOP, we should recognize the common behavior of these two classes (onDoubleClick(), and also an onSingleClick()), and create an interface for the shared ability.

这看起来很棒,除了我们将遇到非叶节点的节点的类转换问题!为了解决这个问题,我们可以为这些节点创建一个类似的类(例如,ParentNodeObject)。为了好的OOP,我们应该认识到这两个类的常见行为(onDoubleClick(),以及onSingleClick()),并为共享能力创建一个接口。

For example:

public interface NodeObject {
    public void onSingleClick();
    public void onDoubleClick();
}

Now we can have our two classes implement this interface:

现在我们可以让我们的两个类实现这个接口:

public class ParentNodeObject implements NodeObject {
    private final String display;

    public ParentNodeObject(final String display) {
        this.display = display;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    @Override
    public void onSingleClick() {
        // Do nothing
    }

    @Override
    public void onDoubleClick() {
        // Do nothing
    }
}

public class LeafNodeObject implements NodeObject {
    private final URI uri;

    public LeafNodeObject(final String display, final URI uri) {
        this.display = display;
        this.uri = uri;
    }

    /**
     * Override so that we control what is display on the Node
     */
    @Override
    public String toString() {
        return display;
    }

    @Override
    public void onSingleClick() {
        // Do nothing
    }

    @Override
    public void onDoubleClick() {
        try {
            Desktop.getDesktop().browse(uri);
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

}

As a result, in our listener we no longer need to know (and no longer care) whether the node is a parent or leaf node. We can simply call either onSingleClick() or onDoubleClick(), and the implementation handles the rest!

因此,在我们的监听器中,我们不再需要知道(并且不再关心)节点是父节点还是叶节点。我们可以简单地调用onSingleClick()或onDoubleClick(),实现处理剩下的!

if (e.getClickCount() == 1) {
    // Single click
    ((NodeObject) node.getUserObject()).onSingleClick();
} else if (e.getClickCount() == 2) {
    // Double click
    ((NodeObject) node.getUserObject()).onDoubleClick();
}

Back in your code, the parent and child nodes can be added like so:

回到代码中,可以添加父节点和子节点,如下所示:

node_1 = new DefaultMutableTreeNode(new ParentNodeObject("Helpdesk Norcic"));
node_1.add(new DefaultMutableTreeNode(new LeafNodeObject("Test", new URI("www.eclipse.org"))));

Now, when you single or double-click any node, either the onSingleClick() or onDoubleClick() methods will also be called. In this case we only care about what happens when there is a double-click on the leaf nodes, so we only need to fill in that method.

现在,当您单击或双击任何节点时,也将调用onSingleClick()或onDoubleClick()方法。在这种情况下,我们只关心在叶子节点上双击时会发生什么,所以我们只需要填写该方法。

#2


0  

While I haven't checked the above mentioned code, here is how I got around the issue while I was waiting for a reply to this thread:

虽然我没有检查上面提到的代码,但是在我等待回复此线程时,我是如何解决这个问题的:

class SelectionListener implements TreeSelectionListener {

    public void valueChanged(TreeSelectionEvent se) {
        JTree tree = (JTree) se.getSource();
        DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
        String selectedNodeName = selectedNode.toString();
        if (selectedNode.isLeaf()) {
            if (selectedNodeName == "Unicef") {
                try {
                    java.awt.Desktop.getDesktop().browse(java.net.URI.create("http://www.eb.dk"));
                } catch (IOException e1) {
                    // make a error pop up appear here
                    JOptionPane.showMessageDialog(null, "Something went wrong, please report this to the developer!", "Something went wrong", 0);
                    e1.printStackTrace();

                }
            }

            if (selectedNodeName == "Vectura") {
                try {
                    java.awt.Desktop.getDesktop().browse(java.net.URI.create("http://www.google.com"));
                } catch (IOException e1) {
                    // make a error pop up appear here
                    JOptionPane.showMessageDialog(null, "Something went wrong, please report this to the developer!", "Something went wrong", 0);
                    e1.printStackTrace();

                }
            }

        }

    }
}

It's definitely not a nice way of doing it, since it will lead to A LOT of if statements, but it was simple enough for me to understand by myself.

这绝对不是一个很好的方式,因为它会导致很多if语句,但这对我来说很容易理解。