如何计算文本在 JTextArea 中的行数(以及每行中的列数)?
Posted
技术标签:
【中文标题】如何计算文本在 JTextArea 中的行数(以及每行中的列数)?【英文标题】:How to calculate the number of rows (and columns in each row) a text takes in a JTextArea? 【发布时间】:2011-08-24 04:49:22 【问题描述】:对question中提出的问题产生兴趣后 我试图接近它几次但失败了,我不喜欢那样:)
我认为如果将问题拆分为子问题,它可能有助于解决它。
为简单起见,我们假设 JTextArea 不会改变它的大小,所以我们不需要担心重新评估等。我认为重要的问题是:
1.如何计算某个文本在JTextArea中的行数?
2. JTextArea 中的列数和它可以容纳在一行中的字符数之间的关系是什么?所以我们可以计算行长。
请在下面找到显示要处理的文本区域的示例代码:
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: 所以我想出了以下代码,但问题是输出不是你看到的,即
//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);
我做错了什么,我忘记了什么?文本区域没有自动换行。
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]
在我的脑海中编译你们所有人所说的话,我认为可能可靠的解决方案可能是破解文本区域设置其文本的方式,并采取完整的控制它。通过运行该区域的 setText 中的算法(请注意,如@trashgod 所建议的,上述算法将“
是什么让我有这样的想法……例如,如果您在我提供的示例中
将 textarea 的文本更改为 JTextArea ta = new JTextArea("alfred abcdefghijkl\nmnoprstuwvxyz ab\ncdefg");
,因为它是在我的情况下计算的,那么它将完全适合 textarea。
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);
);
提前致谢。
【问题讨论】:
我在 C# 中做了这个[或类似的东西]...花了一些时间。我看看能不能把代码挖出来。 @Doug Chamberlain 非常欢迎所有的意见。希望你能找到它,Java实现中的问题将是相似的:) @camickr @jjnguy @Aleadam @Doug Chamberlain 伙计们请查看我的编辑。我正在等待您的建议 感谢所有相关人员。所有答案都非常有帮助,尽管我只能接受一个。我选择了@trashgod,因为它对我最有用,因为它为我提供了通往我想出的“黑客”解决方案的方法。再次感谢。 【参考方案1】:我做错了什么,我忘记了什么?
没什么,真的。我修改了您的示例以在宽度上使用“
FontMetrics
指出,“String
的进步不一定是其单独衡量的角色进步的总和……”
文本组件的首选大小与最宽行的公制边界非常匹配。由于成比例的间距,这因字体而异。
TextLayout
显示更严格的界限,但请注意“基线相对坐标”。
getLineCount()
方法计算 line.separator
分隔行,而不是换行。
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://***.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());
【讨论】:
感谢您的示例和您的时间 (+1)。在一些初步测试之后,这有助于确定控制该区域的文本设置可能是唯一的出路。 :) 请检查我的 EDIT2。 EDIT2:和我一样,您的文本区域的首选宽度与最宽行的宽度相匹配。 EDIT3:我在 Mac OS X 上看到匹配的行。【参考方案2】:您可以做的一件事是使用FontMetrics
。我写了一些代码来在某些行号处拆分JTextArea
s。设置代码如下所示:
Graphics2D g = (Graphics2D) g2;
FontMetrics m = g.getFontMetrics();
int lineHeight = m.getHeight();
这会告诉你一行文字有多高。
不幸的是,大多数字体中的字母都有不同的宽度。但是,您可以使用以下代码来确定字符串的宽度。
int width = m.getStringBounds("Some String", g).getWidth();
我知道这并不能完全回答您的问题,但希望对您有所帮助。
如果您不使用自动换行,这里是您可以使用的通用算法:(在绘制组件方法中)
String text[] = getText().split("\n");
String newText = "";
for (String line: text)
newText = line + "| " + line.length() + "\n";
setText(newText);
这是一般的想法。不知道它会有多好。如果您尝试,请告诉我。
【讨论】:
@jjnguy 感谢您的意见。是的,我正在考虑使用 FontMetrics。以为我不确定字符串宽度如何与 JTextArea 具有的列数相关。 @Boro,如果wordwrap
是 false
,这会简单得多。是否有理由需要启用自动换行?
@jjnguy 不是真的。我只是认为包括自动换行在内的解决方案会更通用。但是,如果您有没有 wordwrap 的解决方案,那么请继续,我想检查一下。
@Boro,实际上,我认为这可能会导致无限递归......射击。
@jjnguy 谢谢我喜欢它。它似乎有效,至少给出了一个起点,即这样我就有了一个正确的行号。很酷 :) +1 的帮助和代码。【参考方案3】:
不确定这是否有帮助,但您需要设置文本区域的宽度,以便视图知道何时换行。设置尺寸后,您可以确定首选高度。当您知道首选高度后,您可以使用字体度量行高来确定总行数,包括换行(如果有)。
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();
【讨论】:
+1 有趣的例子。现在让我想想怎么用吧。【参考方案4】:我见过有人使用TextLayout
来做类似的事情。
【讨论】:
快速浏览后。这看起来很有希望。谢谢。您是否也想到了一个特定的例子? @Boro:这是***.com/questions/2275259/…。 我喜欢@camickr 的answer,但我会在TextLayout
中加入第二个example。
感谢您的宝贵时间 (+1)。我将首先尝试使用 FontMetric,然后尝试 TextLayout,但我猜每种情况下都会使用类似的算法。如果我错了,请纠正。
@Boro:我不确定,但我认为 TextLayout 使用了 FontMetrics。【参考方案5】:
好的,我编写了一个程序,您可以将其加载到图像中,并将其转换为 ascii 艺术。我希望它能够根据输入的图像自动使文本区域具有正确的纵横比。
但是,我永远无法让它正常工作。我放弃并指出了我的尝试,这是我尝试过的sn-p。我最终做的只是有一个有时没有填满的 textarea。
//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;
【讨论】:
感谢@Doug Chamberlain,您的代码看起来像 jjnguy 建议的方法。【参考方案6】:昨天有一个关于android的类似问题。我认为需要解决的方法是通过迭代方法:
获取 JTextArea 宽度 按照 jjnguy 的建议,使用 FontMetrics 获取字符串的宽度 用单词拆分字符串。 开始测量字符串的宽度,一次添加一个单词,直到达到区域宽度。保存该号码。 一旦你达到它,开始一个新的迭代,一次添加一个单词(从保存的数字开始)。 行数就是迭代次数。不幸的是,行长将取决于字体和特定的字符串(不是每个字符都具有相同的宽度)。您可以计算每次迭代中单词中的字符数,并返回长度数组或长度平均值。
这是相关的android问题:How to find android TextView number of characters per line?
【讨论】:
很棒的提示。我正在考虑以这种方式接近它。让我试试看。 (+1)【参考方案7】:我发现通过文本分析来计算行数只会导致一无所获。相反,我的解决方案是根据文本区域的首选大小来计算它...
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();
);
【讨论】:
以上是关于如何计算文本在 JTextArea 中的行数(以及每行中的列数)?的主要内容,如果未能解决你的问题,请参考以下文章
如何从JTextArea中删除旧文本,以便文档大小不超过阈值? (JAVA)