Странная перенос текста со стилизованным текстом в JTextPane с Java 7
У меня есть два разных редактора, использующих 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, есть ли способ обхода?
Это может быть связано с вопросом 8666727, но проблема здесь заключается в неправильной упаковке, а не в виде полосы прокрутки.
Ответы
Ответ 1
для фьючерсных читателей, ошибка все еще присутствует в JDK 1.7.0_04.,
сравнение Java7 и со стабильным Java6,
< ------ Java7 v.s. Java6 --- > ![enter image description here]()
< ------ Java7 v.s. Java6 --- > ![enter image description here]()
< ------ Java7 v.s. Java6 --- > ![enter image description here]()
< ------ Java7 v.s. Java6 --- > ![enter image description here]()
из кода
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();
}
});
}
}
Ответ 2
Расследовал это. Причина - кэширование breakSpots. Похоже, что LabelView
хранит их и не пересчитывает смещения при редактировании текста предшествующего. Если я reset их вручную, ошибка не возникает.
Обходной путь (очень грязный из-за частных полей 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();
}
}
}
Меньше взломать без отражения. На основе обычного reset 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);
}
}
}
UPDATE: этот также исправляет TextSamplerDemo. я reset все точки для всех видов меток.
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);
}
}
}
Ответ 3
Мой HTMLDocument был упакован буквой. Я установил СтаниславЛ вышеуказанное решение. Никакой радости, все еще завернутый в букву внутри & lt; td & gt; элементы. Поэтому я исправил ошибку в коде StanislavL. Радость; повсеместно. Затем я обнаружил, что моя программа не использует код StanislavL! Похоже, что мои ошибки исправили ту же ошибку в моем собственном коде.
Проблема заключается в том, что существует два различных типа элемента (в 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) {
...