在可编辑的EditorPane上添加HyperlinkListener

时间:2015-08-09 16:55:05

标签: java swing hyperlink jeditorpane

JEditorPane editorPane;
editorPane.addHyperlinkListener(...)
如果editorPane设置为setEditable(true)

被触发。

因此,我尝试使用editable动态修改FocusListener属性:

public void createGui() {
    SwingUtilities.invokeLater(() -> {
        JEditorPane editorPane = new JEditorPane();
        editorPane.setContentType("text/html");
        editorPane.setText("<a href='http://google.com'>click me!</a>");

        editorPane.setEditable(false);

        editorPane.addFocusListener(new FocusListener() {
            @Override public void focusGained(FocusEvent e) {
                editorPane.setEditable(true); // <-- EDITABLE WHILE FOCUSSED
            }
            @Override public void focusLost(FocusEvent e) {
                editorPane.setEditable(false); // <-- NON-EDITABLE WHILE NON-FOCUSSED
            }
        });

        editorPane.addHyperlinkListener(new HyperlinkListener() {
            @Override public void hyperlinkUpdate(HyperlinkEvent e) {
                System.out.println(e.getEventType().toString());
            }
        });

        JButton someOtherSwingElement = new JButton("click me to remove focus from editorPane");
        JFrame jf = new JFrame();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel jp = new JPanel(new GridLayout());
        jp.add(editorPane);
        jp.add(someOtherSwingElement);
        jf.add(jp);
        jf.pack();
        jf.setVisible(true);
    }
}

但似乎focusGained()的内容总是hyperlinkUpdate之前执行,无论前者需要多长时间。为什么呢?

有什么方法可以解决这个问题?

2 个答案:

答案 0 :(得分:1)

来自editable JEditorPane hypelinks

import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;

public class URLEditorPane {

    public static String HTML="<html>\n" +
            "<body>\n" +
            "Click on the link in the editale JEditorPane <br>\n" +
//            "<a href=\"http://java.sun.com\">\nlink</a>" +
            "<a href=\"file:///c:/temp/test.html\">\nlink</a>" +
            "</body>\n" +
            "</html>";  

    boolean isNeedCursorChange=true;
    JTextPane edit=new JTextPane() {
        public void setCursor(Cursor cursor) {
            if (isNeedCursorChange) {
                super.setCursor(cursor);
            }
        }
    };

    public URLEditorPane() {
        JFrame frame=new JFrame("Click on Links in editable JEditorPane");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(edit);
        MyHTMLEditorKit kit=new MyHTMLEditorKit();
//        HTMLEditorKit kit=new HTMLEditorKit();

        edit.setEditorKit(kit);
//        edit.setEditable(false);

        edit.setText(HTML);
        edit.addHyperlinkListener(new HTMLListener());
        frame.setSize(500,300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        new URLEditorPane();
    }

    private class HTMLListener implements HyperlinkListener {
      public void hyperlinkUpdate(HyperlinkEvent e) {
        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            try {
                edit.setPage(e.getURL());
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
      }
    }

    public class MyHTMLEditorKit extends HTMLEditorKit {

        MyLinkController handler=new MyLinkController();
        public void install(JEditorPane c) {
            MouseListener[] oldMouseListeners=c.getMouseListeners();
            MouseMotionListener[] oldMouseMotionListeners=c.getMouseMotionListeners();
            super.install(c);
            //the following code removes link handler added by original
            //HTMLEditorKit

            for (MouseListener l: c.getMouseListeners()) {
                c.removeMouseListener(l);
            }
            for (MouseListener l: oldMouseListeners) {
                c.addMouseListener(l);
            }

            for (MouseMotionListener l: c.getMouseMotionListeners()) {
                c.removeMouseMotionListener(l);
            }
            for (MouseMotionListener l: oldMouseMotionListeners) {
                c.addMouseMotionListener(l);
            }

            //add out link handler instead of removed one
            c.addMouseListener(handler);
            c.addMouseMotionListener(handler);
        }

        public class MyLinkController extends LinkController {

            public void mouseClicked(MouseEvent e) {
                JEditorPane editor = (JEditorPane) e.getSource();

                if (editor.isEditable() && SwingUtilities.isLeftMouseButton(e)) {
                    if (e.getClickCount()==2) {
                        editor.setEditable(false);
                        super.mouseClicked(e);
                        editor.setEditable(true);
                    }
                }

            }
            public void mouseMoved(MouseEvent e) {
                JEditorPane editor = (JEditorPane) e.getSource();

                if (editor.isEditable()) {
                    isNeedCursorChange=false;
                    editor.setEditable(false);
                    isNeedCursorChange=true;
                    super.mouseMoved(e);
                    isNeedCursorChange=false;
                    editor.setEditable(true);
                    isNeedCursorChange=true;
                }
            }

        }
    }
}

答案 1 :(得分:0)

HyperlinkListener似乎依赖于FocusListener,因此无法绕过setEditable(false)条件。

最笨拙但最简单的解决方法可能是将editorPane.setEditable(true);置于其自己的线程中并在其前面进行睡眠,因此HyperlinkListener有足够的时间来触发其HyperlinkUpdate方法。我发现即使100毫秒仍然不够,所以我把它设置为250毫秒。这意味着,点击editorPane后,您将无法修改其内容250毫秒。这不是一个好的编程风格,但完全可用且易于理解。代码示例:

editorPane.addFocusListener(new FocusListener() {
    @Override public void focusGained(FocusEvent e) {
        new Thread(() -> {
            try {
                Thread.sleep(250);
            } catch (InterruptedException e1) {}
            editorPane.setEditable(true);
        }).start();
    }
    @Override   public void focusLost(FocusEvent e) {
        editorPane.setEditable(false);
    }
});

Here是Stanislav Lapitsky的另一种方法。不过,它说的是

  

我们所需要的只是调用setEditable(false)并调用super方法然后恢复可编辑状态。

,所以即使这不是真正的“解决方案”,而是一种解决方法

我很高兴听到别人对此的看法。