简体   繁体   English

在较大的图像上移动视口; JLablel + JScrollPane的

[英]Moving a view port over a larger image; JLablel+JScrollPane

I have a JScrollPane m_jScrollPane with a JLabel m_jlImage being displayed inside of it. 我有一个JScrollPane m_jScrollPane ,其中显示了一个JLabel m_jlImage The m_jlImage is a screen capture with a red dot drawn where the user last clicked on the screen. m_jlImage是屏幕截图,在用户最后一次单击屏幕的地方绘制了一个红点。 I wish to move (read scroll) the viewing area of the m_jScrollPane over the red dot on the m_jlImage . 我想移动(读滚动)的的可视面积m_jScrollPane过的红点m_jlImage lastClick is the last place the user clicked and is in the same coordinates as m_jlImage . lastClick是用户单击的最后一个位置,并且与m_jlImage处于相同的坐标。

This is proving to be more difficult that I thought. 我认为这被证明更加困难。

I decided to get the ratio of the click point's value to the entire length of the screen along the same axis, and scroll the corresponding scroll bar by that same ratio of its maximum. 我决定获取点击点值与沿着同一轴的整个屏幕长度的比率,并以相同的最大滚动比率滚动相应的滚动条。 This seems to only work if the point last clicked on the screen is in the upper left hand corner. 这似乎仅在屏幕上最后单击的点在左上角时有效。

I am not sure how to handle the situation when the clicked point is on the edge of the screen. 当点击的点位于屏幕边缘时,我不确定如何处理这种情况。 This scenario produces a ratio, causing the scroll bar to scroll by the same ratio, but the red dot is scrolled out of view because it is on the edge of the screen. 这种情况下会产生一个比率,导致滚动条以相同的比率滚动,但是由于红点位于屏幕边缘,因此红点被滚动到视线之外。 Any suggestions on how could I over come this? 关于我该如何解决的任何建议?

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

This is a pretty basic example. 这是一个非常基本的例子。 It uses a image file and places it within a scrollpane (in a round about way). 它使用图像文件并将其放置在滚动窗格中(以某种方式循环)。

From there, it simply uses a Swing Timer to randomly generate points (within the bounds of the image). 从那里开始,它仅使用Swing Timer随机生成点(在图像范围内)。

Each time a new point is generated, I simply use scrollToRectVisible , passing it the location and the size of the point I want to render. 每次生成新点时,我只需使用scrollToRectVisible ,向其传递要渲染的点的位置和大小。 This will ensure that the new point (and the dot) is visible within the scroll 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();
        }
    }
}

Now, if you want to display the point as close to the centre as possible, that will require additional work... 现在,如果要显示尽可能靠近中心的点,则需要进行额外的工作...

Now, if you really want to have fun, set the delay to something like 50 - 100 milliseconds ;) 现在,如果您真的想玩得开心,请将延迟设置为50-100毫秒;)

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

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