如何删除最后一个 line/component/icon/widget/etc。来自 JTextPane

How to remove the last line/component/icon/widget/etc. from JTextPane

我有一个庞大的 Java 项目,它使用 UI 这样的控制台。对于控制台,我使用 JTextPane。最近我需要一些方法来分别删除第一行和最后一行。我删除第一行的方法非常简单,如下

public void removeFirstLine() {
        try {
            Element root = outputArea.getDocument().getDefaultRootElement();
            Element first = root.getElement(0);
            outputArea.getDocument().remove(first.getStartOffset(), first.getEndOffset());
            outputArea.setCaretPosition(outputArea.getDocument().getLength());
        } catch (BadLocationException e) {
            ErrorHandler.handle(e);
        }
    }

但是,当我尝试删除最后一行(NOT OF TEXT I REPEAT NOT OF TEXT)时,我的问题就来了。此行可以是可附加到 JTextPane 的任何内容,例如自定义组件、文本字符串、图标等。如果有人知道如何删除附加到 JTextPane 的最后一个“东西”,我将永远感激您。

编辑:添加最小的可重现示例:

import javax.swing.*;
import javax.swing.text.*;

public class Test {
    public static void main(String[] args) {
        new Test();
    }

    private static final String ELEM = AbstractDocument.ElementNameAttribute;
    private static final String ICON = StyleConstants.IconElementName;
    private static final String COMP = StyleConstants.ComponentElementName;
    private JTextPane outputArea;

