Search code examples
javaswingjtextareamultiline

Count number of lines in JTextArea given set width


I'm trying to reliably count the number of lines (including those from line wraps and line breaks) in a JTextArea given a set width. I'm using this information to set the height of other components in my GUI (for example, for n lines, set n*height of a component).

I stumbled across this solution (reproduced below) but there is a problem with this. Sometimes it would miss a line if there isn't too much text on that line. For example, if a JTextArea of width 100 has 3 lines of text and on the 3rd line it only has say around width 15 of text, then it will only count 2 lines instead of 3.

public class MyTextArea extends JTextArea {

    //...

    public int countLines(int width) {

        AttributedString text = new AttributedString(this.getText());
        FontRenderContext frc = this.getFontMetrics(this.getFont()).getFontRenderContext();
        AttributedCharacterIterator charIt = text.getIterator();
        LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
        lineMeasurer.setPosition(charIt.getBeginIndex());

        int noLines = 0;
        while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
            lineMeasurer.nextLayout(width);
            noLines++;
        }

        System.out.print("there are " + noLines + "lines" + System.getProperty("line.separator"));
        return noLines;
    }
}

Any idea what might be causing this issue? Are there any alternatives to counting lines in a JTextArea? Thanks.


Solution

  • So I came up with a simple solution that uses FontMetrics to calculate the display width of the text, and by splitting the text into string tokens, I can count how many lines there will be.

    public int countLines(int width) {
    
            FontMetrics fontMetrics = this.getFontMetrics(this.getFont());
            String text = this.getText();
            String[] tokens = text.split(" ");
            String currentLine = "";
            boolean beginningOfLine = true;
            int noLines = 1;
    
            for (int i = 0; i < tokens.length; i++) {
                if (beginningOfLine) {
                    beginningOfLine = false;
                    currentLine = currentLine + tokens[i];
                } else {
                    currentLine = currentLine + " " + tokens[i];
                }
    
                if (fontMetrics.stringWidth(currentLine) > width) {
                    currentLine = "";
                    beginningOfLine = true;
                    noLines++;
                }
            }
    
            System.out.print("there are " + noLines + "lines" + System.getProperty("line.separator"));
            return noLines;
    }