我有两个不同的编辑器使用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中引入的新错误?如果它是一个新的JVM错误,是否有解决方法?
它可能与question 8666727有关,但问题在于错误的包装而不是滚动条的外观。
答案 0 :(得分:16)
期货读者,bug is still present in JDK 1.7.0_04.,
比较Java7和稳定的Java6,
< ------ Java7 v.s. Java6 --->
< ------ Java7 v.s. Java6 --->
< ------ Java7 v.s. Java6 --->
< ------ Java7 v.s. 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();
}
});
}
}
答案 1 :(得分:7)
调查此事。原因是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();
}
}
}
没有反思就少了黑客。基于模型更改时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<layoutPool.getViewCount(); i++) {
View v=layoutPool.getView(i);
if (v instanceof MyLabelView) {
((MyLabelView)v).resetBreakSpots();
}
}
}
}
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);
}
public void resetBreakSpots() {
isResetBreakSpots=true;
removeUpdate(null, null, null);
isResetBreakSpots=false;
}
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);
}
}
}
答案 2 :(得分:0)
我的HTMLDocument是按字母换行的。我安装了StanislavL的上述解决方案。没什么好高兴的,仍然在
问题在于存在两种不同类型的Element(在javax.swing.text和javax.swing.text.html.parser中),并且都具有带有字符串值“ content”的标记类型名称。方法Element.getName通过返回一个String而不是一个Object来混淆它。
因此,代码显示为“ .equals()”
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
...
它应该说“ ==“
Object absDocName = attrs.getAttribute(AbstractDocument.ElementNameAttribute)
if (absDocName != null){
if (kind == AbstractDocument.ContentElementName) {
...