对于我的应用程序,我正在设计一个脚本编辑器。目前我有一个JPanel
,其中包含另一个JPanel
,其中包含行号(位于左侧),还有一个JTextArea
,用于允许用户键入其代码(位于右侧)。
目前,我已在JScrollPane
上实施JTextArea
,以允许用户滚动其代码。
对于包含行号的JPanel
,每次用户按下回车键时,它们都会递增。
然而,问题是我想要相同的JScrollPane(在JTextArea
上实现的那个)来控制行号JPanel的滚动;即,当用户在JTextArea上滚动时,行号JPanel也应该滚动。但由于行号保存在JPanel中,我无法将该组件添加到JTextArea。
包含JTextArea和行号JPanel的JPanel类的构造函数:
private ScriptEditor() {
setBackground(Color.WHITE);
lineNumPanel = new LineNumberPanel();
scriptArea = new JTextArea();
scriptArea.setLineWrap(true);
scriptArea.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
scriptArea.setMargin(new Insets(3, 10, 0, 10));
JScrollPane scrollPane = new JScrollPane(scriptArea);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(width, height));
scriptArea.addKeyListener(this);
add(lineNumPanel);
add(scrollPane);
}
行号JPanel的构造函数,它在其自身中添加JLabel以表示行号:
public LineNumberPanel() {
setPreferredSize(new Dimension(width, height));
box = Box.createVerticalBox();
add(box);
//setup the label
label = new JLabel(String.valueOf(lineCount));
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
//setup the label alignment
label.setVerticalAlignment(JLabel.TOP);
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.TOP);
setAlignmentY(TOP_ALIGNMENT);
box.add(label);
}
答案 0 :(得分:4)
您应该使用JScrollPane#setRowHeaderView
设置将出现在滚动窗格左侧的组件。
这样做的好处是,当视图向右滚动时,行标题不会向左滚动...
该示例故意使用换行...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Element;
import javax.swing.text.Utilities;
public class ScrollColumnHeader {
public static void main(String[] args) {
new ScrollColumnHeader();
}
public ScrollColumnHeader() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextArea ta = new JTextArea(20, 40);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
JScrollPane sp = new JScrollPane(ta);
sp.setRowHeaderView(new LineNumberPane(ta));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class LineNumberPane extends JPanel {
private JTextArea ta;
public LineNumberPane(JTextArea ta) {
this.ta = ta;
ta.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
revalidate();
repaint();
}
@Override
public void removeUpdate(DocumentEvent e) {
revalidate();
repaint();
}
@Override
public void changedUpdate(DocumentEvent e) {
revalidate();
repaint();
}
});
}
@Override
public Dimension getPreferredSize() {
FontMetrics fm = getFontMetrics(getFont());
int lineCount = ta.getLineCount();
Insets insets = getInsets();
int min = fm.stringWidth("000");
int width = Math.max(min, fm.stringWidth(Integer.toString(lineCount))) + insets.left + insets.right;
int height = fm.getHeight() * lineCount;
return new Dimension(width, height);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fm = ta.getFontMetrics(ta.getFont());
Insets insets = getInsets();
Rectangle clip = g.getClipBounds();
int rowStartOffset = ta.viewToModel(new Point(0, clip.y));
int endOffset = ta.viewToModel(new Point(0, clip.y + clip.height));
Element root = ta.getDocument().getDefaultRootElement();
while (rowStartOffset <= endOffset) {
try {
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
String lineNumber = "";
if (line.getStartOffset() == rowStartOffset) {
lineNumber = String.valueOf(index + 1);
}
int stringWidth = fm.stringWidth(lineNumber);
int x = insets.left;
Rectangle r = ta.modelToView(rowStartOffset);
int y = r.y + r.height;
g.drawString(lineNumber, x, y - fm.getDescent());
// Move to the next row
rowStartOffset = Utilities.getRowEnd(ta, rowStartOffset) + 1;
} catch (Exception e) {
break;
}
}
}
}
}
正如我刚刚发现的那样,@ camickr有一个更有用的例子,Text Component Line Number
答案 1 :(得分:2)
创建一个外部面板,其中包含“行号”面板和“文本区域”。
然后将这个新面板放入滚动窗格,以便最终得到这样的安排:
代码中的内容如下:
private ScriptEditor() {
setBackground(Color.WHITE);
JPanel outerPanel = new JPanel();
lineNumPanel = new LineNumberPanel();
scriptArea = new JTextArea();
scriptArea.setLineWrap(true);
scriptArea.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
scriptArea.setMargin(new Insets(3, 10, 0, 10));
outerPanel.add(lineNumPanel, BorderLayout.WEST)
outerPanel.add(scriptArea, BorderLayout.CENTER)
JScrollPane scrollPane = new JScrollPane(outerPanel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(width, height));
scriptArea.addKeyListener(this);
add(lineNumPanel);
add(scrollPane);
}
答案 2 :(得分:0)
我建议将两个组件放入一个面板,然后将该面板放入滚动窗格。