Moving a view port over a larger image; JLablel+JScrollPane
Asked Answered
O

1

2

I have a JScrollPane m_jScrollPane with a JLabel m_jlImage being displayed inside of it. The m_jlImage is a screen capture with a red dot drawn where the user last clicked on the screen. I wish to move (read scroll) the viewing area of the m_jScrollPane over the red dot on the m_jlImage. lastClick is the last place the user clicked and is in the same coordinates as 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);
    }
Oiler answered 4/11, 2013 at 22:44 Comment(6)
Why not simply use JComponent#scrollRectToVisble instead?Geer
Is the user clicking on the JLabel or are you recording (some how) the live screen? Can you provide a simple runnable example?Geer
I am utilizing JNativeHook to capture mouse clicks, when a click happens I then take a screen capture and put the info in a Image object, which I draw a small red dot on where the last click occurred, and then I set the JLabel's icon to an icon made from the edited image. I then call the method above to scroll the scroll pane to the red dot on the JLabel is holds.Oiler
I assume you are scaling the image?Geer
No I keep the image the same size as the screen and create an icon out of it, and wish to just scroll the scroll pane enough up/down and left/right to view the red dot on the icon.Oiler
// I was using scrollRectToVisible but I am not sure how to construct the argument Rectangle; I tried new Rectangle(lastClick) this would never get the scrollpane to scroll though.Oiler
G
4

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).

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. This will ensure that the new point (and the dot) is visible within the scroll pane.

enter image description here

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

Geer answered 4/11, 2013 at 23:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.