简体   繁体   English

如何确定哪些行在可滚动的JTextArea中可见?

[英]How to determine which lines are visible in scrollable JTextArea?

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

Interesting question that took me a while but I think I have a quite valid answer. 有趣的问题花了我一段时间,但我认为我有一个非常有效的答案。 Yet there might be some better ways; 然而,可能会有更好的方法; feel free to comment to improve the answer. 随时评论以改善答案。

Stategy: Stategy:

  1. Find which rows are visible using FontMetrics and getVisibleRect() 使用FontMetrics和getVisibleRect()查找哪些行可见
  2. Find the content of the visible rows. 查找可见行的内容。

So, my idea is that we should start from the visible rect. 所以,我的想法是我们应该从可见的矩形开始。 Based on that we can find out what is the first visible vertical offset ( getVisibleRect().y ) and the end of the visible vertical offset ( getVisibleRect().y+getVisibleRect().height ). 基于此,我们可以找出第一个可见垂直偏移( getVisibleRect().y )和可见垂直偏移的结束( getVisibleRect().y+getVisibleRect().height )。 Once we have that, by using the height of the font, we can determine which rows are visible. 一旦我们有了这个,通过使用字体的高度,我们可以确定哪些行是可见的。

The second part is to find out what does those rows contain. 第二部分是找出这些行包含的内容。 This is where I use Utilities with getRowStart() and getRowEnd() comes into play. 这是我使用UtilitiesgetRowStart()getRowEnd()发挥作用的地方。

Here is a sample code of what I was detailing (the result are output to the console as you resize the frame or scroll the textarea): 下面是我详细介绍的示例代码(在调整框架大小或滚动textarea时,结果会输出到控制台):

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

}

If you also have an horizontal scrollbar, I guess you should be able to compute horizontal offsets with FontMetrics.stringWidth() . 如果你还有一个水平滚动条,我想你应该能够用FontMetrics.stringWidth()计算水平偏移。

Okay, this is my take on the problem... (Nice question though) 好的,这是我对这个问题的看法......(但问题不错)

在此输入图像描述

There is a small consideration you need to have with this solution. 您需要对此解决方案进行一些考虑。 It will return partially displayed lines. 它将返回部分显示的行。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM