如何计算文本在JTextArea中所占的行数(以及每行的列数)?
在对这个问题中提出的问题感兴趣之后,我尝试了几次,失败了,我不喜欢那样:)
我认为,如果问题分解成小问题,可能有助于解决问题。
为了简单起见,假设JTextArea不会改变它的大小,所以我们不需要担心重新评估等。我认为重要的问题是:
1.如何计算某个文本在JTextArea中所占的行数?
2.JTextArea中的列数与它可以放在一行中的字符数之间的关系是什么? 所以我们可以计算行长。
请在下面find提供要处理的文本区域的示例代码:
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()); } }); } }
编辑1:所以我想出了下面的代码,但问题是,输出不是你所看到的,即
//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]
在我脑海中编辑你们所有人都在说我认为可能可靠的解决scheme可能是破解文本区域设置文本的方式,并对其进行全面控制。 通过运行algorithm(上面的一个请注意,正如@trashgod所build议的那样,在该区域的setText中将'<'改为'<=')。
是什么让我这样想…例如在我提供的示例中,如果您将textarea的文本更改为JTextArea ta = new JTextArea("alfred abcdefghijkl\nmnoprstuwvxyz ab\ncdefg");
因为它是在我的情况下计算,那么它将完全符合textarea。
编辑3:这是我迅速入侵的一种解决scheme,至less现在显示的字符和计算结果是完全一样的。 可以别人请检查出来,让我知道,可能是如何在其他机器然后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); } }); } }
提前致谢。
我做错了什么,我忘了什么?
真的没什么。 我修改了你的例子,在宽度上使用“<=”,并突出显示一些function:
-
FontMetrics
指出,“一个String
的进步不一定是其孤立测量字符的进步的总和…” -
对于最宽的行,文本组件的首选大小与度量边界相匹配。 由于比例间距的原因,字体会有所不同。
-
TextLayout
显示更紧密的边界,但请注意“基线相对坐标”。 -
getLineCount()
方法计算line.separator
getLineCount()
,而不是包装线。
行:Twas brillig和slithy toves 线:在摇摆中是否有旋转和g </s>; 行数:2 首选: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()); } }
你可以做的一件事就是使用FontMetrics
。 我写了一些代码来分割JTextArea
在某些行号。 设置代码如下所示:
Graphics2D g = (Graphics2D) g2; FontMetrics m = g.getFontMetrics(); int lineHeight = m.getHeight();
这会告诉你一行文字有多高。
不幸的是,大多数字体的字母宽度不同。 但是,您可以使用下面的代码来确定一个string的宽度。
int width = m.getStringBounds("Some String", g).getWidth();
我知道这不能完全回答你的问题,但我希望它有帮助。
如果你不使用自动换行,这里是你可以使用的一般algorithm:(在绘画组件方法中)
String text[] = getText().split("\n"); String newText = ""; for (String line: text) { newText = line + "| " + line.length() + "\n"; } setText(newText);
这是一般的想法。 不知道它会如何工作。 让我知道,如果你尝试。
不知道这是否有帮助,但您需要设置文本区域的宽度,以便视图知道何时包装文本。 一旦你设置的大小,你可以确定首选的高度。 当您知道首选高度时,可以使用字体测量线高度来确定包括被包装的线条(如果有的话)的总数。
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(); } }
我已经看到人们使用TextLayout
的东西。
好吧,我已经写了一个程序,你可以载入一个图像,它会将其转换为ASCII艺术。 我希望它能够根据input的图像自动将文本区域设置为正确的比例。
但是,我永远不能把它做好。 我放弃了,并指出了我的尝试,这里是我尝试的片段。 我最终做的只是有一个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;
昨天有一个类似的安卓问题。 我认为需要解决的方法是通过迭代方法:
- 获取JTextArea宽度
- 按照jjnguy的build议,使用FontMetrics获取string的宽度
- 用文字分割你的string。
- 开始测量一个string的同时加一个单词,直到达到区域宽度。 保存该号码。
- 一旦你到达它,开始一个新的迭代,一次添加一个单词(从保存的数字开始)。
- 线的数量将是迭代次数。
不幸的是,行长取决于字体和特定的string(不是每个字符都是相同的宽度)。 您可以计算每次迭代中单词的字符数,并返回一个长度数组或长度平均数。
这是相关的android问题: 如何find每行android字符数字符?
我发现通过文本分析来计算行数只会导致一无所获。 相反,我的解决scheme是从文本区域的首选大小计算它…
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(); } }); } }