【问题标题】:Java code to display lines number in jtextarea在 jtextarea 中显示行号的 Java 代码
【发布时间】:2013-09-12 15:39:28
【问题描述】:

我尝试编写 java 代码来弹出文本区域,如果我在右侧的 jtextarea 中添加 9000 行,它会显示 9000 行和垂直线。

反正我能做到吗?

有没有我可以像图片中一样添加行号..

请帮帮我!!谢谢!!

这是我的代码:

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.text.DefaultCaret;


public class test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub


        final JFrame frame = new JFrame("Test");
        JPanel panel = new JPanel();
        panel.setLayout((LayoutManager) new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.setOpaque(true);
        final JTextArea textArea = new JTextArea(20, 30);
        textArea.setWrapStyleWord(true);
        textArea.setEditable(true);
        textArea.setFont(Font.getFont(Font.SANS_SERIF));
        JScrollPane scroller = new JScrollPane(textArea);
        scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);



        JPanel inputpanel = new JPanel();
        inputpanel.setLayout(new FlowLayout());

        JButton button = new JButton("Enter");
        DefaultCaret caret = (DefaultCaret) textArea.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        panel.add(scroller);

        inputpanel.add(button);
        panel.add(inputpanel);
        frame.getContentPane().add(BorderLayout.CENTER, panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        frame.setResizable(false);


        button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {

            frame.dispose();
        }});
        frame.setSize(500, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   


    }

}

请帮帮我!!谢谢!!

