将 JScrollPane 的可见区域移动到特定位置
Moving the visible area of a JScrollPane to a specific position
我正在尝试使用 java 模拟调试光标的移动。我在将 JScrollPane 的可视区域设置到正确位置时遇到问题。
这是我想要实现的图片:
只有当我想跳转的行不可见时,我才想滚动。如果有帮助,可以使用 CodeDrowingPanel.NUMBER_OF_LINES
和 CodeDrowingPanel.FONT_SIZE
来完成计算,这些线是使用这些常量绘制在面板上的。
如果一定要跳的话,我要跳的线应该在最下面。
我必须记住,可见区域取决于屏幕分辨率。应用程序已最大化,无法调整大小。
编辑:
public void setCursorToLine(int line, JScrollPane codeArea)
{
if(line*CodeDrowingPanel.FONT_SIZE > this.getHeight()+43)
this.cursorPosition = this.getHeight()+43;
else
this.cursorPosition = line * CodeDrowingPanel.FONT_SIZE;
JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, codeArea);
if (viewPort != null)
{
Rectangle view = viewPort.getViewRect();
view.y += line - previousLine;
codeArea.scrollRectToVisible(view);
}
this.repaint();
}
这就是我现在尝试修改该行的方式。但它不起作用。我试着按照第一条评论中的第二个例子。不知道怎么用第二个评论的方法
这是一个简单的示例,使用 JList
和 JScrollPane
的 rowHeader
支持。
魔术基本上发生在这里...
int index = list.getSelectedIndex();
index++;
if (index >= list.getModel().getSize()) {
index = 0;
}
list.setSelectedIndex(index);
Rectangle cellBounds = list.getCellBounds(index, index);
list.scrollRectToVisible(cellBounds);
基本上,我们要求视图计算由所选索引表示的Rectangle
,并简单地要求组件滚动以便矩形可见
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList list;
public TestPane() {
setLayout(new BorderLayout());
DefaultListModel model = new DefaultListModel();
try (BufferedReader br = new BufferedReader(new FileReader(new File("src/test/Test.java")))) {
String text = null;
while ((text = br.readLine()) != null) {
model.addElement(text);
}
} catch (IOException exp) {
exp.printStackTrace();
}
list = new JList(model);
list.setSelectedIndex(0);
JScrollPane sp = new JScrollPane(list);
sp.setRowHeaderView(new Header(list));
add(sp);
JButton next = new JButton("Next");
next.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
index++;
if (index >= list.getModel().getSize()) {
index = 0;
}
list.setSelectedIndex(index);
Rectangle cellBounds = list.getCellBounds(index, index);
list.scrollRectToVisible(cellBounds);
}
});
add(next, BorderLayout.SOUTH);
}
}
protected class Header extends JPanel {
private JList list;
public Header(JList list) {
this.list = list;
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
repaint();
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Container parent = list.getParent();
if (parent instanceof JViewport) {
JViewport viewport = (JViewport) parent;
Graphics2D g2d = (Graphics2D) g.create();
int selectedRow = list.getSelectedIndex();
if (selectedRow >= 0) {
Rectangle cellBounds = list.getCellBounds(selectedRow, selectedRow);
cellBounds.y -= viewport.getViewPosition().y;
g2d.setColor(Color.RED);
g2d.fillRect(0, cellBounds.y, getWidth(), cellBounds.height);
}
g2d.dispose();
}
}
}
}
我认为行号不应该是文本的一部分。例如,您有一个水平滚动条。如果向右滚动,您将丢失行号。
相反,您应该使用一行 header 来显示行号。
参见 Text Component Line Number。它包含一个 class 可以为您自定义绘制行号。您可以使用将此组件添加到行 header.
那个class里面的绘画代码会高亮显示当前行号。如果要添加箭头,则需要修改绘制代码。在 paintComponent(...)
方法中,您可以添加以下内容:
g.drawString(lineNumber, x, y);
// Code to paint an arrow
if (isCurrentLine(rowStartOffset))
{
int height = fontMetrics.getAscent() - fontMetrics.getDescent();
Polygon triangle = new Polygon();
triangle.addPoint(borderGap, y);
triangle.addPoint(borderGap, y - height);
triangle.addPoint(borderGap + 10, y - height / 2);
Graphics2D g2d = (Graphics2D)g.create();
g2d.fill( triangle );
g2d.dispose();
}
还有一个变化要做。由于我们现在正在绘制箭头,因此我们需要增加组件的宽度。因此,在 setPreferredWidth(...)
方法中,您需要进行以下更改:
//int preferredWidth = insets.left + insets.right + width;
int preferredWidth = insets.left + insets.right + width + 15;
I want to scroll only if the line I want to jump it is not visible.
这里有一些代码可以做到这一点:
public static void gotoStartOfLine(JTextComponent component, int line)
{
Element root = component.getDocument().getDefaultRootElement();
line = Math.max(line, 1);
line = Math.min(line, root.getElementCount());
int startOfLineOffset = root.getElement( line - 1 ).getStartOffset();
component.setCaretPosition( startOfLineOffset );
}
我从 Text Utilities 中获取了上面的代码,它可能有其他感兴趣的方法(如果不是现在,将来)。
如果要在文本窗格中突出显示整行,也可以使用 Line Painter。
我正在尝试使用 java 模拟调试光标的移动。我在将 JScrollPane 的可视区域设置到正确位置时遇到问题。
这是我想要实现的图片:
只有当我想跳转的行不可见时,我才想滚动。如果有帮助,可以使用 CodeDrowingPanel.NUMBER_OF_LINES
和 CodeDrowingPanel.FONT_SIZE
来完成计算,这些线是使用这些常量绘制在面板上的。
如果一定要跳的话,我要跳的线应该在最下面。
我必须记住,可见区域取决于屏幕分辨率。应用程序已最大化,无法调整大小。
编辑:
public void setCursorToLine(int line, JScrollPane codeArea)
{
if(line*CodeDrowingPanel.FONT_SIZE > this.getHeight()+43)
this.cursorPosition = this.getHeight()+43;
else
this.cursorPosition = line * CodeDrowingPanel.FONT_SIZE;
JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, codeArea);
if (viewPort != null)
{
Rectangle view = viewPort.getViewRect();
view.y += line - previousLine;
codeArea.scrollRectToVisible(view);
}
this.repaint();
}
这就是我现在尝试修改该行的方式。但它不起作用。我试着按照第一条评论中的第二个例子。不知道怎么用第二个评论的方法
这是一个简单的示例,使用 JList
和 JScrollPane
的 rowHeader
支持。
魔术基本上发生在这里...
int index = list.getSelectedIndex();
index++;
if (index >= list.getModel().getSize()) {
index = 0;
}
list.setSelectedIndex(index);
Rectangle cellBounds = list.getCellBounds(index, index);
list.scrollRectToVisible(cellBounds);
基本上,我们要求视图计算由所选索引表示的Rectangle
,并简单地要求组件滚动以便矩形可见
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList list;
public TestPane() {
setLayout(new BorderLayout());
DefaultListModel model = new DefaultListModel();
try (BufferedReader br = new BufferedReader(new FileReader(new File("src/test/Test.java")))) {
String text = null;
while ((text = br.readLine()) != null) {
model.addElement(text);
}
} catch (IOException exp) {
exp.printStackTrace();
}
list = new JList(model);
list.setSelectedIndex(0);
JScrollPane sp = new JScrollPane(list);
sp.setRowHeaderView(new Header(list));
add(sp);
JButton next = new JButton("Next");
next.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
index++;
if (index >= list.getModel().getSize()) {
index = 0;
}
list.setSelectedIndex(index);
Rectangle cellBounds = list.getCellBounds(index, index);
list.scrollRectToVisible(cellBounds);
}
});
add(next, BorderLayout.SOUTH);
}
}
protected class Header extends JPanel {
private JList list;
public Header(JList list) {
this.list = list;
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
repaint();
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Container parent = list.getParent();
if (parent instanceof JViewport) {
JViewport viewport = (JViewport) parent;
Graphics2D g2d = (Graphics2D) g.create();
int selectedRow = list.getSelectedIndex();
if (selectedRow >= 0) {
Rectangle cellBounds = list.getCellBounds(selectedRow, selectedRow);
cellBounds.y -= viewport.getViewPosition().y;
g2d.setColor(Color.RED);
g2d.fillRect(0, cellBounds.y, getWidth(), cellBounds.height);
}
g2d.dispose();
}
}
}
}
我认为行号不应该是文本的一部分。例如,您有一个水平滚动条。如果向右滚动,您将丢失行号。
相反,您应该使用一行 header 来显示行号。
参见 Text Component Line Number。它包含一个 class 可以为您自定义绘制行号。您可以使用将此组件添加到行 header.
那个class里面的绘画代码会高亮显示当前行号。如果要添加箭头,则需要修改绘制代码。在 paintComponent(...)
方法中,您可以添加以下内容:
g.drawString(lineNumber, x, y);
// Code to paint an arrow
if (isCurrentLine(rowStartOffset))
{
int height = fontMetrics.getAscent() - fontMetrics.getDescent();
Polygon triangle = new Polygon();
triangle.addPoint(borderGap, y);
triangle.addPoint(borderGap, y - height);
triangle.addPoint(borderGap + 10, y - height / 2);
Graphics2D g2d = (Graphics2D)g.create();
g2d.fill( triangle );
g2d.dispose();
}
还有一个变化要做。由于我们现在正在绘制箭头,因此我们需要增加组件的宽度。因此,在 setPreferredWidth(...)
方法中,您需要进行以下更改:
//int preferredWidth = insets.left + insets.right + width;
int preferredWidth = insets.left + insets.right + width + 15;
I want to scroll only if the line I want to jump it is not visible.
这里有一些代码可以做到这一点:
public static void gotoStartOfLine(JTextComponent component, int line)
{
Element root = component.getDocument().getDefaultRootElement();
line = Math.max(line, 1);
line = Math.min(line, root.getElementCount());
int startOfLineOffset = root.getElement( line - 1 ).getStartOffset();
component.setCaretPosition( startOfLineOffset );
}
我从 Text Utilities 中获取了上面的代码,它可能有其他感兴趣的方法(如果不是现在,将来)。
如果要在文本窗格中突出显示整行,也可以使用 Line Painter。