系统:Win10
JDK:1.8.0_333
IDEA:2020.3.4
1.需求描述
最近在做一个 Swing 项目的时候,使用到了 JTextArea 组件,然后需要显示文本处于多少行,在左边需要显示它的行号,并且会随着行号的增加,显示行号的面板的宽度会自动扩展开
2.代码实现
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import java.awt.*;
/**
* @ClassName MyTextArea
* @Description 实现 JTextArea 显示行号
* @Author Li
* @Date 2022/8/28 13:55
* @ModifyDate 2022/8/28 13:55
* @Version 1.0
*/
public class MyTextArea extends JPanel {
private LinePanel linePanel;
private JTextArea textArea;
private JScrollPane scrollPane;
public MyTextArea() {
this.setLayout(new BorderLayout());
linePanel = new LinePanel();
linePanel.setPreferredSize(new Dimension(25, 10));
this.textArea = new JTextArea() {
public void paint(Graphics g) {
super.paint(g);
linePanel.repaint();
}
};
this.scrollPane = new JScrollPane(this.textArea);
this.add(linePanel, BorderLayout.WEST);
this.add(scrollPane, BorderLayout.CENTER);
}
// 内部类:显示行号的面板
private class LinePanel extends JPanel {
@Override
public void paint(Graphics g) {
super.paint(g);
// starting pos in document
int start = textArea.viewToModel(scrollPane.getViewport().getViewPosition());
// end pos in doc
int end = textArea.viewToModel(new Point(scrollPane.getViewport().getViewPosition().x + textArea.getWidth()
, scrollPane.getViewport().getViewPosition().y + textArea.getHeight()));
// translate offsets to lines
Document doc = textArea.getDocument();
int startLine = doc.getDefaultRootElement().getElementIndex(start) + 1;
int endLine = doc.getDefaultRootElement().getElementIndex(end) + 1;
int fontHeight = g.getFontMetrics(textArea.getFont()).getHeight();
int fontDesc = g.getFontMetrics(textArea.getFont()).getDescent();
int starting_y = -1;
try {
starting_y = textArea.modelToView(start).y -
scrollPane.getViewport().getViewPosition().y + fontHeight - fontDesc;
} catch (BadLocationException ble) {
ble.printStackTrace();
}
for (int line = startLine, y = starting_y; line <= endLine; y += fontHeight, line++) {
g.drawString(Integer.toString(line), getNumX(line), y);
}
}
// 获取当前行的行号 x 坐标
private int getNumX(int line) {
int width = linePanel.getWidth() - 5;
FontMetrics metrics = linePanel.getFontMetrics(this.getFont());
int fontHeight = metrics.getHeight();
int fontWidth = metrics.stringWidth("0");
int digit = 0;
while (line > 0) {
line = line / 10;
digit++;
}
int x = width - digit * fontWidth;
// 自适应调整宽度
if (x < 5) {
linePanel.setPreferredSize(new Dimension(getWidth() + fontWidth, fontHeight));
linePanel.updateUI();
}
return x;
}
}
// 主方法测试
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("JTextArea显示行号");
frame.setLayout(new BorderLayout());
MyTextArea textArea = new MyTextArea();
frame.getContentPane().add(textArea, BorderLayout.CENTER);
frame.setSize(new Dimension(400, 300));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}