以固定字符宽度包装JTextArea

时间:2016-05-16 19:47:14

标签: java swing jtextarea word-wrap

我已经获得了基于Swing的TextArea组件的两个竞争要求:

  1. 必须使用可变宽度字体(因为它看起来很漂亮)。
  2. 必须以特定字符宽度包装文本(以匹配文字UI)
  3. 一个简单的组合,是吗?破解UI以提供我自己的包装视图版本,该视图继承自WrappedPlainView,并覆盖calculateBreakPosition()以提供我想要的包装行为。

    除了看起来不那么简单。暂时忽略从变量渲染宽度派生的问题...我发现没有为编辑调用calculateBreakPosition()我希望它需要调用(在包装文本的前面添加字符),并且它正在计算break在一些相当常见的情况下,在1或2之间的位置,没有引用我的重写方法,让我认为我正在进行优化或文本区域实现的细节,我不知道足够期待。覆盖更多调试和修复此方法的方法很难:WrappedPlainView中的许多方法都标记为final或private,包含对com.sun类的引用,和/或使用com.sun类引用private / final方法或方法。 / p>

    有任何关于实现此目标的建议吗?

    我目前的情况:

    package com.foo.inputs;
    
    import java.util.Vector;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Element;
    import javax.swing.text.WrappedPlainView;
    
    public class CustomWrappedPlainView extends WrappedPlainView
    {
        private int charsWide;
    
        public CustomWrappedPlainView(Element elem, int width)
        {
            super(elem, true);
            this.charsWide = width;
        }
    
    
        @Override    
        protected int calculateBreakPosition(int p0, int p1)
        {
            int length = p1 - p0; 
            // Check for fit without wrapping.
            if(length <= charsWide)
            {
                return p1;
            }
    
            // Given: Past this point, we have at least charsWide+1 characters.
    
            String text;
            try
            {
                // Get one extra character.  If wrapping at 5 chars and the/
                // first six chars are: '12345 '  then we don't need to split
                // anything - that extra char matters.
                text = getDocument().getText(p0, charsWide+1);
            } catch(BadLocationException err)
            {
                // Should not happen
                // Fall back to max width
                return p0 + charsWide; 
            }
            return p0 + getBreakPosition(text, charsWide);
        }
    
    
        /** Determine where to break a substring. */
        static protected int getBreakPosition(String text, int charsWide)
        {
            // Paranoid check to avoid string bound errors
            if(text.length() <= charsWide)
            {
                return text.length();
            }
    
            // keep this simple, until/unless we need something more complex.
    
            for(int i = charsWide; i>= 0; i--)
            {
                char nyble = text.charAt(i);
                if(Character.isAlphabetic(nyble))
                {
                    continue;
                }
                if(Character.isDigit(nyble))
                {
                    continue;
                }
                if(Character.isWhitespace(nyble))
                {
                    // Wrap AFTER the space.
                    // Don't start new lines with a space.
                    return i+1;
                }
            }
            // Fallback: wrap after our break point
            return charsWide;
        }
    
        /** 
         * Utility method for CustomTextAreaEditor.  Ultimately this must
         * match the break logic from {@link #calculateBreakPosition(int, int)}
         * exactly to allow for WYSIWYG.
         */
        public static String[] breakOutLines(String text, int wrapWidth)
        {
            String[] paragraphs = text.split("\n");
            Vector<String> lines = new Vector<>();
            nextParagraph:
            for(String paragraph : paragraphs)
            {
                int length = paragraph.length();
                while(length > 0)
                {
                    if(length <= wrapWidth)
                    {
                        lines.add(paragraph);
                        continue nextParagraph;
                    }
                    int breakPoint = getBreakPosition(paragraph, wrapWidth);
                    lines.add(paragraph.substring(0,breakPoint));
                    paragraph = paragraph.substring(breakPoint);
                    length = paragraph.length();
                }
            }
            return lines.toArray(new String[0]);
        }
    }
    

1 个答案:

答案 0 :(得分:1)

不知道这是否会有所帮助,但也许不是编写自定义中断逻辑,而是可以通过使用FontMetrics来逃避:

@Override    
protected int calculateBreakPosition(int p0, int p1)
{
    FontMetrics original = metrics;
    FontMetrics monospacedFontMetrics = ...;

    int position = super.calculateBreakPosition(p0, p1);

    metrics = original;

    return position;
}

因此,您可以创建与当前Font相同大小的Monospaced字体,然后使用该字体获取在中断计算中使用的FontMetrics。

或者您可能需要复制calculateBreakPosition(...)方法中的代码,以便您也可以覆盖currentWidth以满足您的需求。