    Test() {
        try {
            //init pane
            outputArea = new JTextPane();

            //insert component
            JTextField c = new JTextField(20);
            Style cs = outputArea.getStyledDocument().addStyle("name", null);
            StyleConstants.setComponent(cs, c);
            outputArea.getStyledDocument().insertString(outputArea.getStyledDocument().getLength(), "string", cs);

            //new line
            println("");

            //add string
            println("this is a string added to the pane");

            //add image
            outputArea.insertIcon(new ImageIcon("/path/to/image.png"));

            //new line
            println("");

            //before
            printContents();

            //----------------------------
            //call removeLastLine() as many times as needed and it should remove the last added "thing"
            //regardless of the order added (ex: component, text, icon should function the same as text, text, text, component obviously)

            //changes should be reflected here
            printContents();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void removeLastLine() {
        //TODO remove last line of text, last component, last image icon, etc.
    }

    public void println(String Usage) {
        try {
            StyledDocument document = (StyledDocument) outputArea.getDocument();
            document.insertString(document.getLength(), Usage + "\n", null);
            outputArea.setCaretPosition(outputArea.getDocument().getLength());
        }

        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void printContents() {
        try {
            ElementIterator iterator = new ElementIterator(outputArea.getStyledDocument());
            Element element;
            while ((element = iterator.next()) != null) {
                System.out.println(element);
                AttributeSet as = element.getAttributes();
                if (as.containsAttribute(ELEM, ICON)) {
                    System.out.println(StyleConstants.getIcon(as).getClass());
                }
                else if (as.containsAttribute(ELEM, COMP)) {
                    System.out.println(StyleConstants.getComponent(as).getClass());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

以下是我以前使用的代码,用于删除文本:

public void removeLastLine() {
    //  We use start minus 1 to make sure we remove the newline
    //  character of the previous line
    Element root = outputArea.getDocument().getDefaultRootElement();
    Element line = root.getElement(root.getElementCount() - 1);
    int start = line.getStartOffset();
    int end = line.getEndOffset();

    try
    {
        outputArea.getDocument().remove(start - 1, end - start);
    }
    catch(BadLocationException ble)
    {
        System.out.println(ble);
    }
}

似乎也适用于其他对象,因为“之后”的输出与“之前”的输出不同。

我会让你验证文本窗格是否按预期更新。

我从 Limit Lines in Document

中获取了代码

编辑:

上面的代码在尝试删除第一行时不起作用,因为起始偏移量将为负数。

以下是也处理删除第一行的更新版本:

import java.awt.*;
import javax.swing.*;
import javax.swing.*;
import javax.swing.text.*;

public class Test {
    public static void main(String[] args) {

        new Test();
    }

    private static final String ELEM = AbstractDocument.ElementNameAttribute;
    private static final String ICON = StyleConstants.IconElementName;
    private static final String COMP = StyleConstants.ComponentElementName;
    private JTextPane outputArea;

    Test() {
        try {
            //init pane
            outputArea = new JTextPane();

            JButton button = new JButton("Remove Last Line");
            button.addActionListener((e) -> removeLastLine());

            JFrame frame = new JFrame();
            frame.add( new JScrollPane(outputArea) );
            frame.add( button, BorderLayout.PAGE_END);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setSize(300, 300);
            frame.setLocationByPlatform( true );
            frame.setVisible( true );

            println("first line");

            //insert component
            JTextField c = new JTextField(20);
            Style cs = outputArea.getStyledDocument().addStyle("name", null);
            StyleConstants.setComponent(cs, c);
            outputArea.getStyledDocument().insertString(outputArea.getStyledDocument().getLength(), "string", cs);

            //new line
            println("");

            //add string
            println("this is a string added to the pane");

            //add image
            outputArea.insertIcon(new ImageIcon("about16.gif"));

            //new line
            println("\nsecond last line");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void removeLastLine()
    {
        //  We use start minus 1 to make sure we remove the newline
        //  character of the previous line

        Element root = outputArea.getDocument().getDefaultRootElement();
        Element line = root.getElement(root.getElementCount() - 1);
        int start = line.getStartOffset();
        int end = line.getEndOffset();

        try
        {
            int offset = start - 1;
            int length = end - start;

            //  removing the first line

            if (offset < 0)
            {
                offset = 0;
                length -= 1;
            }

            outputArea.getDocument().remove(offset, length);
        }
        catch(BadLocationException ble)
        {
            System.out.println(ble);
        }
    }

    public void println(String Usage) {
        try {
            StyledDocument document = (StyledDocument) outputArea.getDocument();
            document.insertString(document.getLength(), Usage + "\n", null);
            outputArea.setCaretPosition(outputArea.getDocument().getLength());
        }

        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我用整个下午写的以下代码解决了这个问题。

 /**
     * Removes the last "thing" addeed to the JTextPane whether it's a component,
     *  icon, or string of multi-llined text.
     *
     *  In more detail, this method figures out what it'll be removing and then determines how many calls
     *   are needed to {@link StringUtil#removeLastLine()}
     */
 public void removeLast() {
        boolean removeTwoLines = false;

        LinkedList<Element> elements = new LinkedList<>();
        ElementIterator iterator = new ElementIterator(outputArea.getStyledDocument());
        Element element;
        while ((element = iterator.next()) != null) {
            elements.add(element);
        }

        int leafs = 0;

        for (Element value : elements)
            if (value.getElementCount() == 0)
                leafs++;

        int passedLeafs = 0;

        for (Element value : elements) {
            if (value.getElementCount() == 0) {
                if (passedLeafs + 3 != leafs) {
                    passedLeafs++;
                    continue;
                }

                if (value.toString().toLowerCase().contains("icon") || value.toString().toLowerCase().contains("component")) {
                    removeTwoLines = true;
                }
            }
        }

        if (removeTwoLines) {
            removeLastLine();
        }

        removeLastLine();
    }

    /**
     * Removes the last line added to the linked JTextPane. This could appear to remove nothing,
     *  but really be removing just a newline (line break) character.
     */
    public void removeLastLine() {
        try {
            LinkedList<Element> elements = new LinkedList<>();
            ElementIterator iterator = new ElementIterator(outputArea.getStyledDocument());
            Element element;
            while ((element = iterator.next()) != null) {
                elements.add(element);
            }

            int leafs = 0;

            for (Element value : elements)
                if (value.getElementCount() == 0)
                    leafs++;

            int passedLeafs = 0;

            for (Element value : elements) {
                if (value.getElementCount() == 0) {
                    if (passedLeafs + 2 != leafs) {
                        passedLeafs++;
                        continue;
                    }

                    outputArea.getStyledDocument().remove(value.getStartOffset(),
                            value.getEndOffset() - value.getStartOffset());
                }
            }
        } catch (BadLocationException ignored) {}
        catch (Exception e) {
            e.printStackTrace();
        }
    }

完整代码可以在下面查看:https://github.com/NathanCheshire/Cyder/blob/master/src/cyder/utilities/StringUtil.java