如何计算JTextArea中的行数,包括因换行引起的行数?

11

我有一个JTextArea,我已经将其设置为自动换行和单词折行。我想要将JTextArea“压缩”到给定宽度的最小高度。

为了做到这一点,我计划使用以下方法计算字体的高度...

  Font font = jTextArea.getFont();
  FontMetrics fontMetrics = jTextArea.getFontMetrics(font);
  int lineHeight = fontMetrics.getAscent() + fontMetrics.getDescent();

如何计算JTextArea中使用的行数,包括由单词换行引起的行?在 JTextArea 中,getLineCount() 只计算回车符的数量,而忽略了换行符。

下面是一些演示代码,用于解决这个问题。我有一个监听器,每次调整窗口大小时打印出行数。目前,它总是打印 1,但我想要补偿单词换行并打印实际使用的行数。

编辑:我在下面的代码中包含了解决方案。静态的 countLines 方法提供了解决方案。

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();                                                                   
      }                                                                                       
    });                                                                                       
  }                                                                                           
}                                                                                             

在这个解决方案中,如果文本为空,会引发错误:java.lang.IllegalArgumentException: Text must contain at least one character。我建议用_try catch_ 包围 new LineBreakMeasurer(charIt, frc) - CarlosRos
此外,该方法没有考虑\n字符作为换行符的情况。 - CarlosRos
3个回答

5
你可以使用 LineBreakMeasurer 类。
LineBreakMeasurer 类允许将样式文本分成适合特定视觉进度的行(或段落)。对于希望显示适合特定宽度(称为换行宽度)的文本段落的客户端非常有用。LineBreakMeasurer 实现了最常用的断行策略:每个适合换行宽度的单词都放在同一行上。如果第一个单词不适合,则将适合换行宽度的所有字符放在同一行上。每行至少放置一个字符。

感觉应该有更简单的方法来做这件事。不过你的建议让我找对了方向,我已经把解决方案添加到问题中了。 - peskal

1

你的countLines方法对我来说几乎可用,但我不得不做一些调整才能在我的情况下正常工作。我假设您正在使用默认字体,并且没有边框在您的JTextArea上,但是使用非默认字体或具有边框(或两者都是,就像我的情况一样)将导致您的countLines方法返回不正确的行数。以下是我的更新版本,考虑了这两个因素(还使用textArea.getWidth()而不是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;
}

所有识别到AttributedString需要将其字体设置为JTextArea字体的功劳都归功于Jenny。


1

我认为这不可能是总的答案。如果更改字体并扩展文本,则行数计算将变得不正确。

编辑:解决方案 您需要为文本区域和属性字符串设置字体。现在,行数计算是正确的。代码中的解决方案。

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();                                                                   
      }                                                                                       
    });                                                                                       
  }                                                                                           
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接