【问题标题】:How to determine which lines are visible in scrollable JTextArea?如何确定哪些行在可滚动的 JTextArea 中可见?
【发布时间】:2012-10-21 08:44:15
【问题描述】:

如何确定可滚动JTextArea(JScrollPane中的JTextArea)中第一条可见行的数量和当前可见的行数?

【问题讨论】:

    标签: java swing user-interface jtextcomponent


    【解决方案1】:

    有趣的问题花了我一段时间,但我认为我有一个非常有效的答案。然而,可能有一些更好的方法;随时发表评论以改进答案。

    策略:

    1. 使用 FontMetrics 和 getVisibleRect() 查找哪些行可见
    2. 查找可见行的内容。

    所以,我的想法是我们应该从可见的矩形开始。基于此,我们可以找出第一个可见垂直偏移量(getVisibleRect().y)和可见垂直偏移量的结尾(getVisibleRect().y+getVisibleRect().height)。一旦我们有了这个,通过使用字体的高度,我们可以确定哪些行是可见的。

    第二部分是找出这些行包含什么。这就是我使用UtilitiesgetRowStart()getRowEnd() 发挥作用的地方。

    这是我详细说明的示例代码(当您调整框架大小或滚动文本区域时,结果将输出到控制台):

    import java.awt.event.AdjustmentEvent;
    import java.awt.event.AdjustmentListener;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Utilities;
    
    public class TestTextAre {
    
        private void initUI() {
            JFrame frame = new JFrame(TestTextAre.class.getSimpleName());
            final JTextArea ta = new JTextArea(5, 25);
            ta.setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has "
                    + "been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of "
                    + "type and scrambled it to make a type specimen book.\n It has survived not only five centuries, but also the "
                    + "leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the"
                    + " release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing "
                    + "software like Aldus PageMaker including versions of Lorem Ipsum.");
            ta.setColumns(20);
            ta.setEditable(false);
            ta.setLineWrap(true);
            ta.setWrapStyleWord(true);
            JScrollPane scrollpane = new JScrollPane(ta);
            scrollpane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
    
                @Override
                public void adjustmentValueChanged(AdjustmentEvent e) {
                    if (e.getValueIsAdjusting()) {
                        return;
                    }
                    printTAVisibleInfo(ta);
                }
            });
            frame.add(scrollpane);
            frame.addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e) {
                    printTAVisibleInfo(ta);
    
                }
    
            });
            frame.pack();
            frame.setVisible(true);
        }
    
        private void printTAVisibleInfo(final JTextArea ta) {
            List<Row> taInfo = getTAInfo(ta);
            int y1 = ta.getVisibleRect().y;
            int y2 = y1 + ta.getVisibleRect().height;
            int lineHeight = ta.getFontMetrics(ta.getFont()).getHeight();
            int startRow = (int) Math.ceil((double) y1 / lineHeight);
            int endRow = (int) Math.floor((double) y2 / lineHeight);
            endRow = Math.min(endRow, taInfo.size());
            System.err.println(startRow + " " + endRow);
            for (int i = startRow; i < endRow; i++) {
                System.err.println(taInfo.get(i) + " is visible");
            }
        }
    
        private List<Row> getTAInfo(final JTextArea ta) {
            List<Row> taInfo = new ArrayList<TestTextAre.Row>();
            int start = 0;
            int end = -1;
            int i = 0;
            try {
                do {
                    start = Utilities.getRowStart(ta, end + 1);
                    end = Utilities.getRowEnd(ta, start);
                    taInfo.add(new Row(i, start, end, ta.getDocument().getText(start, end - start)));
                    i++;
                } while (end < ta.getDocument().getLength());
    
            } catch (BadLocationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return taInfo;
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new TestTextAre().initUI();
                }
            });
        }
    
        public static class Row {
            private final int row;
            private final int start;
            private final int end;
            private final String text;
    
            public Row(int row, int start, int end, String text) {
                super();
                this.row = row;
                this.start = start;
                this.end = end;
                this.text = text;
            }
    
            public int getRow() {
                return row;
            }
    
            public int getStart() {
                return start;
            }
    
            public int getEnd() {
                return end;
            }
    
            public String getText() {
                return text;
            }
    
            @Override
            public String toString() {
                return "Row " + row + " contains " + text + " (" + start + "," + end + ")";
            }
        }
    
    }
    

    如果你还有一个水平滚动条,我想你应该能够用FontMetrics.stringWidth() 计算水平偏移量。

    【讨论】:

    • +1 但使用 viewToModel() 方法传递可见矩形 y 比使用字体度量计算更容易
    • @StanislavL 是的,你是对的。 MadProgrammer 的解决方案更优雅。
    【解决方案2】:

    好的,这就是我对这个问题的看法……(不过很好的问题)

    对于此解决方案,您需要考虑一个小问题。它将返回部分显示的行。

    public class TestTextArea {
    
        public static void main(String[] args) {
            new TestTextArea();
        }
    
        public TestTextArea() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException ex) {
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
    
                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new TestTextAreaPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestTextAreaPane extends JPanel {
    
            private JTextArea textArea;
            private JTextArea viewText;
    
            public TestTextAreaPane() {
                setLayout(new GridLayout(2, 1));
                textArea = new JTextArea(20, 100);
                textArea.setWrapStyleWord(true);
                textArea.setLineWrap(true);
                textArea.setText(loadText());
    
                viewText = new JTextArea(20, 100);
                viewText.setWrapStyleWord(false);
                viewText.setLineWrap(false);
                viewText.setEditable(false);
    
                JScrollPane scrollPane = new JScrollPane(textArea);
                add(scrollPane);
    
                add(viewText);
    
                scrollPane.getViewport().addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        if (textArea.getText().length() > 0) {
                            JViewport viewport = (JViewport) e.getSource();
                            Rectangle viewRect = viewport.getViewRect();
    
                            Point p = viewRect.getLocation();
                            int startIndex = textArea.viewToModel(p);
    
                            p.x += viewRect.width;
                            p.y += viewRect.height;
                            int endIndex = textArea.viewToModel(p);
    
                            if (endIndex - startIndex >= 0) {
    
                                try {
                                    viewText.setText(textArea.getText(startIndex, (endIndex - startIndex)));
                                } catch (BadLocationException ex) {
                                    ex.printStackTrace();
                                    viewText.setText(ex.getMessage());
                                }
    
                            }
    
                        }
    
                    }
                });
    
            }
    
            protected String loadText() {
                String text = null;
                File file = new File("src/testtextarea/TestTextArea.java");
    
                BufferedReader br = null;
                try {
                    br = new BufferedReader(new FileReader(file));
                    StringBuilder sb = new StringBuilder(128);
                    String read = null;
                    while ((read = br.readLine()) != null) {
                        sb.append(read).append("\n");
                    }
    
                    text = sb.toString();
                } catch (IOException exp) {
                    exp.printStackTrace();
                } finally {
                    try {
                        br.close();
                    } catch (Exception e) {
                    }
                }
    
                return text;
            }
        }
    }
    

    【讨论】:

    • 看起来 viewToModel() 是要走的路。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    相关资源
    最近更新 更多