Swing - 我的 class 扩展了 JTextPane 并首先调用我的 append() 然后 super.setText() 添加额外的换行符

Swing - My class extends JTextPane and calling first my append() and then super.setText() add additional line break

很简单:我有这个 SSCCE:

import java.io.File;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import net.miginfocom.swing.MigLayout;

public class Test1_ChangeStyleAndAppend extends JFrame {
    public class MyJTextPane extends JTextPane {
        /**
         * Append some text to this pane.
         * @param s
         */
        public void append(String s) {
            try {
               Document doc = this.getDocument();
               doc.insertString(doc.getLength(), s, null);
            } catch(BadLocationException e) {
                System.err.println(e);
            }
        }

        /**
         * Append some text and change line.
         * @param s
         */
        public void appendLine(String s) {
            try {
                Document doc = this.getDocument();
                doc.insertString(doc.getLength(), s + System.lineSeparator(), null);
            } catch(BadLocationException e) {
                System.err.println(e);
            }
        }
    }
    public Test1_ChangeStyleAndAppend() {
        begin();
    }

    private void begin() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        MyJTextPane pane0 = new MyJTextPane();
        pane0.appendLine("MyJTextPane using append() and then calling setText()");
        pane0.appendLine("Second line. ");
        pane0.appendLine("Third line");
        pane0.setText(pane0.getText() + "At last" + System.lineSeparator());
        pane0.setBorder(new EtchedBorder(EtchedBorder.RAISED));
        add(pane0, BorderLayout.NORTH);

        MyJTextPane pane = new MyJTextPane();
//        changeLineSpacing(pane, 1.5f, false);
        pane.appendLine("MyJTextPane calling appendLine()");
        pane.appendLine("Second line. ");
        pane.appendLine("Third line");
        pane.appendLine("At last");
        pane.setBorder(new EtchedBorder(EtchedBorder.RAISED));
        add(pane, BorderLayout.CENTER);


        JTextPane pane2 = new JTextPane();
        pane2.setText("Normal JTextPane calling setText()");
        pane2.setText(pane2.getText() + System.lineSeparator() + "Second line. ");
        pane2.setText(pane2.getText() + System.lineSeparator() + "Third line");
        pane2.setText(pane2.getText() + System.lineSeparator() + "At last");
        pane2.setBorder(new EtchedBorder(EtchedBorder.RAISED));
        add(pane2, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }

    /**
     * Select all the text of a <code>JTextPane</code> first and then set the line spacing.
     * @param pane the <code>JTextPane</code> to apply the change
     * @param factor the factor of line spacing. For example, <code>1.0f</code>.
     * @param replace whether the new <code>AttributeSet</code> should replace the old set. If set to <code>false</code>, will merge with the old one.
     */
    public static void changeLineSpacing(JTextPane pane, float factor, boolean replace) {
        pane.selectAll();
        MutableAttributeSet set = new SimpleAttributeSet(pane.getParagraphAttributes());
        StyleConstants.setLineSpacing(set, factor);
        pane.setParagraphAttributes(set, replace);
        pane.setCaretPosition(0); //scroll to the top.
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Test1_ChangeStyleAndAppend frame = new Test1_ChangeStyleAndAppend();

            }

        });
    }
}

结果是:

我支持 Windows。

我有两个 TextPanes,一个是 JTextPane,另一个是我的 class 扩展 JTextPane,在这个中我定义了两个方便的方法,append()appendLine() 在我的自定义 class 中,但我没有覆盖超级 getText() 方法。

现在,当我调用 appendLine() 然后在我的 class 中调用 setText() 时,getText() 的调用在行之间添加了另一个 \r,使得它 \r\r\n 并在两行之间添加额外的空行。这不是我想要的,我不想添加\r

我有这个 JTextArea 类型的变量,为了改变行距,我把它改成了 JTextPane 因为 JTextArea 不能调整行距。但它发生在整个项目中,并且一直使用 pane.setText(pane.getText + newText)。现在我必须搜索所有出现的 setText() 并将其更改为 appendLine()。在做这个大改变之前,我想了解为什么要添加这个额外的 \r

如果我总是调用 append() 或总是调用 setText(pane.getText() + newText),则不会发生。但是如果我先调用append()再调用setText(pane.getText() + newText),就会添加\r

有人阐明这一点吗?

Windows 的行分隔符是“\r\n”。

文档仅使用“\n”作为新行字符串。

不确定到底是什么问题,但在 insertString(...) 和 getText() 方法之间处理换行字符串的方式存在不一致。

简单的解决方案是用换行字符串更新文档,而不是平台换行字符串。

//doc.insertString(doc.getLength(), s + System.lineSeparator(), null);
doc.insertString(doc.getLength(), s + "\n", null);

编辑:错误报告并存档。仅出现在 Windows。我和 Windows 10.

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180276

之前的答案被接受是因为我已经多次接受我的答案作为我的问题的解决方案 :) 是时候对他人表现出一些慷慨,以便我们继续互相帮助。

============================================= ============================

最后我认为问题出现在我触摸下面的文档时。我猜是因为 JTextPane 使用 StringWriter 来获取它的内容,这是错误的。在JDK8u111源码中我发现:

/**
 * Returns the text contained in this <code>TextComponent</code>
 * in terms of the
 * content type of this editor.  If an exception is thrown while
 * attempting to retrieve the text, <code>null</code> will be returned.
 * This is implemented to call <code>JTextComponent.write</code> with
 * a <code>StringWriter</code>.
 *
 * @return the text
 * @see #setText
 */
public String getText() {
    String txt;
    try {
        StringWriter buf = new StringWriter();
        write(buf);
        txt = buf.toString();
    } catch (IOException ioe) {
        txt = null;
    }
    return txt;
}

StringWriter 可能做错了什么来产生额外的 \r。因为如果我用我接触文档的方法覆盖这个方法,问题就消失了,所有输出都是正常的, \nSystem.lineSeperator().

    /**
     * Override to permit mixed use of append(), appendLine()
     * with setText(getText() + newText). getText() returns
     * inconsistent content with additional blank lines.
     */
    @Override
    public String getText() {
        String s = "";
        try {
            s = this.getDocument().getText(0, this.getDocument().getLength());
        } catch (BadLocationException e) {
            log.error(e);
        }
        return s;
    }