如何计算文本在 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 分隔行,而不是换行。

线: Twas brillig 和滑溜溜的 toves 线:在wabe中是否有回旋和万向节; 行数: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] 布局1: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] 布局2: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://***.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。我写了一些代码来在某些行号处拆分JTextAreas。设置代码如下所示:

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,如果 wordwrapfalse,这会简单得多。是否有理由需要启用自动换行? @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)

java中统计JTextArea中的行数有两个方法的问题!

Qt - 如何计算 .txt 文件中的行数

JTextArea 到 ArrayList<String> 的行

如何精确计算文本所占的行数

如何计算java中textArea中段落的行数? [复制]