Обернуть длинные слова в JTextPane (Java 7) - PullRequest
16 голосов
/ 29 декабря 2011

Во всех версиях 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).

Ответы [ 4 ]

12 голосов
/ 14 ноября 2012

У меня исправление работает (протестировано под 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 голосов
/ 27 июня 2012

Хороший улов от @ dk89, но, увы, данные обходные пути не работают: JDK 7, по-видимому, все еще не предлагает ждать установки настраиваемого BreakIterator на JTextComponent;даже на GlyphView, где генерация BreakIterator является частной.И если мы вставим строку char по типу char, она все равно не будет работать: я полагаю, что последовательные прогоны текста с одинаковым стилем (AttributeSet) свернуты вместе.

Я потратил два дня, пытаясь создать пользовательскийEditorKit, как рекомендовано в другом месте, но он не работает хорошо (по крайней мере, с JDK 1.7.0_4) в качестве текста.

Я попробовал решение, данное на Как переносить текст, хранящийся в JTextPanes, которыйэто ячейки в JList и вариант, найденный по адресу http://www.experts -exchange.com / Программирование / Языки / Java / Q_20393892.html

Но я обнаружил, что breakViewбольше не вызывается, когда JTextPane меньше самого длинного слова в предложении.Так что это не работает вообще, когда есть только одно (длинное) слово.Это наш случай, когда мы показываем предоставленные пользователем идентичные строки идентификатора, часто без пробелов, в довольно маленьких местах.

Я наконец нашел простое решение, основанное на предложении в отчете об ошибке: действительно, вставкастрока 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);
    }

Как указывалось в ошибке, они должны были предоставить простой способ (возможно, какое-то свойство или некоторые инъекционные данные) длявернуться к старому поведению.

1 голос
/ 02 января 2012

Привет, у меня была такая же проблема, но я нашел обходной путь:

просто создайте расширенный класс JTextPane, например.

        MyWrapJTextPane extends JTextPane

и переписать следующий метод - все работает; -)

        public boolean getScrollableTracksViewportWidth() {
            return true;
        }
1 голос
/ 29 декабря 2011

Посмотрите на эту ошибку:

http://bugs.sun.com/view_bug.do?bug_id=6539700

...