代码之家  ›  专栏  ›  技术社区  ›  user3166216

使JScrollPane控制多个组件

  •  0
  • user3166216  · 技术社区  · 10 年前

    对于我的应用程序,我正在设计一个脚本编辑器。现在我有一个 JPanel 其中包含另一个 面板 保存行号(位于左侧)的,以及 JTextArea 它用于允许用户键入他们的代码(位于右侧)。

    目前,我已经实现了 JScrollPane 文本区 以允许用户滚动他们的代码。

    对于 面板 包含行号,每次用户按下回车键时,它们都会递增。

    然而,问题是我想要相同的JScrollPane(在 文本区 )也控制行号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);
    }
    
    3 回复  |  直到 10 年前
        1
  •  5
  •   MadProgrammer    10 年前

    你应该使用 JScrollPane#setRowHeaderView 设置将显示在滚动窗格左侧的组件。

    这样做的好处是,当视图向右滚动时,行标题不会向左滚动。。。

    该示例故意使用换行符。。。

    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

        2
  •  2
  •   Java Devil    10 年前

    创建包含“行号”面板和“文本区域”的“外部面板”。

    然后将此新面板放入“滚动窗格”中,这样您就可以得到这样的安排:

    enter image description here

    代码中的内容如下:

    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);
    }
    
        3
  •  0
  •   Charlie    10 年前

    我建议将这两个组件放置到一个面板中,然后将该面板放置到滚动窗格中。