如何确定可滚动 JTextArea(JScrollPane 中的 JTextArea)中第一条可见行的数量和当前可见的行数?
问问题
2412 次
2 回答
5
有趣的问题花了我一段时间,但我认为我有一个非常有效的答案。然而,可能有一些更好的方法;随时发表评论以改进答案。
策略:
- 使用 FontMetrics 和 getVisibleRect() 查找哪些行可见
- 查找可见行的内容。
所以,我的想法是我们应该从可见的矩形开始。基于此,我们可以找出第一个可见的垂直偏移量(getVisibleRect().y
)和可见垂直偏移量的结束(getVisibleRect().y+getVisibleRect().height
)。一旦我们有了它,通过使用字体的高度,我们可以确定哪些行是可见的。
第二部分是找出这些行包含什么。这是我使用Utilities
并getRowStart()
发挥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 回答