Оберните длинные слова в JTextPane (Java 7) - программирование
Подтвердить что ты не робот

Оберните длинные слова в 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).

4b9b3361

Ответ 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);
    }

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

Ответ 4

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

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

        MyWrapJTextPane extends JTextPane

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

        public boolean getScrollableTracksViewportWidth() {
            return true;
        }