4

So, I have this JTexrtArea which is almost perfect for my needs. The only thing wrong with it is the line spacing. I can't set it. (Why not JTextPane? Because spacing CAN be changed in JTextArea and JTextArea is way lighter thatn JTextPane, and I have a bunch of those in my program).

I have asked this question before, and this is the answer that I got from user StanislavL:


To override JTextArea's line spacing take a look at the PlainView (used to render PLainDocument).

There are following lines in the public void paint(Graphics g, Shape a) method

    drawLine(line, g, x, y);
    y += fontHeight;

So you can adapt the rendering fixing y offset.

In the BasicTextAreaUI method to create view. Replace it with your own implementation of the PlainView

public View create(Element elem) {
Document doc = elem.getDocument();
Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
    // build a view that support bidi
    return createI18N(elem);
} else {
    JTextComponent c = getComponent();
    if (c instanceof JTextArea) {
    JTextArea area = (JTextArea) c;
    View v;
    if (area.getLineWrap()) {
        v = new WrappedPlainView(elem, area.getWrapStyleWord());
    } else {
        v = new PlainView(elem);
    }
    return v;
    }
}
return null;
}

I grasp the general idea of what he's telling me to do, but I don't know how to do it. Also, I wouldn't like to override the default JTextArea "property", I'd like to have a choice - to use the default one or to use a custom one.

Only change in JTextArea code would be from

y += fontHeight,

to

y+= (fontHeight +(or -) additionalSpacing).

How do I achieve this? Which classes do I use/copy? Where do I put them? How do I make them usable? How do I get the whole thing working?

If you think this is too specific to be useful, maybe someone could write a general tutorial on how to create a custom swing component based 100% on an existing one. Then someone could easely change some values to better adjust it to it's needs.

4

2 回答 2

10

我只是从你的另一个问题中复制粘贴我的答案

我想更改 JTextArea 行之间的间距

我的第一个想法是覆盖javax.swing.JTextArea#getRowHeight就足够了。javadoc明确指出

定义行高的含义。这默认为字体的高度。

所以我希望通过覆盖这个方法,你可以调整定义,你会得到更多的行间距。糟了,没用。快速搜索 JDK 中该方法的用法也发现了同样的情况。主要是用来计算一些尺寸的,但是在组件内部绘制文字的时候肯定不会用到。

通过查看该javax.swing.text.PlainView#paint方法的源代码,我看到FontMetrics使用了 ,并且您可以在JTextArea. 所以第二种方法是扩展JTextArea(bwah,扩展 Swing 组件,但它是为了概念验证)

  private static class JTextAreaWithExtendedRowHeight extends JTextArea{
    private JTextAreaWithExtendedRowHeight( int rows, int columns ) {
      super( rows, columns );
    }

    @Override
    public FontMetrics getFontMetrics( Font font ) {
      FontMetrics fontMetrics = super.getFontMetrics( font );
      return new FontMetricsWrapper( font, fontMetrics );
    }
  }

该类FontMetricsWrapper基本上代表了除getHeight方法之外的所有内容。在那个方法中,我在委托的结果中添加了 10

@Override
public int getHeight() {
  //use +10 to make the difference obvious
  return delegate.getHeight() + 10;
}

这会导致更多的行间距(以及一个太长的插入符号,但可能可以调整)。

一个小屏幕截图来说明这一点(不像其他一些那样好,但它表明这种方法可能有效):

普通文本区域和扩展文本区域之间的区别

小免责声明:这感觉就像一个丑陋的黑客,可能会导致意想不到的问题。我确实希望有人提出更好的解决方案。

我个人更喜欢 StanislavL 提出的解决方案,但这为您提供了另一种选择

于 2012-10-26T06:15:27.043 回答
6

那是一段代码。它还没有完成。未实现换行之间的行间距。您可以获得完整的源代码WrappedPlainViewPlainView在那里添加您的代码以实现所需的行距

import javax.swing.*;
import javax.swing.plaf.basic.BasicTextAreaUI;
import javax.swing.text.*;

public class LineSpacingTextArea {

    public static void main(String[] args) {
        JTextArea ta=new JTextArea();
        JFrame fr=new JFrame("Custom line spacing in JTextArea");
        fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        ta.setText("Line 1\nLine 2\nLong text to show how line spacing works");
        ta.setLineWrap(true);
        ta.setWrapStyleWord(true);
        ta.setUI(new CustomTextAreaUI());
        fr.add(new JScrollPane(ta));
        fr.setSize(100,200);
        fr.setLocationRelativeTo(null);

        fr.setVisible(true);
    }

    static class CustomTextAreaUI extends BasicTextAreaUI {

        public View create(Element elem) {
            Document doc = elem.getDocument();
            Object i18nFlag = doc.getProperty("i18n"/*AbstractDocument.I18NProperty*/);
            if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) {
                // build a view that support bidi
                return super.create(elem);
            } else {
                JTextComponent c = getComponent();
                if (c instanceof JTextArea) {
                    JTextArea area = (JTextArea) c;
                    View v;
                    if (area.getLineWrap()) {
                        v = new CustomWrappedPlainView(elem, area.getWrapStyleWord());
                    } else {
                        v = new PlainView(elem);
                    }
                    return v;
                }
            }
            return null;
        }
    }

    static class CustomWrappedPlainView extends WrappedPlainView {
        public CustomWrappedPlainView(Element elem, boolean wordWrap) {
            super(elem, wordWrap);
        }
        protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
            super.layoutMajorAxis(targetSpan, axis, offsets, spans);
            int ls=spans[0];
            for (int i=0; i<offsets.length; i++) {
                offsets[i]+=i*ls;
            }
        }
    }
}
于 2012-10-25T06:50:26.210 回答