10

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

4

2 回答 2

5

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

策略:

  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().

于 2012-10-31T22:24:46.143 回答
3

好的,这是我对这个问题的看法......(虽然很好的问题)

在此处输入图像描述

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

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;
        }
    }
}
于 2012-11-01T01:15:21.553 回答