JEdi​​torPane 的奇怪行为

Strange behaviour with JEditorPane

我做了一个自定义的 class,它扩展了 JEditorPane 并利用了它的 setPage() 方法。但是我在使用它时遇到了一个非常奇怪的问题。这就是我实现它的方式;

class WebReader extends JEditorPane {

  WebReader(String addressIn) {
    setEditable(false);
    showPage(addressIn)
  }

  void showPage(String address) {    
    try {
      setPage(address);
    } catch (Exception e) {
      e.printStackTrace();
  }
}

调用可能看起来像这样;

WebReader fooReader = new WebReader("https://www.google.com");
fooReader.showPage("https://www.google.comxxxx");

这本不应该起作用但神秘地起作用了。

非常奇怪的是,如果我已经输入了正确的输入,它不会捕获错误的 URL。例如,如果我输入了“https://www.google.com", which works fine (as it should) and after that enter https://www.google.comxxxxx,它仍然会在我的 JEditorPane 上显示 google.com,并且不会引发异常(我希望它这样做)。

值得注意的是,如果我输入 https://www.google.comxxxxx 作为我的 'starting URL',它确实会引发异常。

编辑:添加了更多代码。

我设法修复了它!

我将以下内容添加到我的 showPage() 中:

setEditorKit(createDefaultEditorKit()); 

因此它会在每次更改网页时创建一个新的 EditorKit。

如果页面是异步加载的(在后台),您将不会收到 IOException。 URL 是否异步加载取决于 EditorKit 为您正在加载的内容类型安装的文档。来自 documentation for JEditorPane.setPage:

This may load either synchronously or asynchronously depending upon the document returned by the EditorKit. If the Document is of type AbstractDocument and has a value returned by AbstractDocument.getAsynchronousLoadPriority that is greater than or equal to zero, the page will be loaded on a separate thread using that priority.

If the document is loaded synchronously, it will be filled in with the stream prior to being installed into the editor with a call to setDocument, which is bound and will fire a property change event. If an IOException is thrown the partially loaded document will be discarded and neither the document or page property change events will be fired. If the document is successfully loaded and installed, a view will be built for it by the UI which will then be scrolled if necessary, and then the page property change event will be fired.

If the document is loaded asynchronously, the document will be installed into the editor immediately using a call to setDocument which will fire a document property change event, then a thread will be created which will begin doing the actual loading. In this case, the page property change event will not be fired by the call to this method directly, but rather will be fired when the thread doing the loading has finished. It will also be fired on the event-dispatch thread. Since the calling thread can not throw an IOException in the event of failure on the other thread, the page property change event will be fired when the other thread is done whether the load was successful or not.

显然在这种情况下,网页的 EditorKit 是 HTMLEditorKit。来自 documentation of HTMLEditorKit:

Larger documents involve a lot of parsing and take some time to load. By default, this kit produces documents that will be loaded asynchronously if loaded using JEditorPane.setPage.

解决方法是自己同步加载文档,而不是使用 JEditorPane.setPage:

Document doc;

URLConnection connection = new URL(url).openConnection();
try (InputStream stream = connection.getInputStream()) {
    String contentType = connection.getContentType();
    EditorKit editorKit =
        JEditorPane.createEditorKitForContentType(contentType);
    doc = editorKit.createDefaultDocument();
    editorKit.read(stream, doc, 0);
}

pane.setDocument(doc);