【问题讨论】:

    标签: java swing jtextarea rowheader


    【解决方案1】:

    请参阅Text Component Line Number 了解可用作包含您的文本区域的滚动窗格的行标题的组件。

    【讨论】:

    • 非常感谢!!真的很有帮助!!再次感谢!!我希望你能帮我解决我的问题!!:)
    • 我用过一次,需要再用一次。谢谢@camickr。
    【解决方案2】:

    这是基于 here 的帖子的代码,但已修复,例如例如,在按住 ENTER 键时使用高效的字符串连接来避免延迟。

    import javax.swing.*;
    import javax.swing.text.Element;
    import java.awt.*;
    
    public class LineNumberingTextArea extends JTextArea
    {
        private JTextArea textArea;
    
        public LineNumberingTextArea(JTextArea textArea)
        {
            this.textArea = textArea;
            setBackground(Color.LIGHT_GRAY);
            setEditable(false);
        }
    
        public void updateLineNumbers()
        {
            String lineNumbersText = getLineNumbersText();
            setText(lineNumbersText);
        }
    
        private String getLineNumbersText()
        {
            int caretPosition = textArea.getDocument().getLength();
            Element root = textArea.getDocument().getDefaultRootElement();
            StringBuilder lineNumbersTextBuilder = new StringBuilder();
            lineNumbersTextBuilder.append("1").append(System.lineSeparator());
    
            for (int elementIndex = 2; elementIndex < root.getElementIndex(caretPosition) + 2; elementIndex++)
            {
                lineNumbersTextBuilder.append(elementIndex).append(System.lineSeparator());
            }
    
            return lineNumbersTextBuilder.toString();
        }
    }
    

    在您的 GUI 类中,只需实例化 LineNumberTextArea 并为其提供要附加到的 JTextArea 实例。还将JScrollPanerowHeaderView 设置为LineNumberingTextArea 实例:

    LineNumberingTextArea lineNumberingTextArea = new LineNumberingTextArea(myTextArea);
    myTextAreaScrollPane.setRowHeaderView(lineNumberingTextArea);
    

    最后为您的JTextArea 添加一个DocumentListener 以在文档更改时更新行号:

    myTextArea.getDocument().addDocumentListener(new DocumentListener()
    {
        @Override
        public void insertUpdate(DocumentEvent documentEvent)
        {
            lineNumberingTextArea.updateLineNumbers();
        }
    
        @Override
        public void removeUpdate(DocumentEvent documentEvent)
        {
            lineNumberingTextArea.updateLineNumbers();
        }
    
        @Override
        public void changedUpdate(DocumentEvent documentEvent)
        {
            lineNumberingTextArea.updateLineNumbers();
        }
    });
    

    【讨论】:

      【解决方案3】:

      JScrollPane 中,垂直滚动条无法与组件 TextLineNumber 一起正常工作。 为了纠正这个问题(以及另一个显示问题), 我将类 TextLineNumber 修改为 JTextLineNumber。 在这个新类中,文本组件类是 JTextPane,但您可以将其更改为 JTextComponent。 要使用它,请将您的 JTextLineNumber 组件放入 WEST 的面板(带有边框布局)中,并将您的文本组件放入 CENTER 中。 最后把这个面板放到一个滚动面板中。

      希望对大家有所帮助。

      JTextLineNumber

      import java.awt.*;
      import java.beans.*;
      import java.util.HashMap;
      import javax.swing.*;
      import javax.swing.event.*;
      import javax.swing.text.*;
      
      /**
       * This class will display line numbers for a related text component. The text     component must use the same line height for each line. TextLineNumber supports wrapped lines and will highlight the line number of the current line in the text component.
       *
       * This class was designed to be used as a component added to the row header of a     JScrollPane.
       */
      public class JTextLineNumber extends JPanel implements CaretListener, DocumentListener, PropertyChangeListener
      {
       private static final long serialVersionUID = 1L;
      public final static float                LEFT     = 0.0f;
      public final static float                CENTER = 0.5f;
      public final static float                RIGHT  = 1.0f;
      
      
      // Text component this TextTextLineNumber component is in sync with
      
      private JTextPane            component;
      
      // Properties that can be changed
      
      private boolean                          updateFont;
      private int                                  borderGap;
      private Color                                currentLineForeground;
      private float                                digitAlignment;
      private int                                  minimumDisplayDigits;
      
      // Keep history information to reduce the number of times the component
      // needs to be repainted
      
      private int                                  lastDigits;
      private int                                  lastHeight;
      private int                                  lastLine;
      
      private HashMap<String, FontMetrics> fonts;
      
      /**
       * Create a line number component for a text component. This minimum display width will be based on 3 digits.
       *
       * @param component
       *           the related text component
       */
      public JTextLineNumber(JTextPane component)
      {
          this(component, 3);
      }
      
      /**
       * Create a line number component for a text component.
       *
       * @param component
       *           the related text component
       * @param minimumDisplayDigits
       *           the number of digits used to calculate the minimum width of the component
       */
      public JTextLineNumber(JTextPane component,
                             int minimumDisplayDigits)
      {
          this.component = component;
          setBorder(null);
      
          //setFont(component.getFont());
      
          setBorder(null);
          setBorderGap(5);
          setCurrentLineForeground(Color.BLACK);
          setDigitAlignment(RIGHT);
          setMinimumDisplayDigits(minimumDisplayDigits);
      
          component.getDocument().addDocumentListener(this);
          component.addCaretListener(this);
          component.addPropertyChangeListener("font", this);
      }
      
      /**
       * Gets the update font property
       *
       * @return the update font property
       */
      public boolean getUpdateFont()
      {
          return updateFont;
      }
      
      /**
       * Set the update font property. Indicates whether this Font should be updated automatically when the Font of the related text component is changed.
       *
       * @param updateFont
       *           when true update the Font and repaint the line numbers, otherwise just repaint the line numbers.
       */
      public void setUpdateFont(boolean updateFont)
      {
          this.updateFont = updateFont;
      }
      
      /**
       * Gets the border gap
       *
       * @return the border gap in pixels
       */
      public int getBorderGap()
      {
          return borderGap;
      }
      
      /**
       * The border gap is used in calculating the left and right insets of the border. Default value is 5.
       *
       * @param borderGap
       *           the gap in pixels
       */
      public void setBorderGap(int borderGap)
      {
          this.borderGap = borderGap;
          lastDigits = 0;
          setPreferredWidth();
      }
      
      /**
       * Gets the current line rendering Color
       *
       * @return the Color used to render the current line number
       */
      public Color getCurrentLineForeground()
      {
          return currentLineForeground == null ? getForeground() : currentLineForeground;
      }
      
      /**
       * The Color used to render the current line digits. Default is Coolor.RED.
       *
       * @param currentLineForeground
       *           the Color used to render the current line
       */
      public void setCurrentLineForeground(Color currentLineForeground)
      {
          this.currentLineForeground = currentLineForeground;
      }
      
      /**
       * Gets the digit alignment
       *
       * @return the alignment of the painted digits
       */
      public float getDigitAlignment()
      {
          return digitAlignment;
      }
      
      /**
       * Specify the horizontal alignment of the digits within the component. Common values would be:
       * <ul>
       * <li>TextLineNumber.LEFT
       * <li>TextLineNumber.CENTER
       * <li>TextLineNumber.RIGHT (default)
       * </ul>
       * 
       * @param currentLineForeground
       *           the Color used to render the current line
       */
      public void setDigitAlignment(float digitAlignment)
      {
          this.digitAlignment = digitAlignment > 1.0f ? 1.0f : digitAlignment < 0.0f ? -1.0f : digitAlignment;
      }
      
      /**
       * Gets the minimum display digits
       *
       * @return the minimum display digits
       */
      public int getMinimumDisplayDigits()
      {
          return minimumDisplayDigits;
      }
      
      /**
       * Specify the mimimum number of digits used to calculate the preferred width of the component. Default is 3.
       *
       * @param minimumDisplayDigits
       *           the number digits used in the preferred width calculation
       */
      public void setMinimumDisplayDigits(int minimumDisplayDigits)
      {
          this.minimumDisplayDigits = minimumDisplayDigits;
          setPreferredWidth();
      }
      
      /**
       * Calculate the width needed to display the maximum line number
       */
      private void setPreferredWidth()
      {
          // Define the font to display the numbers.
          Font componentFont = component.getFont();
          Font font = new Font(componentFont.getFamily(), Font.BOLD, componentFont.getSize());
      
          Element root = component.getDocument().getDefaultRootElement();
          int lines = root.getElementCount();
          int digits = Math.max(String.valueOf(lines).length(), minimumDisplayDigits);
      
          // Update sizes when number of digits in the line number changes
      
          if (lastDigits != digits)
          {
              lastDigits = digits;
              FontMetrics fontMetrics = getFontMetrics(font);
              int iPreferredWidth = 0;
      
              if (digits <= 1)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("0");
              } else if (digits == 2)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("00");
              } else if (digits == 3)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("000");
              } else if (digits == 4)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("0000");
              } else if (digits == 5)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("00000");
              } else if (digits == 6)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("000000");
              } else if (digits == 7)
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("0000000");
              } else 
              {
                  iPreferredWidth = 10 + fontMetrics.stringWidth("00000000");
              }
      
              Dimension dimension = new Dimension(iPreferredWidth, 0);
              setPreferredSize(dimension);
              setSize(dimension);
          }
      }
      
      /**
       * Draw the line numbers
       */
      @Override
      public void paintComponent(Graphics g)
      {
          // Paint the background.
          Graphics2D g2d = (Graphics2D)g;
          g2d.setColor(component.getBackground());
          g2d.fillRect(0, 0, getWidth(), getHeight());
          // Paint a vertical line at right.
          g2d.setStroke(new BasicStroke(1));
          g2d.setColor(new Color(0, 0, 0, 64));
          g2d.drawLine(getWidth() - 1, 0, getWidth() - 1, getHeight());
          // Define the font.
          Font componentFont = component.getFont();
          Font font = new Font(componentFont.getFamily(), Font.BOLD, componentFont.getSize());
      
          // Determine the width of the space available to draw the line number
      
          FontMetrics fontMetrics = component.getFontMetrics(component.getFont());
          int iRightAlignement = getSize().width - 5;
      
          // Determine the rows to draw within the clipped bounds.
      
          Rectangle clip = g.getClipBounds();
          int rowStartOffset = component.viewToModel(new Point(0, clip.y));
          int endOffset = component.viewToModel(new Point(0, clip.y + clip.height));
          g2d.setFont(font);
      
          while (rowStartOffset <= endOffset)
          {
              try
              {
                  if (isCurrentLine(rowStartOffset))
                      g2d.setColor(new Color(0, 0, 0, 160));
                  else
                      g2d.setColor(new Color(0, 0, 0, 100));
      
                  // Get the line number as a string and then determine the
                  // "X" and "Y" offsets for drawing the string.
      
                  String lineNumber = getTextLineNumber(rowStartOffset);
                  int stringWidth = ((Graphics2D)g).getFontMetrics().stringWidth(lineNumber);
                  int iX = iRightAlignement - stringWidth;
                  int iY = getOffsetY(rowStartOffset, fontMetrics);
                  g2d.drawString(lineNumber, iX, iY);
      
                  // Move to the next row
      
                  rowStartOffset = Utilities.getRowEnd(component, rowStartOffset) + 1;
              } catch (Exception e)
              {
                  break;
              }
          }
      }
      
      /*
       * We need to know if the caret is currently positioned on the line we are about to paint so the line number can be highlighted.
       */
      private boolean isCurrentLine(int rowStartOffset)
      {
          int caretPosition = component.getCaretPosition();
          Element root = component.getDocument().getDefaultRootElement();
      
          if (root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition))
              return true;
          else
              return false;
      }
      
      /*
       * Get the line number to be drawn. The empty string will be returned when a line of text has wrapped.
       */
      protected String getTextLineNumber(int rowStartOffset)
      {
          Element root = component.getDocument().getDefaultRootElement();
          int index = root.getElementIndex(rowStartOffset);
          Element line = root.getElement(index);
      
          if (line.getStartOffset() == rowStartOffset)
              return String.valueOf(index + 1);
          else
              return "";
      }
      
      /*
       * Determine the Y offset for the current row
       */
      private int getOffsetY(int rowStartOffset,
                             FontMetrics fontMetrics)
               throws BadLocationException
      {
          // Get the bounding rectangle of the row
      
          Rectangle r = component.modelToView(rowStartOffset);
          int lineHeight = fontMetrics.getHeight();
          int y = r.y + r.height;
          int descent = 0;
      
          // The text needs to be positioned above the bottom of the bounding
          // rectangle based on the descent of the font(s) contained on the row.
      
          if (r.height == lineHeight) // default font is being used
          {
              descent = fontMetrics.getDescent();
          } else // We need to check all the attributes for font changes
          {
              if (fonts == null)
                  fonts = new HashMap<String, FontMetrics>();
      
              Element root = component.getDocument().getDefaultRootElement();
              int index = root.getElementIndex(rowStartOffset);
              Element line = root.getElement(index);
      
              for (int i = 0; i < line.getElementCount(); i++)
              {
                  Element child = line.getElement(i);
                  AttributeSet as = child.getAttributes();
                  String fontFamily = (String) as.getAttribute(StyleConstants.FontFamily);
                  Integer fontSize = (Integer) as.getAttribute(StyleConstants.FontSize);
                  String key = fontFamily + fontSize;
      
                  FontMetrics fm = fonts.get(key);
      
                  if (fm == null)
                  {
                      Font font = new Font(fontFamily, Font.PLAIN, fontSize);
                      fm = component.getFontMetrics(font);
                      fonts.put(key, fm);
                  }
      
                  descent = Math.max(descent, fm.getDescent());
              }
          }
      
          return y - descent;
      }
      
      //
      // Implement CaretListener interface
      //
      @Override
      public void caretUpdate(CaretEvent e)
      {
          // Get the line the caret is positioned on
      
          int caretPosition = component.getCaretPosition();
          Element root = component.getDocument().getDefaultRootElement();
          int currentLine = root.getElementIndex(caretPosition);
      
          // Need to repaint so the correct line number can be highlighted
      
          if (lastLine != currentLine)
          {
              repaint();
              lastLine = currentLine;
          }
      }
      
      //
      // Implement DocumentListener interface
      //
      @Override
      public void changedUpdate(DocumentEvent e)
      {
          documentChanged();
      }
      
      @Override
      public void insertUpdate(DocumentEvent e)
      {
          documentChanged();
      }
      
      @Override
      public void removeUpdate(DocumentEvent e)
      {
          documentChanged();
      }
      
      /*
       * A document change may affect the number of displayed lines of text. Therefore the lines numbers will also change.
       */
      private void documentChanged()
      {
          // View of the component has not been updated at the time
          // the DocumentEvent is fired
      
          SwingUtilities.invokeLater(new Runnable()
          {
              @Override
              public void run()
              {
                  try
                  {
                      int endPos = component.getDocument().getLength();
                      Rectangle rect = component.modelToView(endPos);
      
                      if (rect != null && rect.y != lastHeight)
                      {
                          setPreferredWidth();
                          repaint();
                          lastHeight = rect.y;
                      }
                  } catch (BadLocationException ex)
                  {
                      /* nothing to do */ }
              }
          });
      }
      
      //
      // Implement PropertyChangeListener interface
      //
      @Override
      public void propertyChange(PropertyChangeEvent evt)
      {
          if (evt.getNewValue() instanceof Font)
          {
              if (updateFont)
              {
                  Font newFont = (Font) evt.getNewValue();
                  setFont(newFont);
                  lastDigits = 0;
                  setPreferredWidth();
              } else
              {
                  repaint();
              }
          }
      }
      }
      

      【讨论】:

        【解决方案4】:

        你可能需要这样的东西。从 camickr 获取链接。

        在工作文件夹中从此链接添加类 TextLineNumber: 点击here

        import java.awt.BorderLayout;
        import java.awt.EventQueue;
        
        import javax.swing.JFrame;
        import javax.swing.JPanel;
        import javax.swing.JScrollPane;
        import javax.swing.border.EmptyBorder;
        import javax.swing.GroupLayout;
        import javax.swing.GroupLayout.Alignment;
        import javax.swing.JTextArea;
        import javax.swing.JLabel;
        import javax.swing.LayoutStyle.ComponentPlacement;
        import java.awt.Font;
        
        public class TextLine extends JFrame {
        
            private JPanel contentPane;
        
            /**
             * Launch the application.
             */
            public static void main(String[] args) {
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        try {
                            TextLine frame = new TextLine();
                            frame.setVisible(true);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        
            /**
             * Create the frame.
             */
            public TextLine() {
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                setBounds(100, 100, 540, 425);
                contentPane = new JPanel();
                contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
                setContentPane(contentPane);
        
                JTextArea textArea = new JTextArea();
                JScrollPane pane1 = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                        JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
                TextLineNumber tln1 = new TextLineNumber(textArea);
                pane1.setRowHeaderView(tln1);
                contentPane.add(pane1, null);
        
                JLabel lblNewLabel = new JLabel("                 Text Line Example");
                lblNewLabel.setFont(new Font("Tahoma", Font.PLAIN, 18));
                GroupLayout gl_contentPane = new GroupLayout(contentPane);
                gl_contentPane.setHorizontalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(
                        gl_contentPane
                                .createSequentialGroup()
                                .addContainerGap(88, Short.MAX_VALUE)
                                .addGroup(
                                        gl_contentPane
                                                .createParallelGroup(Alignment.LEADING)
                                                .addGroup(
                                                        Alignment.TRAILING,
                                                        gl_contentPane
                                                                .createSequentialGroup()
                                                                .addComponent(pane1, GroupLayout.PREFERRED_SIZE, 383,
                                                                        GroupLayout.PREFERRED_SIZE).addGap(43))
                                                .addGroup(
                                                        Alignment.TRAILING,
                                                        gl_contentPane
                                                                .createSequentialGroup()
                                                                .addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 333,
                                                                        GroupLayout.PREFERRED_SIZE).addGap(72)))));
                gl_contentPane.setVerticalGroup(gl_contentPane.createParallelGroup(Alignment.LEADING).addGroup(
                        Alignment.TRAILING,
                        gl_contentPane.createSequentialGroup()
                                .addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE, 51, GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(pane1, GroupLayout.PREFERRED_SIZE, 278, GroupLayout.PREFERRED_SIZE).addGap(42)));
                contentPane.setLayout(gl_contentPane);
            }
        }
        

        工人阶级:

        http://img28.imageshack.us/img28/3731/vm6p.png

        【讨论】:

        • -1 用于勺子喂食代码。 @user2726811,对不起,我在 13 小时前给了你答案。这张海报所做的只是将我博客中的两行代码复制到一个示例中。您应该能够自己弄清楚如何做到这一点。祝你下次能得到我的帮助。
        • 我自己做。我不只是想完成它,我想了解它是如何工作的。比我创造自己.. 非常感谢你的帮助!!
        • @user2726811 ,这个想法是应该归功于提出建议的人(并在这种情况下编写代码),而不是复制他人建议的人。
        • 嗯!!是的,没错!!
        猜你喜欢
        • 2014-11-16
        • 2011-03-17
        • 1970-01-01
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 2012-03-24
        • 2023-03-09
        • 2017-06-28
        相关资源
        最近更新 更多