java 如何计算文本在 JTextArea 中的行数(和每行中的列数)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5979795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to calculate the number of rows (and columns in each row) a text takes in a JTextArea?
提问by Boro
After getting interested in the problem presented in the questionI tried to approach it few times and failed, and I do not like that :)
在对问题中提出的问题感兴趣后, 我尝试接近它几次但失败了,我不喜欢那样 :)
I think if the problem was split into sub issues it might help to solve it.
我认为如果将问题分解为子问题可能有助于解决它。
For simplicity lets assume the JTextArea will not change its size, so we do not need to worry about re-evaluation etc. I think the important issues are:
为简单起见,假设 JTextArea 不会改变其大小,因此我们无需担心重新评估等。我认为重要的问题是:
1.How to calculate the number of rows a certain text takes in a JTextArea?
1.如何计算某个文本在JTextArea中所占的行数?
2.What is the relation between the number of columns in a JTextArea and a number of characters it can fit in a row? So we can calculate row length.
2.JTextArea 的列数和它在一行中可以容纳的字符数有什么关系?所以我们可以计算行长。
Please find included below the sample code presenting the text area to process:
请在下面的示例代码中找到要处理的文本区域:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TextAreaLines
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
JPanel p = new JPanel();
JFrame f = new JFrame();
JTextArea ta = new JTextArea("dadsad sasdasdasdasdasd");
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
ta.setRows(5);
ta.setColumns(5);
p.add(ta);
f.setContentPane(p);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
//BTW the code below prints 1
System.out.println("ta.getLineCount()="+ta.getLineCount());
}
});
}
}
EDIT1:So I have come up with the following code but the problem is that the output is not what you see, i.e
EDIT1:所以我想出了下面的代码,但问题是输出不是你看到的,即
//for input
//JTextArea ta = new JTextArea("alfred abcdefghijklmnoprstuwvxyz abcdefg");
//we have output
//s=alfred abcdefghijk
//s=lmnoprstuwvxyz a
//s=bcdefg
FontMetrics fm = ta.getFontMetrics(ta.getFont());
String text = ta.getText();
List<String> texts = new ArrayList<String>();
String line = "";
//no word wrap
for(int i = 0;i < text.length(); i++)
{
char c = text.charAt(i);
if(fm.stringWidth(line +c) <= ta.getPreferredSize().width)
{
//System.out.println("in; line+c ="+(line + c));
line += c;
}
else
{
texts.add(line);//store the text
line = ""+c;//empty the line, add the last char
}
}
texts.add(line);
for(String s: texts)
System.out.println("s="+s);
What am I doing wrong, what am I forgetting about? There is no word wrap on the text area.
我做错了什么,我忘记了什么?文本区域没有自动换行。
EDIT2: @trashgod This is the output I am getting. Apparent from this is that we have different default fonts. And the problem in fact might be either font or even system dependent. (PS: I am on Win7).
EDIT2:@trashgod 这是我得到的输出。显然,我们有不同的默认字体。事实上,问题可能与字体有关,甚至可能与系统有关。(PS:我用的是Win7)。
line: Twas brillig and the slithy tovesD
line: id gyre and gimble in the wabe;
line count: 2
preferred: java.awt.Dimension[width=179,height=48]
bounds1: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.064453,w=170.0,h=15.09375]
layout1: java.awt.geom.Rectangle2D$Float[x=0.28125,y=-8.59375,w=168.25,h=11.125]
bounds2: java.awt.geom.Rectangle2D$Float[x=0.0,y=-12.064453,w=179.0,h=15.09375]
layout2: java.awt.geom.Rectangle2D$Float[x=0.921875,y=-8.59375,w=177.34375,h=11.125]
Compiling in my head what all of you guys are saying I think that the possibly reliable solution might be to hackthe way in which the text area sets its text, and take a full control over it. By running the algorithm (above one, please notice, as suggested by @trashgod the '<' was changed to '<=') in the setText of the area.
在我的脑海中编译你们所有人所说的我认为可能可靠的解决方案可能是破解文本区域设置文本的方式,并完全控制它。通过运行算法(上面的一个,请注意,正如@trashgod 所建议的那样,该区域的 setText 中的“<”已更改为“<=”)。
What got me to think like this... for example in the sample I have provided if you
change text of the textarea to JTextArea ta = new JTextArea("alfred abcdefghijkl\nmnoprstuwvxyz ab\ncdefg");
as it is calculated in my case then it will fit perfectly into the textarea.
是什么让我这么想……例如,在我提供的示例中,如果您将 textarea 的文本更改JTextArea ta = new JTextArea("alfred abcdefghijkl\nmnoprstuwvxyz ab\ncdefg");
为在我的情况下计算的那样,那么它将完全适合 textarea。
EDIT3:This is a kind of solution I quickly hacked, at least now the shown characters and calculated are exactly the same. Can someone else please check it out and let me know, possibly how it works on other machine then Win7? The example below is ready to use you should be able to resize the window and get the printout of lines the same as you see.
EDIT3:这是我快速破解的一种解决方案,至少现在显示的字符和计算的完全相同。其他人可以检查一下并告诉我,它可能如何在其他机器上运行,然后在 Win7 上运行?下面的示例已准备好使用,您应该能够调整窗口大小并打印出与您看到的相同的行。
import java.awt.BorderLayout;
import java.awt.FontMetrics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TextAreaLines
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
JPanel p = new JPanel(new BorderLayout());
JFrame f = new JFrame();
final JTextArea ta = new JTextArea("alfred abcdefghijklmnoprstuwvxyz abcdefg");
ta.addComponentListener(new ComponentAdapter()
{
@Override
public void componentResized(ComponentEvent e)
{
super.componentResized(e);
System.out.println("ta componentResized");
reformatTextAreaText(ta);
}
});
//ta.setWrapStyleWord(true);
ta.setLineWrap(true);
p.add(ta);
f.setContentPane(p);
f.setSize(200, 100);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private void reformatTextAreaText(JTextArea ta)
{
String text = ta.getText();
//remove all new line characters since we want to control line braking
text = text.replaceAll("\n", "");
FontMetrics fm = ta.getFontMetrics(ta.getFont());
List<String> texts = new ArrayList<String>();
String line = "";
//no word wrap
for(int i = 0; i < text.length(); i++)
{
char c = text.charAt(i);
if(fm.stringWidth(line + c) <= ta.getPreferredSize().width)
{
//System.out.println("in; line+c ="+(line + c));
line += c;
}
else
{
texts.add(line);//store the text
line = "" + c;//empty the line, add the last char
}
}
texts.add(line);
//print out of the lines
for(String s : texts)
System.out.println("s=" + s);
//build newText for the
String newText = "";
for(String s : texts)
newText += s + "\n";
ta.setText(newText);
}
});
}
}
Thanks in advance.
提前致谢。
采纳答案by trashgod
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:
真的没什么。我修改了您的示例以在宽度上使用“<=”并突出显示一些功能:
FontMetrics
notes, "the advance of aString
is not necessarily the sum of the advances of its characters measured in isolation…"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.
TextLayout
shows even tighter bounds, but note the "baseline-relative coordinates."The
getLineCount()
method countsline.separator
delimited lines, not wrapped lines.
FontMetrics
注意到,“aString
的进步不一定是单独衡量的其特征进步的总和……”文本组件的首选大小与最宽行的度量边界非常匹配。由于比例间距,这因字体而异。
TextLayout
显示更严格的界限,但请注意“基线相对坐标”。该
getLineCount()
方法计算line.separator
分隔行,而不是包装行。
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());
}
}
回答by camickr
Not sure if this helps but you need to set the width of the text area so that the view knows when to wrap the text. Once you set the size you can determine the preferred height. When you know the preferred height you can use the font metrice line height to determine the total number of lines including the wrapped lines if any.
不确定这是否有帮助,但您需要设置文本区域的宽度,以便视图知道何时换行文本。设置尺寸后,您可以确定首选高度。当您知道首选高度时,您可以使用字体公制行高来确定总行数,包括包装行(如果有)。
import java.awt.*;
import javax.swing.*;
public class TextAreaPreferredHeight extends JFrame
{
public TextAreaPreferredHeight()
{
JTextArea textArea = new JTextArea();
textArea.setText("one two three four five six seven eight nine ten");
textArea.setLineWrap( true );
textArea.setWrapStyleWord( true );
FontMetrics fm = textArea.getFontMetrics( textArea.getFont() );
int height = fm.getHeight();
System.out.println("000: " + textArea.getPreferredSize());
textArea.setSize(100, 1);
System.out.println("100: " + textArea.getPreferredSize());
System.out.println("lines : " + textArea.getPreferredSize().height / height);
textArea.setSize(200, 1);
System.out.println("200: " + textArea.getPreferredSize());
System.out.println("lines : " + textArea.getPreferredSize().height / height);
textArea.setSize(300, 1);
System.out.println("300: " + textArea.getPreferredSize());
System.out.println("lines : " + textArea.getPreferredSize().height / height);
add(textArea);
pack();
setVisible(true);
}
public static void main(String[] args)
{
new TextAreaPreferredHeight();
}
}
回答by jjnguy
One thing you can do is use FontMetrics
. I wrote some code for splitting JTextArea
s up at certain line numbers. The setup code looked like:
您可以做的一件事是使用FontMetrics
. 我编写了一些代码,用于JTextArea
在某些行号处拆分s。设置代码如下所示:
Graphics2D g = (Graphics2D) g2;
FontMetrics m = g.getFontMetrics();
int lineHeight = m.getHeight();
This will tell you how tall a line of text is.
这将告诉您一行文本有多高。
Unfortunately, letters have different widths in most fonts. But, you can use the following code to determine the width of a String.
不幸的是,大多数字体中的字母具有不同的宽度。但是,您可以使用以下代码来确定字符串的宽度。
int width = m.getStringBounds("Some String", g).getWidth();
I know this doesn't fully answer your question, but I hope it helps.
我知道这并不能完全回答您的问题,但我希望它有所帮助。
If you aren't using word wrap, here is the general algorithm you could use: (in the paint component method)
如果您不使用自动换行,这里是您可以使用的通用算法:(在绘制组件方法中)
String text[] = getText().split("\n");
String newText = "";
for (String line: text) {
newText = line + "| " + line.length() + "\n";
}
setText(newText);
That's the general idea. Not sure how well it would work out. Let me know if you try it.
这是一般的想法。不知道效果如何。如果您尝试,请告诉我。
回答by Catalina Island
I've seen people using TextLayout
for something like this.
我见过人们使用这样TextLayout
的东西。
回答by Aleadam
There was a similar question for android yesterday. The way I see it need to be solved is by an iterative approach:
昨天android有一个类似的问题。我认为需要解决的方法是通过迭代方法:
- Get JTextArea width
- Use the FontMetrics to get the width of a string, as jjnguy suggested
- split your string in words.
- Start measuring the witdh of a string adding one word at a time until you reach the area width. Save that number.
- Once you reached it, start a new iteration, adding one word at a time (beginning with the number saved).
- The numbers of lines will be the number of iterations.
- 获取 JTextArea 宽度
- 使用 FontMetrics 获取字符串的宽度,如 jjnguy 所建议的
- 将您的字符串拆分为单词。
- 开始测量字符串的宽度,一次添加一个单词,直到达到区域宽度。保存那个号码。
- 到达后,开始新的迭代,一次添加一个单词(从保存的数字开始)。
- 行数将是迭代次数。
Unfortunately, row length will depend on the font and the particular string (not every character as the same width). You can count the number of characters in the words in each iteration, and return an array of lengths or a length average.
不幸的是,行长将取决于字体和特定的字符串(并非每个字符都具有相同的宽度)。您可以计算每次迭代中单词的字符数,并返回长度数组或长度平均值。
This is the related android question: How to find android TextView number of characters per line?
这是相关的android问题:How to find android TextView number of characters per line?
回答by Doug Chamberlain
Okay, I had written a program that you could load in an image, and it would convert it to ascii art. I wanted it to automatically make the text area the right aspect ration based on the image that was input.
好的,我已经编写了一个程序,您可以将其加载到图像中,并将其转换为 ascii 艺术。我希望它根据输入的图像自动使文本区域具有正确的纵横比。
However, I could never get it to work quite right. I gave up and remarked out my attempts, here is the snippet of what I tried. What I ended up doing was just having a textarea that sometimes didn't fill all the way.
但是,我永远无法让它正常工作。我放弃并评论了我的尝试,这是我尝试过的片段。我最终做的只是有一个有时没有完全填满的文本区域。
//Graphics fontG = this.textBox4.CreateGraphics();
//fontG.PageUnit = GraphicsUnit.Point;
//SizeF fontSize = fontG.MeasureString(RowData, this.textBox4.Font,(SizeF) this.textBox4.ClientSize);
sb.AppendLine();
RowData = "";
//fontH += fontSize.Height + 1.2F;
//fontW = (int) fontSize.Width;
回答by m.i.k.e.
I found that counting the number of lines by text analysis only led to nothing. Instead my solution was calculating it from the preferred size of the text area ...
我发现通过文本分析计算行数只会导致一事无成。相反,我的解决方案是根据文本区域的首选大小计算它......
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
public class LineNumbering extends JFrame {
private static final long serialVersionUID = 1L;
private static final Font fixedFont = new Font("Monospaced", Font.PLAIN, 12);
private static JTextArea jta;
private static JTextArea lines;
private static String lineSeparator = "\n";
private static int numRows = 10;
private static int numCols = 30;
public LineNumbering() {
super("Line Numbering Example");
}
public static void createAndShowGUI() {
JFrame frame = new LineNumbering();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane();
jta = new JTextArea(numRows, numCols);
jta.setFont(fixedFont);
jta.setLineWrap(true);
jta.setWrapStyleWord(true);
lines = new JTextArea(numRows, 3);
lines.setEditable(false);
lines.setFocusable(false);
lines.setEnabled(false);
lines.setFont(fixedFont);
lines.setBackground(Color.LIGHT_GRAY);
lines.setDisabledTextColor(Color.BLACK);
// do initial line numbering
for (int i = 1; i <= lines.getRows(); i++) {
lines.append(i + System.getProperty("line.separator"));
}
final class DebugCaretListener implements CaretListener {
int rowHeight = jta.getFontMetrics(jta.getFont()).getHeight();
/**
* @return total of lines showed in the text area
*/
private int getTotalLinesInView() {
int insetsTotalHeight = jta.getInsets().top + jta.getInsets().bottom;
return (jta.getPreferredSize().height - insetsTotalHeight) / rowHeight;
}
/**
* @return text with line numbers
*/
public String getText() {
StringBuffer text = new StringBuffer();
int totalLines = getTotalLinesInView();
System.out.println("totalLines : " + totalLines);
for (int i = 1; i <= totalLines; i++) {
text.append(i);
if (i < totalLines) {
text.append(lineSeparator);
}
}
return text.toString();
}
/**
* <p>
* Reset line numbers on caret event. Since the total number of
* lines is calculated from preferred size of text area, we do this
* on an event that occurred after repainting of the text area.
* </p>
* (non-Javadoc)
*
* @see javax.swing.event.CaretListener#caretUpdate(javax.swing.event.CaretEvent)
*/
@Override
public void caretUpdate(CaretEvent e) {
int totalLines = getTotalLinesInView();
System.out.println("totalLines : " + totalLines);
if (totalLines >= numRows) {
lines.setText(getText());
}
}
}
jta.addCaretListener(new DebugCaretListener());
jsp.getViewport().add(jta);
jsp.setRowHeaderView(lines);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JPanel textPanel = new JPanel();
textPanel.add(jsp);
JPanel contentPanel = new JPanel();
contentPanel.add(textPanel);
frame.setContentPane(contentPanel);
contentPanel.setOpaque(true);
frame.pack();
frame.setPreferredSize(new Dimension(500, 500));
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}