java - 让JScrollPane控制多个组件

标签 java swing jscrollpane jtextarea

对于我的应用程序,我正在设计一个脚本编辑器。目前我有一个 JPanel ,其中包含另一个包含行号(位于左侧)的 JPanel ,以及一个使用的 JTextArea允许用户输入代码(位于右侧)。

目前,我在 JTextArea 上实现了 JScrollPane,以允许用户滚动浏览代码。

对于包含行号的 JPanel,每次用户按 Enter 键时行号都会递增。

但是,问题是我也想要相同的 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 的构造函数在其自身内添加 JLabels 来表示行号:

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

最佳答案

您应该使用 JScrollPane#setRowHeaderView 来设置将出现在滚动 Pane 左侧的组件。

这样做的好处是当 View 向右滚动时行标题不会向左滚动...

该示例故意使用换行...

Line Numbering

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

关于java - 让JScrollPane控制多个组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21766588/

相关文章:

java - 将未知数量的 JButton 添加到 JScrollPane

jquery - 在 jScrollPane 中搜索?

java - JLayeredPane 中的 JScrollPane 没有重绘?

java - 有没有办法找到 Java 程序访问了哪些内存地址以及访问顺序是什么?

java - 条件事件处理程序

java - 在 JDialog 上强制执行隐式最小大小?

java - 需要帮助来创建 java GUI

java - 保存少量数据的最佳方法

java - 使用 Spring MVC、JRebel 进行 Java Web 应用程序的最佳开发环境?

java - 如何获得带有空 JLabel 的 JPanel 以占用 GridBagLayout 中的空间