Java JTree 节点是可点击的 URL link

Java JTree node is a clickable URL link

我正在尝试使节点成为可点击的 URL,但我似乎无法弄清楚该怎么做。

我找遍了高处和低处,似乎找不到解决办法。

这是我的代码:

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);
    }
}

抱歉我有点菜鸟,但你总得从某个地方开始吧?

如果有人能指出我正确的方向,我将不胜感激!大家晚上好!

我认为没有办法让节点本身成为 URL。但是,您可以通过在单击节点时使用侦听器打开浏览器来完成同样的事情。要打开浏览器,可以使用Desktop#browse(URI) 方法。

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


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

没错!请允许我在这里提供更多细节,希望对学习更多有所帮助。

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

例如:

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();
        }
    }

}

可以这样添加节点:

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

并且在我们的监听器中(从上面的 link 稍微修改):

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();
            }
        }
    }
};

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

这看起来不错,除了我们将 运行 转换为非叶节点的节点的 class 转换问题!为了解决这个问题,我们可以为这些节点创建一个类似的 class(例如 ParentNodeObject)。为了良好的 OOP,我们应该认识到这两个 classes(onDoubleClick(),还有一个 onSingleClick())的共同行为,并为共享能力创建一个接口。

例如:

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

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

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();
        }
    }

}

因此,在我们的侦听器中,我们不再需要知道(也不再关心)该节点是父节点还是叶节点。我们可以简单地调用 onSingleClick()onDoubleClick(),其余的由实现来处理!

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

返回您的代码,可以像这样添加父节点和子节点:

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

现在,当您单击或双击任何节点时,也会调用 onSingleClick()onDoubleClick() 方法。在本例中我们只关心双击叶子节点时会发生什么,所以我们只需要填写那个方法即可。

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

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();

                }
            }

        }

    }
}

这绝对不是一个好方法,因为它会导致很多 if 语句,但它很简单,我自己就能理解。