如何使 TreeSelectionListener 在 JEditorPane 上作为 Hyperlinklistener 工作?
How to make TreeSelectionListener work as a Hyperlinklistener on an JEditorPane?
我正在处理一个帮助 window,我在其中使用 jsoup 将 HTML 文件解析为 JSplitPane 右侧的 JEditorPane。 HTML 文件有一个内容页面,我成功地使用了 HyperlinkListener。 JSplitPane 的右侧是一个 JTree,我在其中进一步提取了 HTML 中的所有 link 标记。我开始编写一个 TreeListener,但我不知道如何在 TreeListener 内的 JEditorPane 上触发 HyperlinkEvent。我有一个地图 (linkMap) 填充了 link 的格式化 text value
(显示值作为字符串)作为键和包含 link 的 Jsoup 元素作为价值。如何触发 HyperlinkEvent,然后在单击 TreeNode 时跳转到 JEditorPane 右侧的页面?
这是我的测试项目中唯一的 class。如果您使用包含 hyperlinks 的任何本地 html 路径初始化 String url,您将看到树将显示它们,右侧的 JEditorPane 将使它们可点击并且已经有一个HyperlinkListener 已正确实施。我希望树的行为方式与 EditorPane 中的 Hyperlinks 相同。
package yur.html;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BoxLayout;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class HTMLDisplayer
{
private String url = ""; // local html-file path
private JFrame frame;
private JSplitPane splitPane;
private JPanel leftPanel;
private JScrollPane leftScrollPane;
private JScrollPane rightScrollPane;
private JEditorPane editorPane;
private JTree tree;
private JLabel descriptionLabel;
private DefaultMutableTreeNode root;
private DefaultMutableTreeNode lastChapterNode;
private Map<String, Element> linkMap;
public HTMLDisplayer()
{
initGui();
}
private HyperlinkListener helpContentsHyperlinkListener = new HyperlinkListener()
{
@Override
public void hyperlinkUpdate(HyperlinkEvent ev)
{
if(ev.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
{
JEditorPane pane = (JEditorPane) ev.getSource();
if(ev instanceof HTMLFrameHyperlinkEvent)
{
HTMLFrameHyperlinkEvent event = (HTMLFrameHyperlinkEvent) ev;
HTMLDocument doc = (HTMLDocument) pane.getDocument();
doc.processHTMLFrameHyperlinkEvent(event);
}
else
{
try
{
pane.setPage(ev.getURL());
}
catch(Throwable t)
{
t.printStackTrace();
}
}
}
}
};
public HyperlinkListener getHelpContentsHyperlinkListener()
{
return helpContentsHyperlinkListener;
}
public void setHelpContentsHyperlinkListener(HyperlinkListener helpContentsHyperlinkListener)
{
this.helpContentsHyperlinkListener = helpContentsHyperlinkListener;
}
public static void main(String[] args) throws Exception
{
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch(Exception e)
{
e.printStackTrace();
}
HTMLDisplayer htmlDisplayer = new HTMLDisplayer();
htmlDisplayer.setVisible();
}
private void initGui()
{
initRightPane();
initLeftPane();
initSplitPane();
initFrame();
}
private void initRightPane()
{
try
{
editorPane = new JEditorPane(url);
}
catch(IOException e)
{
e.printStackTrace();
}
editorPane.addHyperlinkListener(getHelpContentsHyperlinkListener());
editorPane.setEditable(false);
editorPane.setFocusable(false);
rightScrollPane = new JScrollPane(editorPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
private void initLeftPane()
{
descriptionLabel = new JLabel("Hilfe");
descriptionLabel.setBorder(new EmptyBorder(5, 0, 5, 0));
leftPanel = new JPanel();
leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
initTree();
leftScrollPane = new JScrollPane(tree, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
leftPanel.add(descriptionLabel);
leftPanel.add(leftScrollPane);
}
private void initTree()
{
Elements links = getContents();
initNodes(links);
tree = new JTree(root);
addTreeSelectionListener(tree);
}
private static Elements getContents()
{
File file = new File("C:\\SoftwareEntwicklung\\yur.poi\\Hilfe_VR_FESAe.htm");
try
{
return parseLinksFromFile(file);
}
catch(IOException e)
{
e.printStackTrace();
}
return null;
}
private static Elements parseLinksFromFile(File file) throws IOException
{
Document doc = Jsoup.parse(file, "windows-1252", "");
Elements links = doc.select("a[href]");
return links;
}
private void initNodes(Elements links)
{
root = new DefaultMutableTreeNode("Inhaltsverzeichnis");
createNodes(links);
}
private void createNodes(Elements links)
{
linkMap = new HashMap<>();
for(Element link : links)
{
createNode(link);
}
}
private void createNode(Element link)
{
String uneditedText = link.text();
String[] splitText = uneditedText.split(" ");
String text = splitText[0];
text = text.replaceAll("[^a-zA-Z0-9äÄöÖüÜ *]+", "");
Pattern pattern = Pattern.compile(".*[0-9]+$");
Matcher matcher = pattern.matcher(text);
text = text.substring(0, text.length() - 2);
boolean isSpace = Character.isWhitespace(text.charAt(text.length() - 1));
if(isSpace)
{
text = text.substring(0, text.length() - 1);
}
linkMap.put(text, link);
if(matcher.matches())
{
if(!text.contains(" "))
{
DefaultMutableTreeNode chapterNode = new DefaultMutableTreeNode(text);
root.add(chapterNode);
lastChapterNode = chapterNode;
}
else
{
DefaultMutableTreeNode node = new DefaultMutableTreeNode(text);
lastChapterNode.add(node);
}
}
}
private void addTreeSelectionListener(JTree tree)
{
tree.addTreeSelectionListener(new TreeSelectionListener()
{
@Override
public void valueChanged(TreeSelectionEvent ev)
{
DefaultMutableTreeNode lastSelectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if(lastSelectedNode == null)
{
return;
}
Object nodeInfo = lastSelectedNode.getUserObject();
for(Map.Entry<String, Element> entry : linkMap.entrySet())
{
if(entry.getKey().equals(nodeInfo))
{
// TODO
System.out.println(entry.getKey());
}
}
}
});
}
private void initSplitPane()
{
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setLeftComponent(leftPanel);
splitPane.setRightComponent(rightScrollPane);
}
private void initFrame()
{
frame = new JFrame("Hilfe");
frame.getContentPane().add(splitPane, BorderLayout.CENTER);
frame.setSize(new Dimension(1450, 1000));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void setVisible()
{
frame.setVisible(true);
}
}
我尝试使用 editorPane.setPage()
方法和 Jsoup 方法从 link 元素中获取 a href
标签。这会触发一个
MalformedURLException: no protocol: #_Toc513013684
(#_Toc513013684是a href标签中的值)。
我也准备接受我完全错误的方式,所以如果您有不同的方式实现我的目标,请随时留下您的建议!
我自己找到了答案。我想得太远了,注意到 TreeListener 和 HyperlinkListener 不必互相调用。我注意到抛出 "MalformedURLException" 的 "a href" 标签中的值是实际 url 的一部分,它只是缺少第一部分,即实际文件路径。所以我所要做的就是将 href 标记与我的 url 字符串连接起来,并将其用作 JEditorPane.setPage() 的参数。只是为了最终完成这个问题,这里是我的 TreeSelectionListener 的代码:
private void addTreeSelectionListener(JTree tree)
{
tree.addTreeSelectionListener(new TreeSelectionListener()
{
@Override
public void valueChanged(TreeSelectionEvent ev)
{
DefaultMutableTreeNode lastSelectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if(lastSelectedNode == null)
{
return;
}
Object nodeInfo = lastSelectedNode.getUserObject();
for(Map.Entry<String, Element> entry : linkMap.entrySet())
{
if(entry.getKey().equals(nodeInfo))
{
String href = entry.getValue().attr("href");
String link = url.concat(href);
try
{
editorPane.setPage(link);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
});
}
我正在处理一个帮助 window,我在其中使用 jsoup 将 HTML 文件解析为 JSplitPane 右侧的 JEditorPane。 HTML 文件有一个内容页面,我成功地使用了 HyperlinkListener。 JSplitPane 的右侧是一个 JTree,我在其中进一步提取了 HTML 中的所有 link 标记。我开始编写一个 TreeListener,但我不知道如何在 TreeListener 内的 JEditorPane 上触发 HyperlinkEvent。我有一个地图 (linkMap) 填充了 link 的格式化 text value
(显示值作为字符串)作为键和包含 link 的 Jsoup 元素作为价值。如何触发 HyperlinkEvent,然后在单击 TreeNode 时跳转到 JEditorPane 右侧的页面?
这是我的测试项目中唯一的 class。如果您使用包含 hyperlinks 的任何本地 html 路径初始化 String url,您将看到树将显示它们,右侧的 JEditorPane 将使它们可点击并且已经有一个HyperlinkListener 已正确实施。我希望树的行为方式与 EditorPane 中的 Hyperlinks 相同。
package yur.html;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.BoxLayout;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLFrameHyperlinkEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class HTMLDisplayer
{
private String url = ""; // local html-file path
private JFrame frame;
private JSplitPane splitPane;
private JPanel leftPanel;
private JScrollPane leftScrollPane;
private JScrollPane rightScrollPane;
private JEditorPane editorPane;
private JTree tree;
private JLabel descriptionLabel;
private DefaultMutableTreeNode root;
private DefaultMutableTreeNode lastChapterNode;
private Map<String, Element> linkMap;
public HTMLDisplayer()
{
initGui();
}
private HyperlinkListener helpContentsHyperlinkListener = new HyperlinkListener()
{
@Override
public void hyperlinkUpdate(HyperlinkEvent ev)
{
if(ev.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
{
JEditorPane pane = (JEditorPane) ev.getSource();
if(ev instanceof HTMLFrameHyperlinkEvent)
{
HTMLFrameHyperlinkEvent event = (HTMLFrameHyperlinkEvent) ev;
HTMLDocument doc = (HTMLDocument) pane.getDocument();
doc.processHTMLFrameHyperlinkEvent(event);
}
else
{
try
{
pane.setPage(ev.getURL());
}
catch(Throwable t)
{
t.printStackTrace();
}
}
}
}
};
public HyperlinkListener getHelpContentsHyperlinkListener()
{
return helpContentsHyperlinkListener;
}
public void setHelpContentsHyperlinkListener(HyperlinkListener helpContentsHyperlinkListener)
{
this.helpContentsHyperlinkListener = helpContentsHyperlinkListener;
}
public static void main(String[] args) throws Exception
{
try
{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
}
catch(Exception e)
{
e.printStackTrace();
}
HTMLDisplayer htmlDisplayer = new HTMLDisplayer();
htmlDisplayer.setVisible();
}
private void initGui()
{
initRightPane();
initLeftPane();
initSplitPane();
initFrame();
}
private void initRightPane()
{
try
{
editorPane = new JEditorPane(url);
}
catch(IOException e)
{
e.printStackTrace();
}
editorPane.addHyperlinkListener(getHelpContentsHyperlinkListener());
editorPane.setEditable(false);
editorPane.setFocusable(false);
rightScrollPane = new JScrollPane(editorPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
private void initLeftPane()
{
descriptionLabel = new JLabel("Hilfe");
descriptionLabel.setBorder(new EmptyBorder(5, 0, 5, 0));
leftPanel = new JPanel();
leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));
initTree();
leftScrollPane = new JScrollPane(tree, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
leftPanel.add(descriptionLabel);
leftPanel.add(leftScrollPane);
}
private void initTree()
{
Elements links = getContents();
initNodes(links);
tree = new JTree(root);
addTreeSelectionListener(tree);
}
private static Elements getContents()
{
File file = new File("C:\\SoftwareEntwicklung\\yur.poi\\Hilfe_VR_FESAe.htm");
try
{
return parseLinksFromFile(file);
}
catch(IOException e)
{
e.printStackTrace();
}
return null;
}
private static Elements parseLinksFromFile(File file) throws IOException
{
Document doc = Jsoup.parse(file, "windows-1252", "");
Elements links = doc.select("a[href]");
return links;
}
private void initNodes(Elements links)
{
root = new DefaultMutableTreeNode("Inhaltsverzeichnis");
createNodes(links);
}
private void createNodes(Elements links)
{
linkMap = new HashMap<>();
for(Element link : links)
{
createNode(link);
}
}
private void createNode(Element link)
{
String uneditedText = link.text();
String[] splitText = uneditedText.split(" ");
String text = splitText[0];
text = text.replaceAll("[^a-zA-Z0-9äÄöÖüÜ *]+", "");
Pattern pattern = Pattern.compile(".*[0-9]+$");
Matcher matcher = pattern.matcher(text);
text = text.substring(0, text.length() - 2);
boolean isSpace = Character.isWhitespace(text.charAt(text.length() - 1));
if(isSpace)
{
text = text.substring(0, text.length() - 1);
}
linkMap.put(text, link);
if(matcher.matches())
{
if(!text.contains(" "))
{
DefaultMutableTreeNode chapterNode = new DefaultMutableTreeNode(text);
root.add(chapterNode);
lastChapterNode = chapterNode;
}
else
{
DefaultMutableTreeNode node = new DefaultMutableTreeNode(text);
lastChapterNode.add(node);
}
}
}
private void addTreeSelectionListener(JTree tree)
{
tree.addTreeSelectionListener(new TreeSelectionListener()
{
@Override
public void valueChanged(TreeSelectionEvent ev)
{
DefaultMutableTreeNode lastSelectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if(lastSelectedNode == null)
{
return;
}
Object nodeInfo = lastSelectedNode.getUserObject();
for(Map.Entry<String, Element> entry : linkMap.entrySet())
{
if(entry.getKey().equals(nodeInfo))
{
// TODO
System.out.println(entry.getKey());
}
}
}
});
}
private void initSplitPane()
{
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setLeftComponent(leftPanel);
splitPane.setRightComponent(rightScrollPane);
}
private void initFrame()
{
frame = new JFrame("Hilfe");
frame.getContentPane().add(splitPane, BorderLayout.CENTER);
frame.setSize(new Dimension(1450, 1000));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void setVisible()
{
frame.setVisible(true);
}
}
我尝试使用 editorPane.setPage()
方法和 Jsoup 方法从 link 元素中获取 a href
标签。这会触发一个
MalformedURLException: no protocol: #_Toc513013684
(#_Toc513013684是a href标签中的值)。
我也准备接受我完全错误的方式,所以如果您有不同的方式实现我的目标,请随时留下您的建议!
我自己找到了答案。我想得太远了,注意到 TreeListener 和 HyperlinkListener 不必互相调用。我注意到抛出 "MalformedURLException" 的 "a href" 标签中的值是实际 url 的一部分,它只是缺少第一部分,即实际文件路径。所以我所要做的就是将 href 标记与我的 url 字符串连接起来,并将其用作 JEditorPane.setPage() 的参数。只是为了最终完成这个问题,这里是我的 TreeSelectionListener 的代码:
private void addTreeSelectionListener(JTree tree)
{
tree.addTreeSelectionListener(new TreeSelectionListener()
{
@Override
public void valueChanged(TreeSelectionEvent ev)
{
DefaultMutableTreeNode lastSelectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if(lastSelectedNode == null)
{
return;
}
Object nodeInfo = lastSelectedNode.getUserObject();
for(Map.Entry<String, Element> entry : linkMap.entrySet())
{
if(entry.getKey().equals(nodeInfo))
{
String href = entry.getValue().attr("href");
String link = url.concat(href);
try
{
editorPane.setPage(link);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
});
}