Оберните длинные слова в JTextPane (Java 7)
Во всех версиях Java до 6 поведение по умолчанию JTextPane, помещенное внутри JScrollPane, было: обертывание строк по границам слов, если это возможно. Если нет, тогда заверните их.
В JDK 7 поведение по умолчанию выглядит следующим образом: оберните строки на границах слов, если это возможно. Если нет, просто расширьте ширину JTextPane (никогда не завершайте длинные слова).
Это легко воспроизвести, вот SSCCE:
public class WrappingTest extends JFrame
{
public static void main ( String[] args )
{
new WrappingTest();
}
public WrappingTest ()
{
setSize(200,200);
getContentPane().setLayout(new BorderLayout());
JTextPane jtp = new JTextPane();
JScrollPane jsp = new JScrollPane(jtp);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
getContentPane().add(jsp,BorderLayout.CENTER);
setVisible(true);
}
}
Просто запустите его в JDK 6 и в JDK 7, напишите несколько небольших слов и напишите длинное слово, и вы увидите разницу.
Мой вопрос прост... новое поведение по умолчанию в JDK 7 полностью испортит мою программу (они должны быть более осторожны в Oracle с изменением такого типа умолчаний... они кажутся несущественными, но когда вы используете JTextPane, чтобы отображать данные, которые обычно содержат очень длинные строки букв, они не так несущественны - на самом деле я собираюсь подать отчет об ошибке, но мне бы хотелось иметь временное решение, пока они не разрешают его). Любой способ вернуться к предыдущему поведению?
Обратите внимание, что я проверил ответ на соответствующий вопрос Как выполняется перенос слов в JTextPane, и как мне заставить его обернуть строку без пробелов? он не отвечает на этот вопрос - он предоставляет способ сделать обертку JTextPane без каких-либо ограничений для пробелов, но для меня желаемое поведение - это разделение строк по пробелу, если это возможно, и в других местах, если это невозможно (как в предыдущих версиях Java).
Ответы
Ответ 1
Для меня исправление работает (проверено под 1.7.0_09)
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
public class WrapTestApp extends JFrame {
public static void main ( String[] args ) {
new WrapTestApp();
}
public WrapTestApp () {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(200,200);
getContentPane().setLayout(new BorderLayout());
JTextPane jtp = new JTextPane();
jtp.setEditorKit(new WrapEditorKit());
JScrollPane jsp = new JScrollPane(jtp);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
getContentPane().add(jsp, BorderLayout.CENTER);
jtp.setText("ExampleOfTheWrapLongWordWithoutSpaces");
setVisible(true);
}
class WrapEditorKit extends StyledEditorKit {
ViewFactory defaultFactory=new WrapColumnFactory();
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
class WrapColumnFactory implements ViewFactory {
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new WrapLabelView(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 WrapLabelView extends LabelView {
public WrapLabelView(Element elem) {
super(elem);
}
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
}
Ответ 2
Хороший улов от @dk89, но, увы, обходные методы не работают: JDK 7, похоже, все еще не предлагает подождать, чтобы установить пользовательский BreakIterator на JTextComponent; даже не на GlyphView, где генерация BreakIterator является частной. И если мы вставим строку char в char, она по-прежнему не работает: я полагаю, что последовательные прогоны текста с одинаковым стилем (AttributeSet) рушится вместе.
Я провел два дня, пытаясь сделать собственный редактор EditorKit, как рекомендовано в другом месте, но он не работает (как минимум, с JDK 1.7.0_4).
Я попробовал решение, данное в Как переносить текст, хранящийся в JTextPanes, которые являются ячейками в JList, и вариант, найденный в http://www.experts-exchange.com/Programming/Languages/Java/Q_20393892.html
Но я узнал, что breakView больше не вызывается, когда JTextPane меньше самого длинного слова в предложении. Так что это не работает вообще, когда есть только одно (длинное) слово. Это наш случай, поскольку мы отображаем предоставленные пользователем строки, подобные идентификаторам, часто без пробелов, в довольно небольших пространствах.
Наконец, я нашел простое решение, полученное из предложения в отчете об ошибке: действительно, вставьте строку char на char, но чередуйте стили! Таким образом, у нас столько сегментов, сколько у нас есть, и строка обтекается границами char. До следующего "исправления ошибок"?
Фрагменты кода:
private JTextPane tp;
private SimpleAttributeSet sas = new SimpleAttributeSet();
tp= new JTextPane();
sas.addAttribute( "A", "C" ); // Arbitrary attribute names and value, not used actually
// Set the global attributes (italics, etc.)
tp.setParagraphAttributes(styleParagraphAttributes, true);
Document doc = tp.getDocument();
try
{
doc.remove(0, doc.getLength()); // Clear
for (int i = 0; i < textToDisplay.length(); i++)
{
doc.insertString(doc.getLength(), textToDisplay.substring(i, i+1),
// Change attribute every other char
i % 2 == 0 ? null : sas);
}
}
catch (BadLocationException ble)
{
log.warn("Cannot happen...", ble);
}
Как указано в ошибке, они должны были предоставить простой способ (возможно, какое-то свойство или некоторые инъекционные вещи), чтобы вернуться к старому поведению.
Ответ 3
Взгляните на эту ошибку:
http://bugs.sun.com/view_bug.do?bug_id=6539700
Ответ 4
Привет, у меня была такая же проблема, но я нашел работу:
просто создайте расширенный класс JTextPane, например.
MyWrapJTextPane extends JTextPane
и перезаписать следующий метод - он работает; -)
public boolean getScrollableTracksViewportWidth() {
return true;
}