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.
I am simply going to copy-paste my answer from your other question.
I'd like to change the spacing inbetweem the rows of a JTextArea
My first thought was that overriding javax.swing.JTextArea#getRowHeight
would be sufficient. The javadoc clearly states
Defines the meaning of the height of a row. This defaults to the height of the font.
So I was hoping that by overriding this method, you would adjust the definition and you would get more spacing between the rows. Bummer, didn't work. A quick search on the usages of that method in the JDK revealed the same. It is mainly used to calculate some sizes, but certainly not used when painting text inside the component.
By looking at the source code of the javax.swing.text.PlainView#paint
method, I saw that the FontMetrics
are used, and those you can easily override in the JTextArea
. So second approach was to extend the JTextArea
(bwah, extending Swing components but it is for a proof-of-concept)
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 );
}
}
The FontMetricsWrapper
class basically delegates everything, except the getHeight
method. In that method I added 10 to the result of the delegate
@Override
public int getHeight() {
//use +10 to make the difference obvious
return delegate.getHeight() + 10;
}
And this results in more row spacing (and a caret which is way too long, but that can probably be adjusted).
A little screenshot to illustrate this (not as nice as some of the other ones, but it shows that this approach might work):
Small disclaimer: this feels like an ugly hack and might result in unexpected issues. I do hope somebody comes with a better solution.
I personally prefer the solution StanislavL is proposing, but this gives you an alternative
That's a piece of code. It's not finished. Line spacing between wrapped lines is not implemented. You can get full source of WrappedPlainView
or PlainView
and add your code there to achieve desired line spacing
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;
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With