3

I need to syncronise line spacing of my PDF ListItem and JTextArea in my GUI. I can do it by adjusting one or another.

And it all works great unil a ListItem (or JTextArea) is more than one line long (line wrap is set to true in JTextArea).

I can adjust the height between the two ListItems. That distance will also be applied to the height between two rows of a single multiline ListItem.

However, in my GUI, due to compononet boreders and default line spacing in JTextArea, those two are not the same. Difference is about one pixel, but on large scale that can accumulate and cause some issues.

So, is there any way I can set linespacing in JTextArea, or any way I can differentiate between spaceing between two list items and two rows of the same list item?

I'm all up for using external libraries of any kind and any kind of tricks it may take...

Karlovsky120
  • 6,212
  • 8
  • 41
  • 94

3 Answers3

4

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;
}
StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • I'm sorry for being stupid, but where is the connection between the paint(Graphics g, Shape a) method and create(Element elem) method? Why do I have to implement my verson of PlainView, couldn't I just change the paint(args) method? – Karlovsky120 Oct 11 '12 at 22:50
  • I am afraid not. The rendering in fact done by the View. paint() calls view's paint() – StanislavL Oct 12 '12 at 05:54
  • @StanislavL How do I modify the method? However I do it, I end up with the bunch of private method and variables, that make extending of the class impossible... I may be asking for much, but could you add to your answer the instructions on how to create a JTextArea that has working paint method implemented? I really tred but I keep failing, I don't know how to do it... – Karlovsky120 Oct 16 '12 at 19:00
1

Control the space between your rows using the ParagraphAttribute SpaceBelow. You can do this with 4 lines of code or less (see example below). You will need to use a JTextPane to use these ParagraphAttributes (but JTextPane and JTextArea` are so similar, you shouldn't notice a difference).

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


public class LineSpacingExample extends Box{
    JTextPane modifiedTA;
    //Modify this to whatever spacing you want between the rows.
    static final float spaceBelow = 5.0f;
    //Font height - automatically calculated by code below
    int fontHeight = 0;

    public LineSpacingExample(){
        super(BoxLayout.X_AXIS);

        //Demonstrating that the spacing is predictable
        final JPanel leftBox = new CustomBox();
        add(leftBox);

        //Sets the amount of space below a row (only code you need to add)
        DefaultStyledDocument pd = new DefaultStyledDocument();
        SimpleAttributeSet sas = new SimpleAttributeSet();
        StyleConstants.setSpaceBelow(sas, spaceBelow);
        pd.setParagraphAttributes(0, pd.getLength(), sas, false);

        modifiedTA= new JTextPane(pd);  
        add(modifiedTA);

        //Calculates the font height in pixels
        fontHeight = modifiedTA.getFontMetrics(modifiedTA.getFont()).getHeight();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new LineSpacingExample());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    //EXTRA! Paints the left hand side box - to show that the spacing is predictable in pixels
    public class CustomBox extends JPanel{

        public CustomBox() {
            super();
            setOpaque(true);
            setBackground(Color.orange);

        }

        public void paintComponent(Graphics g){
            super.paintComponent(g);
            int height = getSize().height;
            int drawLocation = 2; //To account for padding on the TextPane
            int row = 1;
            while(drawLocation < height){

                //Drawing the text row background
                g.setColor(Color.blue);
                g.fillRect(0, drawLocation, 50, fontHeight);

                //Drawing the text row number
                g.setColor(Color.white);
                g.drawString(Integer.toString(row++), 0, drawLocation+14);
                drawLocation += fontHeight;

                //Drawing the space row
                g.setColor(Color.green);
                g.fillRect(0, drawLocation, 50, (int)spaceBelow);
                drawLocation += spaceBelow;
            }
        }
    };

}
Nick Rippe
  • 6,465
  • 14
  • 30
0

You can try to use a JTextPane instead of a JTextArea and set the line spacing accordingly to look like your ListItems.

SimpleAttributeSet set = new SimpleAttributeSet();

StyleConstants.setLineSpacing(set, 0.5f);    // <--- your value here

textPane.setParagraphAttributes(set, true);

This will not affect the line spacing of existing text, so you should set the text afterwards.

Rempelos
  • 1,220
  • 10
  • 18