How to count the number of lines in a JTextArea, including those caused by wrapping?
I have a JTextArea for which I have set word-wrap and wrap-style-word to true. I want to "pack" the JTextArea to the minimum possible height given a specified width.
To do this, I'm planning calculating the height of the font using...
Font font = jTextArea.getFont();
FontMetrics fontMetrics = jTextArea.getFontMetrics(font);
int lineHeight = fontMetrics.getAscent() + fontMetrics.getDescent();
...and then multiply this by the number of lines used in the JTextArea. The problem is that JTextArea.getLineCount() counts the number of line returns ignoring the wrapped lines.
How do I count the number of lines used in a JTextArea including those that are caused by word wrap?
Here's some demo code to make toying with this problem easier. I have a listener that prints out the number of lines each time the window is resized. At the moment, it always prints 1, but I want to to compensate for the word wrap and print out how many lines are actually being used.
EDIT: I've included the solution to the problem in the code below. The static countLines method gives the solution.
package components;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.text.*;
import javax.swing.*;
public class JTextAreaLineCountDemo extends JPanel {
JTextArea textArea;
public JTextAreaLineCountDemo() {
super(new GridBagLayout());
String inputStr = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo";
textArea = new JTextArea(inputStr);
textArea.setEditable(false);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
// Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(textArea, c);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
System.out.println("Line count: " + countLines(textArea));
}
});
}
private static int countLines(JTextArea textArea) {
AttributedString text = new AttributedString(textArea.getText());
FontRenderContext frc = textArea.getFontMetrics(textArea.getFont())
.getFontRenderContext();
AttributedCharacterIterator charIt = text.getIterator();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
float formatWidth = (float) textArea.getSize().width;
lineMeasurer.setPosition(charIt.getBeginIndex());
int noLines = 0;
while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
lineMeasurer.nextLayout(formatWidth);
noLines++;
}
return noLines;
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("JTextAreaLineCountDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JTextAreaLineCountDemo());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
} 开发者_如何学Python
});
}
}
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.
It seems to me that this could not be the over all answer. If changing the font and extending the text the linecount is becoming incorrect.
EDIT: Solution You have to set the font for the textarea and for the attributed string. Now linecount is correct. Solution in Code.
package lineBreak;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.text.*;
import javax.swing.*;
public class JTextAreaLineCountDemo extends JPanel {
JTextArea textArea;
static Font f = new Font("Helvetiva", Font.ITALIC, 50);
public JTextAreaLineCountDemo() {
super(new GridBagLayout());
String inputStr = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo, Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo, Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo, Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmo";
textArea = new JTextArea(inputStr);
textArea.setEditable(false);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
// Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(textArea, c);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
**textArea.setFont(new Font("Arial", Font.BOLD, 22));**
System.out.println("Line count: " + countLines(textArea));
}
});
}
private static int countLines(JTextArea textArea) {
AttributedString text = new AttributedString(textArea.getText());
text.addAttribute(TextAttribute.FONT, f);
FontRenderContext frc = textArea.getFontMetrics(textArea.getFont())
.getFontRenderContext();
AttributedCharacterIterator charIt = text.getIterator();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
float formatWidth = (float) textArea.getSize().width;
lineMeasurer.setPosition(charIt.getBeginIndex());
int noLines = 0;
while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
lineMeasurer.nextLayout(formatWidth);
noLines++;
}
return noLines;
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("JTextAreaLineCountDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JTextAreaLineCountDemo());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Your countLines
method almost worked for me, but I had to make a few tweaks to make it work correctly in my case. I assume that you are using the default font and don't have a border on your JTextArea
, but using a non-default font or having a border (or both, as was my case) will cause your countLines
method to return the incorrect number. Below is my updated version that accounts for both of these factors (and also uses textArea.getWidth()
instead of textArea.getSize().width
).
private static int countLines(JTextArea textArea)
{
AttributedString text = new AttributedString(textArea.getText());
text.addAttribute(TextAttribute.FONT, textArea.getFont());
FontRenderContext frc = textArea.getFontMetrics(textArea.getFont()).getFontRenderContext();
AttributedCharacterIterator charIt = text.getIterator();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
Insets textAreaInsets = textArea.getInsets();
float formatWidth = textArea.getWidth() - textAreaInsets.left - textAreaInsets.right;
lineMeasurer.setPosition(charIt.getBeginIndex());
int noLines = 0;
while (lineMeasurer.getPosition() < charIt.getEndIndex())
{
lineMeasurer.nextLayout(formatWidth);
noLines++;
}
return noLines;
}
All credit for recognizing that the AttributedString
needed to have its font set to the font of the JTextArea
goes to Jenny
精彩评论