使用Java 7在JTextPane中使用样式文本进行奇怪的文本包装

我有两个不同的编辑器使用JTextPane和Java 7中的奇怪错误,这些错误在以前的JVM版本中没有出现。 它出现在包含样式文本或组件的长行中。

这是一个演示此错误的示例。 在此示例中,每次插入字符时,都会对所有文本应用默认样式。 我用JDK 1.7.0_04测试了它。

import java.awt.BorderLayout; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class BugWrapJava7 extends JFrame { JTextPane jtp; StyledDocument doc; public BugWrapJava7() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); jtp = new JTextPane(); add(jtp, BorderLayout.CENTER); jtp.setText("\ntype some text in the above empty line and check the wrapping behavior"); doc = jtp.getStyledDocument(); doc.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { insert(); } public void removeUpdate(DocumentEvent e) { } public void changedUpdate(DocumentEvent e) { } }); setSize(200, 200); setVisible(true); } public void insert() { SwingUtilities.invokeLater(new Runnable() { public void run() { Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE); doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false); } }); } public static void main(String[] args) { new BugWrapJava7(); } } 

我的问题是:我的代码中有什么问题,或者它确实是Java 7中引入的新bug? 如果它是一个新的JVM错误,是否有解决方法?

它可能与问题8666727有关 ,但问题在于错误的包装而不是滚动条的外观。

对于期货读者, JDK 1.7.0_04中仍然存在bug。 ,

比较Java7和稳定的Java6,

在此处输入图像描述 <------ Java7 vs Java6 ---> 在此处输入图像描述

在此处输入图像描述 <------ Java7 vs Java6 ---> 在此处输入图像描述

在此处输入图像描述 <------ Java7 vs Java6 ---> 在此处输入图像描述

在此处输入图像描述 <------ Java7 vs Java6 ---> 在此处输入图像描述

来自代码

 import java.awt.Dimension; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class BugWrapJava7 { private JFrame frame = new JFrame(); private JTextPane jtp; private StyledDocument doc; public BugWrapJava7() { jtp = new JTextPane(); jtp.setText("\ntype some text in the above empty line and check the wrapping behavior"); doc = jtp.getStyledDocument(); doc.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { insert(); } public void removeUpdate(DocumentEvent e) { insert(); } public void changedUpdate(DocumentEvent e) { insert(); } public void insert() { SwingUtilities.invokeLater(new Runnable() { public void run() { Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE); doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false); } }); } }); JScrollPane scroll = new JScrollPane(jtp); scroll.setPreferredSize(new Dimension(200, 200)); frame.add(scroll); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BugWrapJava7 bugWrapJava7 = new BugWrapJava7(); } }); } } 

调查了这个。 原因是breakSpots缓存。 看起来像LabelView存储它们并且不会重新计算previos文本编辑的偏移量。 如果我手动重置它们,则不会发生错误。

下面是一个解决方法(由于私有breakSpots字段而非常脏)

 import java.awt.Dimension; import java.lang.reflect.Field; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; public class BugWrapJava7 { private JFrame frame = new JFrame(); private JTextPane jtp; private StyledDocument doc; public BugWrapJava7() { jtp = new JTextPane(); jtp.setEditorKit(new MyStyledEditorKit()); jtp.setText("\ntype some text in the above empty line and check the wrapping behavior"); doc = jtp.getStyledDocument(); doc.addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent e) { insert(); } public void removeUpdate(DocumentEvent e) { insert(); } public void changedUpdate(DocumentEvent e) { insert(); } public void insert() { SwingUtilities.invokeLater(new Runnable() { public void run() { Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE); doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false); } }); } }); JScrollPane scroll = new JScrollPane(jtp); scroll.setPreferredSize(new Dimension(200, 200)); frame.add(scroll); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BugWrapJava7 bugWrapJava7 = new BugWrapJava7(); } }); } } class MyStyledEditorKit extends StyledEditorKit { private MyFactory factory; public ViewFactory getViewFactory() { if (factory == null) { factory = new MyFactory(); } return factory; } } class MyFactory implements ViewFactory { public View create(Element elem) { String kind = elem.getName(); if (kind != null) { if (kind.equals(AbstractDocument.ContentElementName)) { return new MyLabelView(elem); } else if (kind.equals(AbstractDocument.ParagraphElementName)) { return new ParagraphView(elem); } else if (kind.equals(AbstractDocument.SectionElementName)) { return new BoxView(elem, View.Y_AXIS); } else if (kind.equals(StyleConstants.ComponentElementName)) { return new ComponentView(elem); } else if (kind.equals(StyleConstants.IconElementName)) { return new IconView(elem); } } // default to text display return new LabelView(elem); } } class MyLabelView extends LabelView { public MyLabelView(Element elem) { super(elem); } public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { resetBreakSpots(); } return super.breakView(axis, p0, pos, len); } private void resetBreakSpots() { try { // HACK the breakSpots private fields Field f=GlyphView.class.getDeclaredField("breakSpots"); f.setAccessible(true); f.set(this, null); } catch (Exception e) { e.printStackTrace(); } } } 

没有reflection就少了黑客。 基于模型更改时breakSpots的通常重置。

 class MyLabelView extends LabelView { boolean isResetBreakSpots=false; public MyLabelView(Element elem) { super(elem); } public View breakView(int axis, int p0, float pos, float len) { if (axis == View.X_AXIS) { resetBreakSpots(); } return super.breakView(axis, p0, pos, len); } private void resetBreakSpots() { isResetBreakSpots=true; removeUpdate(null, null, null); isResetBreakSpots=false; // try { // Field f=GlyphView.class.getDeclaredField("breakSpots"); // f.setAccessible(true); // f.set(this, null); // } catch (Exception e) { // e.printStackTrace(); // } } public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.removeUpdate(e, a, f); } public void preferenceChanged(View child, boolean width, boolean height) { if (!isResetBreakSpots) { super.preferenceChanged(child, width, height); } } } 

更新:这个也修复了TextSamplerDemo。 我重置了所有标签视图的所有位置。

 class MyStyledEditorKit extends StyledEditorKit { private MyFactory factory; public ViewFactory getViewFactory() { if (factory == null) { factory = new MyFactory(); } return factory; } } class MyFactory implements ViewFactory { public View create(Element elem) { String kind = elem.getName(); if (kind != null) { if (kind.equals(AbstractDocument.ContentElementName)) { return new MyLabelView(elem); } else if (kind.equals(AbstractDocument.ParagraphElementName)) { return new MyParagraphView(elem); } else if (kind.equals(AbstractDocument.SectionElementName)) { return new BoxView(elem, View.Y_AXIS); } else if (kind.equals(StyleConstants.ComponentElementName)) { return new ComponentView(elem); } else if (kind.equals(StyleConstants.IconElementName)) { return new IconView(elem); } } // default to text display return new LabelView(elem); } } class MyParagraphView extends ParagraphView { public MyParagraphView(Element elem) { super(elem); } public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.removeUpdate(e, a, f); resetBreakSpots(); } public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.insertUpdate(e, a, f); resetBreakSpots(); } private void resetBreakSpots() { for (int i=0; i