【问题标题】:JTextPane line wrap behaviorJTextPane 换行行为
【发布时间】:2015-06-02 07:33:45
【问题描述】:

最近我一直在做一个Java文本编辑器项目,我想用JTextPane替换旧的JTextArea来实现语法高亮。但是,JTextPaneJTextArea 中缺少方法(例如append()getLineStartOffset() 等),我想在我的类MyTextPaneJTextPane 的子类)中重新实现它们,但遇到了麻烦。

我当前的代码(只有一小部分):

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

public class MyTextPane extends JTextPane
{
    public MyTextPane()
    {
        super();
    }

    public void append(String text)
    {
        try
        {
            Document doc = this.getDocument();
            doc.insertString(doc.getLength(),text,null);
        }
        catch (BadLocationException ex)
        {
            //must succeed
            throw new InternalError(ex.getMessage());
        }
    }

    public void insert(String text, int pos)
    {
        try
        {
            this.getStyledDocument().insertString(pos,text,null);
        }
        catch (BadLocationException ex)
        {
            throw new IllegalArgumentException(ex);
        }
    }

    public void replaceRange(String str, int start, int end)
    {
        try
        {
            Document doc = this.getDocument();
            doc.remove(start,end-start);
            doc.insertString(start,str,null);
        }
        catch (BadLocationException ex)
        {
            throw new IllegalArgumentException(ex);
        }
    }

    public void setLineWrap(boolean isLineWrap)
    {
        /*
         * implements later
         */
    }

    public boolean getLineWrap()
    {
        /*
         * implements later
         */
         return true;
    }

    public void setWrapStyleWord(boolean isWrapStyleWord)
    {
        /*
         * implements later
         */
    }

    public boolean getWrapStyleWord()
    {
        /*
         * implements later
         */
        return true;
    }

    public void setTabSize(int size)
    {
        /*
         * implements later
         */        
    }

    public int getTabSize()
    {
        /*
         * implements later
         */
        return 4;
    }

    public int getLineCount()
    {
        //follow JTextArea implementation
        Element root = this.getDocument().getDefaultRootElement();
        return root.getElementCount();
    }

    public int getLineStartOffset(int line) throws BadLocationException
    {
        //follow JTextArea implementation
        int count = this.getLineCount();
        Document doc = this.getDocument();
        if (line < 0)
        {
            throw new BadLocationException("Negative line", -1);
        }
        if (line >= count)
        {
            throw new BadLocationException("No such line", doc.getLength() + 1);
        }
        return doc.getDefaultRootElement().getElement(line).getStartOffset();
    }

    public int getLineEndOffset(int line) throws BadLocationException
    {
        //follow JTextArea implementation
        int count = this.getLineCount();
        Document doc = this.getDocument();
        if (line < 0)
        {
            throw new BadLocationException("Negative line", -1);
        }
        if (line >= count)
        {
            throw new BadLocationException("No such line", doc.getLength() + 1);
        }
        int end = doc.getDefaultRootElement().getElement(line).getEndOffset();
        return (line==count-1)?(end-1):end;
    }

    public int getLineOfOffset(int off) throws BadLocationException
    {
        //follow JTextArea implementation
        Document doc = this.getDocument();
        if (off < 0)
        {
            throw new BadLocationException("Can't translate offset to line", -1);
        }
        if (off > doc.getLength())
        {
            throw new BadLocationException("Can't translate offset to line", doc.getLength() + 1);
        }
        return doc.getDefaultRootElement().getElementIndex(off);
    }

