How to Calculate the Number of Rows (And Columns in Each Row) a Text Takes in a Jtextarea

How to calculate the number of rows (and columns in each row) a text takes in a JTextArea?

What am I doing wrong, what am I forgetting about?

Nothing, really. I modified your example to use "<=" on the width and to highlight a few features:

  1. FontMetrics notes, "the advance of a String is not necessarily the sum of the advances of its characters measured in isolation…"

  2. The preferred size of the text component matches the metric bounds pretty well for the widest line. This varies by font due to proportional spacing.

  3. TextLayout shows even tighter bounds, but note the "baseline-relative coordinates."

  4. The getLineCount() method counts line.separator delimited lines, not wrapped lines.


line: Twas brillig and the slithy toves
line: Did gyre and gimble in the wabe;
line count: 2
preferred: java.awt.Dimension[width=207,height=48]
bounds1: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.568359,w=205.0,h=15.310547]
layout1: java.awt.geom.Rectangle2D$Float[x=0.0,y=-10.0,w=200.0,h=13.0]
bounds2: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.568359,w=207.0,h=15.310547]
layout2: java.awt.geom.Rectangle2D$Float[x=1.0,y=-10.0,w=205.0,h=13.0]
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

/** #see http://stackoverflow.com/questions/5979795 */
public class TextAreaLine {

private static final String text1 =
"Twas brillig and the slithy toves\n";
private static final String text2 =
"Did gyre and gimble in the wabe;";
private static final JTextArea ta = new JTextArea(text1 + text2);

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
display();
}
});
}

static void display() {
JFrame f = new JFrame();
ta.setWrapStyleWord(false);
ta.setLineWrap(false);
ta.setRows(3);
f.add(ta);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
FontMetrics fm = ta.getFontMetrics(ta.getFont());
List<String> texts = new ArrayList<String>();
Dimension d = ta.getPreferredSize();
String text = ta.getText();
String line = "";
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c != '\n') {
if (fm.stringWidth(line + c) <= d.width) {
line += c;
} else {
texts.add(line);
line = "" + c;
}
}
}
texts.add(line);
for (String s : texts) {
System.out.println("line: " + s);
}
System.out.println("line count: " + ta.getLineCount());
System.out.println("preferred: " + d);
System.out.println("bounds1: " + fm.getStringBounds(text1, null));
FontRenderContext frc = new FontRenderContext(null, false, false);
TextLayout layout = new TextLayout(text1, ta.getFont(), frc);
System.out.println("layout1: " + layout.getBounds());
System.out.println("bounds2: " + fm.getStringBounds(text2, null));
layout = new TextLayout(text2, ta.getFont(), frc);
System.out.println("layout2: " + layout.getBounds());
}
}

JTextArea how to find column count / row width?

If you don't specify number of rows and columns when creating JTextArea, it default the rows and columns to zero. This mode allows the JTextArea to expand/contract according to available space for itself and actual number of letters shown in a line etc can be adjusted.

In the default mode, the number of letters that would fit in a line will depend on the current dimension of the component, the line wrapping style (word boundary or character boundary) and the font used.

If you cannot set the row, columns in the JTextArea then you can probably use javax.swing.JTextArea.getColumnWidth() to get the size of one character and use the current width of the component to get approximate number of letters that will fit in a line.

How to count the number of lines in a JTextArea, including those caused by wrapping?

You can use LineBreakMeasurer Class.

The LineBreakMeasurer class allows
styled text to be broken into lines
(or segments) that fit within a
particular visual advance. This is
useful for clients who wish to display
a paragraph of text that fits within a
specific width, called the wrapping
width.LineBreakMeasurer implements the most commonly used line-breaking policy: Every
word that fits within the wrapping
width is placed on the line. If the
first word does not fit, then all of
the characters that fit within the
wrapping width are placed on the line.
At least one character is placed on
each line.

Rows in JTextArea

All JTextComponents have modelToView(...) and viewToModel(...) methods that can help, but perhaps even better are the methods in the javax.swing.text.Utilities class including getRowStart(...) and getRowEnd(...)

Count number of lines in JTextArea given set width

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;
}

JTextArea - count selected rows

Check out the getLineAtCaret() method found in Text Utilities. This will get the line number at the caret (which won't help here).

So, you will need to modify the code to receive an offset as a parameter. Then you can pass in the getSelectionStart() and getSelectionEnd() values of the text component and use these values to return the line number. Then once you know the line number of each you can subtract the starting line number from the ending line number to give you the number of rows selected.

You can also check out the Text Component Line Number blog entry for a line number component to use for you editor.

My code counts the wrong number of words when the JTextArea is empty

Don't know anything about regex but it appears this is the default behaviour of the split method.

It appears to always return an array with the original value, even if that value happens to be the empty string.

I tried something simple like:

public static void main(String[] args) throws Exception
{
int wordCount = "".split("a").length;
System.out.println( wordCount );
}

and it always displays "1" no matter what character I attempt to split on.

The solution could be something like:

    String text = inputField.getText();
int wordCount = text.isEmpty() ? 0 : text.split("\\s").length;
numberOfWords.setText(String.valueOf(wordCount));

JTextArea with selectable rows

That is a JList, not a JTextArea. It has functionality like selection built-in. It can be shown with elements either horizontally or vertically. Please have a look at the official tutorial to which I've linked.

How to clear text from a jtextarea, and then keep appending other text (with the same button)?

I want to approach my answer to this problem with two concepts in program design: the first is the design dependence on an external feature, and the second is a state machine.

I'm making some guesses and assumptions here, but from your description it appears that you have fastened onto the "append" feature of a text area and are now programming so that you can use it in a couple of situations: adding a number to the number on the display, and putting a new number in the display ('appending' to an empty display).

That is a nice feature of textarea, it might be useful for your program, but you don't have to use it here.

However you solve this problem, you have a 'state' to save -- when the user presses a key, the thing the program does depends on its state. You have to be able to discern what state it's in so that you can program the response to the keypress correctly.

So let me suggest this: have a variable that stores what you want in the display -- all of it. When someone presses a key, discern your state in whatever fashion and update that variable with what is supposed to be in the display. When you're done processing, set the text area to that variable's value.

Note that this allows the program flow -- receive input, process, display -- to be the same in all cases. No part of the program has to figure out whether to 'clear' the display. Because of that, there is less complexity in the state you are saving.

If your state is "accumulating numbers for first operand", and a number is pressed, then you append the number pressed to the string being displayed.

If your state is "accumulating..." and an operator is pressed, append the operator.

If your state is "operator just pressed" and another number is pressed, set the variable to the number pressed.

and so forth.

These may or may not be the correct states to use, I just have them here as simple examples. But done in some similar way, I think you'll find your program gets simpler to write and to understand -- you remove the code that interacts between the 'append' feature and the program's state, which was making your code more complicated than it needed to be.

It is very common, I've found, for programmers (including myself) to get attached to some implementation that we then work around -- in fact, we are always working around implementations to some extent, that's what we do. But sometimes if we take a step back, we can see that we are doing more programming working around some implementation detail that we can eliminate, and by eliminating it we can simplify our solution.



Related Topics



Leave a reply



Submit