我有一个 JScrollPane m_jScrollPane
,其中显示有一个 JLabel m_jlImage
。 m_jlImage
是一个屏幕截图,在用户上次单击屏幕的位置绘制了一个红点。我希望将 m_jScrollPane
的查看区域移动(读取滚动)到 m_jlImage
上的红点上。 lastClick
是用户最后点击的位置,与 m_jlImage
坐标相同。
事实证明这比我想象的要困难。
我决定获取点击点的值与沿同一轴的屏幕整个长度的比率,并按其最大值的相同比率滚动相应的滚动条。这似乎只有在屏幕上最后单击的点位于左上角时才有效。
我不知道如何处理当点击点位于屏幕边缘时的情况。这种情况会产生一个比率,导致滚动条以相同的比率滚动,但红点会滚动到 View 之外,因为它位于屏幕边缘。关于我如何克服这个问题有什么建议吗?
public void scrollViewToLastClick()
{
int clckH = lastClick.y;
int clckW = lastClick.x;
int picH = this.m_jlImage.getHeight();
int picW = this.m_jlImage.getWidth();
int ratW = (int)(m_jScrollPane.getWidth()*(double)clckW/(double)picW);
int ratH = (int)(m_jScrollPane.getHeight()*(double)clckH/(double)picH);
m_jScrollPane.getHorizontalScrollBar().setValue(ratW);
m_jScrollPane.getVerticalScrollBar().setValue(ratH);
}
最佳答案
这是一个非常基本的示例。它使用图像文件并将其放置在滚动 Pane 中(以迂回的方式)。
从那里,它只是使用 Swing Timer
来随机生成点(在图像的范围内)。
每次生成新点时,我只需使用 scrollToRectVisible
,向其传递我想要渲染的点的位置和大小。这将确保新点(和点)在滚动 Pane 中可见。
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class ScrollTest {
public static void main(String[] args) {
new ScrollTest();
}
private JScrollPane scrollPane;
private DesktopPane desktopPane;
public ScrollTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
desktopPane = new DesktopPane();
scrollPane = new JScrollPane(desktopPane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.setSize(desktopPane.getPreferredSize().width / 2, desktopPane.getPreferredSize().height / 2);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public class DesktopPane extends JLayeredPane {
private List<Point> points;
public DesktopPane() throws IOException {
points = new ArrayList<>(25);
final BufferedImage img = ImageIO.read(new File("Desktop.jpg"));
final JLabel desktop = new JLabel(new ImageIcon(img));
final JPanel overlay = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int xOff = desktop.getX();
int yOff = desktop.getY();
int count = 0;
FontMetrics fm = g.getFontMetrics();
int height = fm.getHeight();
for (Point p : points) {
g.setColor(Color.RED);
String text = Integer.toString(++count);
int width = fm.stringWidth(text);
int radius = Math.max(width, height) + 5;
int x = xOff + p.x - radius / 2;
int y = yOff + p.y - radius / 2;
g.fillOval(x, y, radius, radius);
g.setColor(Color.WHITE);
x += (radius - width) / 2;
y += ((radius - height) / 2) + fm.getAscent();
g.drawString(text, x, y);
}
}
};
overlay.setOpaque(false);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(desktop, gbc);
add(overlay, gbc);
setLayer(desktop, 0);
setLayer(overlay, 5);
overlay.setBorder(new LineBorder(Color.RED));
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int x = (int) Math.round(Math.random() * img.getWidth());
int y = (int) Math.round(Math.random() * img.getHeight());
points.add(new Point(x, y));
repaint();
FontMetrics fm = getFontMetrics(overlay.getFont());
int height = fm.getHeight();
String text = Integer.toString(points.size() - 1);
int width = fm.stringWidth(text);
int radius = Math.max(width, height) + 5;
scrollRectToVisible(new Rectangle(x - radius / 2, y - radius / 2, radius, radius));
}
});
timer.start();
}
}
}
现在,如果您想显示尽可能靠近中心的点,则需要额外的工作...
现在,如果您真的想玩得开心,请将延迟设置为 50 - 100 毫秒;)
关于java - 在更大的图像上移动视口(viewport); JLabel+JScrollPane,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19778717/