Перемещение окна просмотра на увеличенное изображение; JLablel+JScrollPane

У меня есть JScrollPane m_jScrollPane, внутри которого отображается JLabel m_jlImage. m_jlImage — это снимок экрана с красной точкой, нарисованной там, где пользователь в последний раз щелкнул по экрану. Я хочу переместить (читай прокрутить) область просмотра m_jScrollPane над красной точкой на m_jlImage. lastClick — это последнее место, на которое щелкнул пользователь, и оно находится в тех же координатах, что и m_jlImage.

Это оказывается сложнее, чем я думал.

Я решил получить отношение значения точки клика ко всей длине экрана по той же оси, и прокрутить соответствующую полосу прокрутки на такое же отношение ее максимума. Кажется, это работает только в том случае, если точка, на которую последний раз нажали на экране, находится в верхнем левом углу.

Я не уверен, как справиться с ситуацией, когда точка щелчка находится на краю экрана. Этот сценарий создает соотношение, в результате чего полоса прокрутки прокручивается с тем же соотношением, но красная точка прокручивается вне поля зрения, поскольку она находится на краю экрана. Любые предложения о том, как я могу преодолеть это?

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

person Community    schedule 04.11.2013    source источник
comment
Почему бы просто не использовать JComponent#scrollRectToVisble вместо этого?   -  person MadProgrammer    schedule 05.11.2013
comment
Пользователь нажимает на JLabel или вы записываете (каким-то образом) экран в реальном времени? Можете ли вы привести простой работающий пример?   -  person MadProgrammer    schedule 05.11.2013
comment
Я использую JNativeHook для захвата щелчков мышью, когда происходит щелчок, я делаю снимок экрана и помещаю информацию в объект Image, на котором я рисую маленькую красную точку там, где произошел последний щелчок, а затем я устанавливаю значок JLabel на значок, сделанный из отредактированного изображения. Затем я вызываю описанный выше метод для прокрутки панели прокрутки до красной точки на удерживаемом JLabel.   -  person    schedule 05.11.2013
comment
Я полагаю, вы масштабируете изображение?   -  person MadProgrammer    schedule 05.11.2013
comment
Нет, я сохраняю изображение того же размера, что и экран, и создаю из него значок, и хочу просто прокручивать панель прокрутки достаточно вверх/вниз и влево/вправо, чтобы увидеть красную точку на значке.   -  person    schedule 05.11.2013
comment
// Я использовал scrollRectToVisible, но не знаю, как создать аргумент Rectangle; Я пробовал new Rectangle(lastClick), но это никогда не заставило бы полосу прокрутки прокручиваться.   -  person    schedule 05.11.2013


Ответы (1)


Это довольно простой пример. Он использует файл изображения и помещает его в область прокрутки (окольным путем).

Оттуда он просто использует Swing Timer для случайной генерации точек (в пределах изображения).

Каждый раз, когда создается новая точка, я просто использую scrollToRectVisible, передавая ей местоположение и размер точки, которую я хочу отобразить. Это гарантирует, что новая точка (и точка) будет видна в области прокрутки.

введите здесь описание изображения

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 миллисекунд ;)

person MadProgrammer    schedule 04.11.2013