我正在使用 和 实现一个简单的 HTML
编辑JTextPane
器。代码如下所示:HTMLDocument
HTMLEditorKit
public class SimpleHTMLEditor extends JFrame {
private static final long serialVersionUID = 1L;
private final JTextPane textPane;
private final HTMLEditorKit edtKit;
private HTMLDocument doc;
public static void main(String[] args) {
final SimpleHTMLEditor editor = new SimpleHTMLEditor();
editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
editor.setVisible(true);
}
public SimpleHTMLEditor() {
super("Simple HTML Editor");
textPane = new JTextPane();
edtKit = new HTMLEditorKit();
textPane.setEditorKit(edtKit);
doc = new HTMLDocument();
textPane.setDocument(doc);
final Container content = getContentPane();
content.add(textPane, BorderLayout.CENTER);
content.add(createToolBar(), BorderLayout.NORTH);
setJMenuBar(createMenuBar());
setSize(500, 240);
textPane.requestFocusInWindow();
}
/**
* Creates the toolbar with the combo box that allows for creation and
* use of different conditions with their respective presentation styles.
* @return The toolbar
*/
private JToolBar createToolBar() {
final JToolBar bar = new JToolBar();
return bar;
}
/**
* Creates the menu bar. It contains:
* <li> Actions to read/write HTML file
* <li> Action to display the HTML source in a popup window.
* @return The menu bar
*/
private JMenuBar createMenuBar() {
final JMenuBar menubar = new JMenuBar();
final JMenu mnuFile = new JMenu("File");
menubar.add(mnuFile);
final SaveAction actSave = new SaveAction();
mnuFile.add(actSave);
final LoadAction actLoad = new LoadAction();
mnuFile.add(actLoad);
final JMenuItem mnuPreview = new JMenuItem("Preview");
menubar.add(mnuPreview);
mnuPreview.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
final HTMLPreview previewer = new HTMLPreview(SimpleHTMLEditor.this,
getDocSource());
previewer.setVisible(true);
}
});
return menubar;
}
/**
* Helper method to extract the HTML source code from the HTML document
* @return The HTML source code
*/
private String getDocSource() {
final StringWriter sw = new StringWriter();
try {
edtKit.write(sw, doc, 0, doc.getLength());
} catch (IOException | BadLocationException e1) {
e1.printStackTrace();
}
try {
sw.close();
} catch (final IOException e1) {
e1.printStackTrace();
}
return sw.toString();
}
class SaveAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public SaveAction() {
super("Save to File");
}
@Override
public void actionPerformed(ActionEvent ev) {
final JFileChooser chooser = new JFileChooser();
if (chooser.showSaveDialog(SimpleHTMLEditor.this) != JFileChooser.APPROVE_OPTION)
return;
final File targetFile = chooser.getSelectedFile();
if (targetFile == null)
return;
FileWriter writer = null;
try {
writer = new FileWriter(targetFile);
textPane.write(writer);
} catch (final IOException ex) {
JOptionPane.showMessageDialog(SimpleHTMLEditor.this,
"File Not Saved", "ERROR",
JOptionPane.ERROR_MESSAGE);
} finally {
if (writer != null) {
try {
writer.close();
} catch (final IOException x) {
}
}
}
}
}
class LoadAction extends AbstractAction {
private static final long serialVersionUID = 1L;
public LoadAction() {
super("Load from File");
}
@Override
public void actionPerformed(ActionEvent ev) {
final JFileChooser chooser = new JFileChooser();
if (chooser.showOpenDialog(SimpleHTMLEditor.this) != JFileChooser.APPROVE_OPTION)
return;
final File sourceFile = chooser.getSelectedFile();
if (sourceFile == null)
return;
FileReader reader = null;
try {
reader = new FileReader(sourceFile);
doc = (HTMLDocument)edtKit.createDefaultDocument();
textPane.setDocument(doc);
edtKit.read(reader,doc,0);
} catch (final IOException ex) {
JOptionPane.showMessageDialog(SimpleHTMLEditor.this,
"File '" + sourceFile.getAbsolutePath() +
"' Not Loaded", "ERROR",
JOptionPane.ERROR_MESSAGE);
} catch (final BadLocationException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (final IOException x) {
}
}
}
}
}
}
/**
* Popup window for display of the current contents of the editor as HTML
* source code.
*/
class HTMLPreview extends JDialog {
private static final long serialVersionUID = 1L;
public HTMLPreview(JFrame parent, String source) {
super(parent, "HTML Source", true);
final JPanel pp = new JPanel(new BorderLayout());
pp.setBorder(new EmptyBorder(10, 10, 5, 10));
final JTextArea srcTxtArea = new JTextArea(source, 20, 60);
srcTxtArea.setFont(new Font("Courier", Font.PLAIN, 12));
final JScrollPane sp = new JScrollPane(srcTxtArea);
pp.add(sp, BorderLayout.CENTER);
final JPanel p = new JPanel(new FlowLayout());
final JPanel p1 = new JPanel(new GridLayout(1, 2, 10, 0));
final JButton closeBtn = new JButton("Close");
closeBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
p1.add(closeBtn);
p.add(p1);
pp.add(p, BorderLayout.SOUTH);
getContentPane().add(pp, BorderLayout.CENTER);
pack();
setResizable(true);
setLocationRelativeTo(parent);
}
}
我注意到,当我加载一个包含嵌套 SPAN 元素的 HTML 文件时,嵌套会被静默删除。这是一个示例 HTML 文件:
<html>
<head>
</head>
<body>
<p>
<span title="tag:one">Outer span. <span title="tag:two">Inner span.</span> Outer span continued.</span>
</p>
</body>
</html>
加载该文件并在工具栏中选择“预览”操作后,我会看到一个显示 HTML 源代码的弹出窗口,如下所示:
<html>
<head>
</head>
<body>
<p>
<span title="tag:one">Outer span.</span> <span title="tag:two">Inner
span.</span> <span title="tag:one"> Outer span continued.</span>
</p>
</body>
</html>
可以看出,外部 SPAN 元素被无声地分成两个 SPAN 元素,内部 SPAN 元素放置在两者之间。在我看来,这种行为显示了实现 HTML 编辑器的 Java Swing 组件与据我所知允许嵌套 SPAN 元素的 HTML 4.x 标准之间的不兼容之一。我现在的问题是:是否有一种(希望不会太复杂)方法来解决或克服该限制,即让 HTML 编辑器保留它在阅读 HTML 文本时遇到的嵌套 SPAN 元素?
非常感谢提前,apatwork。