2011-06-16 291 views
10

Tengo un JTextArea para el que he configurado word-wrap y wrap-style-word en verdadero. Quiero "empacar" el JTextArea a la altura mínima posible dado un ancho especificado.¿Cómo contar el número de líneas en un JTextArea, incluidas las causadas por el ajuste?

Para hacer esto, estoy planeando el cálculo de la altura de la fuente usando ...

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

... y luego multiplicar por el número de líneas utilizadas en el JTextArea. El problema es que JTextArea.getLineCount() cuenta el número de retornos de línea que ignoran las líneas envueltas.

¿Cómo cuento el número de líneas utilizadas en un JTextArea, incluidas las causadas por el ajuste de palabras?

Aquí hay un código de demostración para hacer que jugar con este problema sea más fácil. Tengo un oyente que imprime el número de líneas cada vez que se cambia el tamaño de la ventana. Por el momento, siempre imprime 1, pero quiero compensar el ajuste de palabras e imprimir cuántas líneas se están usando en realidad.

EDIT: He incluido la solución al problema en el siguiente código. El método static CountLines da la solución.

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

ver también [Cómo calcular el número de filas ... en un JTextArea?] (Http://stackoverflow.com/questions/5979795). – trashgod

+0

En esta solución, si el texto está vacío arrojará el error: 'java.lang.IllegalArgumentException: El texto debe contener al menos un carácter' Sugiero rodear _try catch_ the' new LineBreakMeasurer (charIt, frc) ' – CarlosRos

+0

Además, este método no tiene en cuenta el carácter '\ n' como nueva línea. – CarlosRos

Respuesta

5

Puede utilizar LineBreakMeasurer clase.

The LineBreakMeasurer class allows styled text to be broken into lines (or segments) that fit within a particular visual advance. This is useful for clients who wish to display a paragraph of text that fits within a specific width, called the wrapping width.LineBreakMeasurer implements the most commonly used line-breaking policy: Every word that fits within the wrapping width is placed on the line. If the first word does not fit, then all of the characters that fit within the wrapping width are placed on the line. At least one character is placed on each line.

+0

Parece que debería haber una manera más fácil de hacerlo. Sin embargo, su sugerencia me condujo por buen camino y agregué la solución a la pregunta. – peskal

1

Me parece que esta no podría ser la respuesta general. Si cambia la fuente y extiende el texto, el conteo de líneas se está volviendo incorrecto.

EDIT: Solución Hay que establecer la fuente para el área de texto y para la cadena con atributos. Ahora el conteo de líneas es correcto. Solución en Código.

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

Su método countLines casi trabajado para mí, pero tenía que hacer algunos ajustes para que funcione correctamente en mi caso. Supongo que está usando la fuente predeterminada y no tiene un borde en su JTextArea, pero usar una fuente no predeterminada o tener un borde (o ambos, como era mi caso) hará que su método countLines devuelva el número incorrecto . Debajo está mi versión actualizada que da cuenta de estos dos factores (y también usa textArea.getWidth() en lugar de 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; 
} 

All credit for recognizing that the AttributedString needed to have its font set to the font of the JTextArea goes to Jenny

Cuestiones relacionadas