    public static void main(String[] args)
    {
        final SimpleAttributeSet BOLD_SET = new SimpleAttributeSet();
        StyleConstants.setBold(BOLD_SET, true);
        StyleConstants.setForeground(BOLD_SET, new Color(0,0,125));
        SwingUtilities.invokeLater(new Runnable()
        {            
            @Override
            public void run()
            {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                MyTextPane textPane = new MyTextPane();
                frame.add(new JScrollPane(textPane), BorderLayout.CENTER);
                frame.setSize(200,200);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

如您所见,我已经添加了一些方法,例如append()。但是,我想不出任何方法来控制换行策略。

默认行为很奇怪:当有一个短词和一个长词时,

如果我连续输入没有空格的字符,

它最初看起来像是用单词包装的。但是,当我继续输入字符时,

它根本不换行。

是否有任何优雅的方式来控制JTextPane 的包装策略?换句话说,JTextPane 可以像JTextArea 一样包装单词吗?我发现了很多重复项(如 thisthisthis)但找不到解决方案。提前致谢。

【问题讨论】:

    标签: java swing jtextarea jtextpane word-wrap


    【解决方案1】:

    这是关于同一问题的讨论:Word wrapping behavior in JTextPane since Java 7。用户StanislavL(他在 Stack Overflow 上似乎也很活跃:StanislavL)提出的解决方案支持我使用 Java 8 进行自动换行。它使用自定义 WrapEditorKit 作为 @987654326 的编辑器工具包@(而WrapEditorKit 类又使用WrapColumnFactoryWrapLabelView 类)。

    将此与NonWrappingTextPane example(来自Kim Topley 的Core Swing:高级编程一书)结合使用可以关闭换行:

    import java.awt.*;
    import javax.swing.*;
    
    public class WrapTestApp extends JFrame {
        public static void main(final String[] arguments) {
            new WrapTestApp();
        }
    
        public WrapTestApp() {
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            setBounds(800, 400, 200, 200);
            getContentPane().setLayout(new BorderLayout());
            final CustomTextPane textPane = new CustomTextPane(true);
            final JScrollPane scrollPane = new JScrollPane(textPane);
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
            getContentPane().add(scrollPane, BorderLayout.CENTER);
            textPane.setText("ExampleOfTheWrapLongWordWithoutSpaces");
            setVisible(true);
        }
    }
    

    CustomTextPane 类:

    import javax.swing.*;
    import javax.swing.text.*;
    
    public class CustomTextPane extends JTextPane {
        private boolean lineWrap;
    
        public CustomTextPane(final boolean lineWrap) {
            this.lineWrap = lineWrap;
    
            if (lineWrap)
                setEditorKit(new WrapEditorKit());
        }
    
        @Override
        public boolean getScrollableTracksViewportWidth() {
            if (lineWrap)
                return super.getScrollableTracksViewportWidth();
            else
                return getParent() == null
                      || getUI().getPreferredSize(this).width <= getParent().getSize().width;
        }
    
        private class WrapEditorKit extends StyledEditorKit {
            private final ViewFactory defaultFactory = new WrapColumnFactory();
    
            @Override
            public ViewFactory getViewFactory() {
                return defaultFactory;
            }
        }
    
        private class WrapColumnFactory implements ViewFactory {
            @Override
            public View create(final Element element) {
                final String kind = element.getName();
                if (kind != null) {
                    switch (kind) {
                        case AbstractDocument.ContentElementName:
                            return new WrapLabelView(element);
                        case AbstractDocument.ParagraphElementName:
                            return new ParagraphView(element);
                        case AbstractDocument.SectionElementName:
                            return new BoxView(element, View.Y_AXIS);
                        case StyleConstants.ComponentElementName:
                            return new ComponentView(element);
                        case StyleConstants.IconElementName:
                            return new IconView(element);
                    }
                }
    
                // Default to text display.
                return new LabelView(element);
            }
        }
    
        private class WrapLabelView extends LabelView {
            public WrapLabelView(final Element element) {
                super(element);
            }
    
            @Override
            public float getMinimumSpan(final 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);
                }
            }
        }
    }
    

    【讨论】:

    • 没关系,但还是不行setLineWrap(false)
    • 谢谢它工作正常。顺便说一句,我使用SwingUtilities.getUnwrappedParent(this); 代替,因为我使用JLayer 来包装JTextPane。 =) 已接受答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多