Make JScrollPane control multiple components

后端 未结 3 921
被撕碎了的回忆
被撕碎了的回忆 2020-11-30 16:05

For my application I am designing a script editor. At the moment I have a JPanel which contains another JPanel that holds the line number (position

3条回答
  •  清歌不尽
    2020-11-30 16:19

    You should use JScrollPane#setRowHeaderView to set the component that will appear at the left hand side of the scroll pane.

    The benefit of this is the row header won't scroll to the left as the view scrolls to the right...

    The example deliberately uses line wrapping...

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

    And as I just discovered, @camickr has a much more useful example, Text Component Line Number

提交回复
热议问题