如何让JEditorPane中嵌套div横向溢出并仅显示自身水平滚动条?
实现带局部水平滚动的JEditorPane
完全可以实现,虽然Swing的JEditorPane仅原生支持HTML 3.2,不支持overflow-x: auto这类CSS样式,但通过自定义ViewFactory和View,能为指定嵌套div添加独立的水平滚动条,其余内容保持自动换行。
核心思路
- 给需要滚动的目标div标记特定标识(比如自定义class属性
scroll-x),方便后续识别 - 自定义
ViewFactory,在解析HTML时,识别目标div并替换为嵌套了JScrollPane的自定义View - 让自定义
View负责将div内容渲染在滚动面板中,同时保留其余内容的正常布局逻辑
代码实现示例
1. 自定义ViewFactory与View类
import javax.swing.*; import javax.swing.text.*; import javax.swing.text.html.*; import java.awt.*; public class ScrollableDivFactory extends HTMLEditorKit.HTMLFactory { @Override public View create(Element elem) { AttributeSet attrs = elem.getAttributes(); Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute); Object o = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute); // 识别带有scroll-x class的div元素 if (o instanceof HTML.Tag && o == HTML.Tag.DIV) { String classAttr = (String) attrs.getAttribute(HTML.Attribute.CLASS); if ("scroll-x".equals(classAttr)) { return new ScrollDivView(elem); } } return super.create(elem); } // 自定义View,将内容包装在JScrollPane中 private static class ScrollDivView extends CompositeView { private final JScrollPane scrollPane; private View contentView; public ScrollDivView(Element elem) { super(elem); scrollPane = new JScrollPane(); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); // 获取div的固定宽度(从HTML的width属性读取) String widthStr = (String) elem.getAttributes().getAttribute(HTML.Attribute.WIDTH); if (widthStr != null) { int width = Integer.parseInt(widthStr.replace("px", "")); scrollPane.setPreferredSize(new Dimension(width, scrollPane.getPreferredSize().height)); scrollPane.setMaximumSize(new Dimension(width, Integer.MAX_VALUE)); } } @Override protected void loadChildren(ViewFactory f) { super.loadChildren(f); if (getViewCount() > 0) { contentView = getView(0); // 将内容View包装在JViewport中 JPanel panel = new JPanel(new BorderLayout()); panel.add(new ViewComponent(contentView)); scrollPane.setViewportView(panel); } } @Override public float getPreferredSpan(int axis) { if (axis == View.X_AXIS) { return scrollPane.getPreferredSize().width; } else { return scrollPane.getPreferredSize().height; } } @Override public void paint(Graphics g, Shape allocation) { Rectangle bounds = allocation.getBounds(); scrollPane.setBounds(bounds); scrollPane.paint(g); } // 用于将View渲染到Swing组件的辅助类 private static class ViewComponent extends JComponent { private final View view; public ViewComponent(View view) { this.view = view; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; Shape oldClip = g2.getClip(); g2.clipRect(0, 0, getWidth(), getHeight()); view.paint(g2, getBounds()); g2.setClip(oldClip); } @Override public Dimension getPreferredSize() { return new Dimension( (int) view.getPreferredSpan(View.X_AXIS), (int) view.getPreferredSpan(View.Y_AXIS) ); } } } }
2. 使用示例
import javax.swing.*; import javax.swing.text.html.HTMLEditorKit; import java.awt.*; public class LocalScrollEditorPane extends JFrame { public LocalScrollEditorPane() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(600, 400); setLocationRelativeTo(null); JEditorPane editorPane = new JEditorPane(); editorPane.setEditable(false); editorPane.setContentType("text/html"); // 设置自定义ViewFactory HTMLEditorKit kit = new HTMLEditorKit(); kit.setViewFactory(new ScrollableDivFactory()); editorPane.setEditorKit(kit); // 测试HTML内容:目标div带scroll-x class和固定宽度,内部放超宽表格 String htmlContent = """ <html> <body> <p>这是一段普通文本,会自动换行适应JEditorPane的宽度:</p> <p>测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试</p> <div class="scroll-x" width="300"> <table border="1"> <tr> <td>列1</td><td>列2</td><td>列3</td><td>列4</td><td>列5</td><td>列6</td><td>列7</td> </tr> <tr> <td>内容1</td><td>内容2</td><td>内容3</td><td>内容4</td><td>内容5</td><td>内容6</td><td>内容7</td> </tr> </table> </div> <p>这是div下方的普通文本,同样会自动换行:</p> <p>测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试</p> </body> </html> """; editorPane.setText(htmlContent); add(editorPane); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new LocalScrollEditorPane().setVisible(true)); } }
关键细节说明
- 目标div通过
class="scroll-x"和width属性标记,自定义ViewFactory会识别并替换为带滚动条的View - 自定义
ScrollDivView将div内容包装在JScrollPane中,仅启用水平滚动条,垂直滚动条禁用 - 其余HTML元素仍使用默认View渲染,保持自动换行的原有逻辑
JEditorPane设置为editable=false,确保仅用于显示
内容的提问来源于stack exchange,提问作者bric3
相关产品推荐
相关产品推荐

