屏幕上鼠标位置周围区域的缩放框[关闭]

Posted

技术标签:

【中文标题】屏幕上鼠标位置周围区域的缩放框[关闭]【英文标题】:Zoom box for area around mouse location on screen [closed] 【发布时间】:2013-08-12 02:03:33 【问题描述】:

有没有办法在 Java 中创建一个动态缩放框,其中包括 e. G。光标周围的 20x20pix 区域(但即使光标移动到应用程序的框架之外)并且会在一个小的 JPanel 中显示?

我是在颜色选择器程序的上下文中询问的。最后一个需要实现的功能就是 Zoom Box。

【问题讨论】:

【参考方案1】:

我确信有许多不同的方法可以实现这一目标。

这基本上使用了一个单独的组件,它充当“缩放框”。你向它提供一个你想要“放大”的组件。它添加了一个鼠标侦听器,因此它可以监视鼠标运动事件以及进入和退出事件。

这些用于确定“弹出”窗口应显示的时间、弹出窗口应显示的位置以及应“绘制”的区域。

这使用“要缩放的组件”paint 方法将其区域绘制到后备缓冲区,然后将其缩放并绘制到“缩放框”...简单

我没有玩过缩放系数,所以可能还有一些怪癖,但你应该明白基本的想法......

虽然我展示了一张图片作为背景,但这应该适用于任何组件

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ZoomBoxWindow 

  public static void main(String[] args) 
    new ZoomBoxWindow();
  

  public ZoomBoxWindow() 
    EventQueue.invokeLater(new Runnable() 
      @Override
      public void run() 
        try 
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
          ex.printStackTrace();
        

        TestPane pane = new TestPane();
        ZoomPane zoomPane = new ZoomPane(pane);

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
      
    );
  

  public static class ZoomPane extends JPanel 

    protected static final int ZOOM_AREA = 40;

    private JComponent parent;
    private JWindow popup;

    private BufferedImage buffer;

    private float zoomLevel = 2f;

    public ZoomPane(JComponent parent) 
      this.parent = parent;
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      MouseAdapter ma = new MouseAdapter() 
        @Override
        public void mouseMoved(MouseEvent e) 
          Point p = e.getPoint();
          Point pos = e.getLocationOnScreen();
          updateBuffer(p);
          popup.setLocation(pos.x + 16, pos.y + 16);
          repaint();
        

        @Override
        public void mouseEntered(MouseEvent e) 
          popup.setVisible(true);
        

        @Override
        public void mouseExited(MouseEvent e) 
          popup.setVisible(false);
        

      ;

      parent.addMouseListener(ma);
      parent.addMouseMotionListener(ma);
    

    protected void updateBuffer(Point p) 
      int width = Math.round(ZOOM_AREA);
      int height = Math.round(ZOOM_AREA);
      buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2d = buffer.createGraphics();
      AffineTransform at = new AffineTransform();

      int xPos = (ZOOM_AREA / 2) - p.x;
      int yPos = (ZOOM_AREA / 2) - p.y;

      if (xPos > 0) 
        xPos = 0;
      
      if (yPos > 0) 
        yPos = 0;
      

      if ((xPos * -1) + ZOOM_AREA > parent.getWidth()) 
        xPos = (parent.getWidth() - ZOOM_AREA) * -1;
      
      if ((yPos * -1) + ZOOM_AREA > parent.getHeight()) 
        yPos = (parent.getHeight()- ZOOM_AREA) * -1;
      

      at.translate(xPos, yPos);
      g2d.setTransform(at);
      parent.paint(g2d);
      g2d.dispose();
    

    @Override
    public Dimension getPreferredSize() 
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    

    @Override
    protected void paintComponent(Graphics g) 
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) 
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    

  

  public class TestPane extends JPanel 

    private BufferedImage img;

    public TestPane() 
      try 
        img = ImageIO.read(new File("/path/to/your/image"));
       catch (IOException ex) 
        ex.printStackTrace();
      
    

    @Override
    public Dimension getPreferredSize() 
      return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
    

    @Override
    protected void paintComponent(Graphics g) 
      super.paintComponent(g);
      if (img != null) 
        Graphics2D g2d = (Graphics2D) g.create();
        int x = (getWidth() - img.getWidth()) / 2;
        int y = (getHeight() - img.getHeight()) / 2;
        g2d.drawImage(img, x, y, this);
        g2d.dispose();
      
    

  


更新为“屏幕”版本

此版本将允许您在屏幕上的任何位置显示“缩放窗口”。

这有一个小问题,您需要在捕获屏幕之前隐藏缩放窗口,然后重新显示它。

我可能很想改变这个过程,以便当updateBuffer 方法检测到鼠标位置没有改变时,它会更新缓冲区并显示缩放窗口。当鼠标位置改变时,它会再次隐藏窗口......但那是我;)

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import static zoomboxwindow.ZoomBoxWindow.ZoomPane.ZOOM_AREA;

public class GlobalZoomBox 

  public static void main(String[] args) 
    new GlobalZoomBox();
  

  public GlobalZoomBox() 
    EventQueue.invokeLater(new Runnable() 
      @Override
      public void run() 
        try 
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
          ex.printStackTrace();
        
        Zoomer zoomer = new Zoomer();
        zoomer.setZoomWinodwVisible(true);
      
    );
  

  public class Zoomer extends JPanel 

    protected static final int ZOOM_AREA = 40;

    private JWindow popup;

    private BufferedImage buffer;
    private Robot bot;

    private float zoomLevel = 2f;
    private Point lastPoint;
    private final Timer timer;

    public Zoomer() 
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      try 
        bot = new Robot();
       catch (AWTException ex) 
        ex.printStackTrace();
      
      timer = new Timer(125, new ActionListener() 
        @Override
        public void actionPerformed(ActionEvent e) 
          updateBuffer();
        
      );
      timer.setCoalesce(true);
      timer.setInitialDelay(0);
    

    public void setZoomWinodwVisible(boolean value) 

      if (value && !popup.isVisible()) 

        timer.start();
        popup.setVisible(true);

       else 

        timer.stop();
        popup.setVisible(false);

      

    

    @Override
    public Dimension getPreferredSize() 
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    

    protected void updateBuffer() 
      if (bot != null) 
        PointerInfo info = MouseInfo.getPointerInfo();
        Point p = info.getLocation();
        if (lastPoint == null || !lastPoint.equals(p)) 
          int x = p.x - (ZOOM_AREA / 2);
          int y = p.y - (ZOOM_AREA / 2);
          popup.setLocation(p.x + 16, p.y + 16);
          popup.setVisible(false);
          buffer = bot.createScreenCapture(new Rectangle(x, y, ZOOM_AREA, ZOOM_AREA));
          popup.setVisible(true);
          lastPoint = p;
          repaint();
        
      
    

    @Override
    protected void paintComponent(Graphics g) 
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) 
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    

  


更新了“工具提示”样式的弹出窗口

第二个示例的主要问题是您需要隐藏弹出窗口才能抓取屏幕截图。这样做是为了防止弹出窗口也开始被捕获。这使得每次移动鼠标时弹出窗口都会“闪烁”。

您“可以”解决此问题,确保弹出窗口位于捕获范围之外,但随着您增加捕获区域,弹出窗口将远离光标。

当然,这对于固定位置显示来说是一个很好的解决方案(即,您将面板固定在 JFrame 而不是浮动框上)

这是一个额外的更新,它使用第二个计时器在用户停止移动鼠标后显示缩放框。

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.Action;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import static zoomboxwindow.ZoomBoxWindow.ZoomPane.ZOOM_AREA;

public class GlobalZoomBox 

  public static void main(String[] args) 
    new GlobalZoomBox();
  

  public GlobalZoomBox() 
    EventQueue.invokeLater(new Runnable() 
      @Override
      public void run() 
        try 
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
          ex.printStackTrace();
        
        Zoomer zoomer = new Zoomer();
        zoomer.setZoomWinodwVisible(true);
      
    );
  

  public class Zoomer extends JPanel 

    protected static final int ZOOM_AREA = 80;

    private JWindow popup;

    private BufferedImage buffer;
    private Robot bot;

    private float zoomLevel = 2f;
    private Point lastPoint;
    private final Timer timer;
    private final Timer popupTimer;

    public Zoomer() 
      popup = new JWindow();
      popup.setLayout(new BorderLayout());
      popup.add(this);
      popup.pack();
      try 
        bot = new Robot();
       catch (AWTException ex) 
        ex.printStackTrace();
      
      timer = new Timer(125, new ActionListener() 
        @Override
        public void actionPerformed(ActionEvent e) 
          updateBuffer();
        
      );
      timer.setCoalesce(true);
      timer.setInitialDelay(0);

      popupTimer = new Timer(250, new ActionListener() 
        @Override
        public void actionPerformed(ActionEvent e) 
          if (lastPoint != null) 
            System.out.println("lastPoint = " + lastPoint);
            popup.setVisible(false);
            Point p = lastPoint;
            int x = p.x - (ZOOM_AREA / 2);
            int y = p.y - (ZOOM_AREA / 2);
            popup.setLocation(p.x + 16, p.y + 16);
            buffer = bot.createScreenCapture(new Rectangle(x, y, ZOOM_AREA, ZOOM_AREA));
            repaint();
            popup.setVisible(true);
          
        
      );
      popupTimer.setRepeats(false);
    

    public void setZoomWinodwVisible(boolean value) 

      if (value && !popup.isVisible()) 

        timer.start();
        popup.setVisible(true);

       else 

        timer.stop();
        popup.setVisible(false);

      

    

    @Override
    public Dimension getPreferredSize() 
      return new Dimension(Math.round(ZOOM_AREA * zoomLevel), Math.round(ZOOM_AREA * zoomLevel));
    

    protected void updateBuffer() 
      if (bot != null) 
        PointerInfo info = MouseInfo.getPointerInfo();
        Point p = info.getLocation();
        if (lastPoint == null || !lastPoint.equals(p)) 
          lastPoint = p;
          popupTimer.stop();
          popup.setVisible(false);
         else 
          if (!popup.isVisible()) 
            popupTimer.start();
          
        
      
    

    @Override
    protected void paintComponent(Graphics g) 
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D) g.create();
      if (buffer != null) 
        AffineTransform at = g2d.getTransform();
        g2d.setTransform(AffineTransform.getScaleInstance(zoomLevel, zoomLevel));
        g2d.drawImage(buffer, 0, 0, this);
        g2d.setTransform(at);
      
      g2d.setColor(Color.RED);
      g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
      g2d.dispose();
    

  


【讨论】:

非常酷的想法,光标后面有一个小框,但是......可惜它只出现在父组件边框中。是否可以从父组件扩展或使独立的框/工具提示存在? 正如我所说,应该可以将任何组件传递给它,例如,你将它传递给框架的内容窗格,它应该也能够看到所有子组件。或者您是否正在寻找可用于屏幕任何部分的解决方案? 我正在寻找可以在整个屏幕上使用的解决方案。 我会看到我的鞭子;) 谢谢。我会试着弄清楚:)【参考方案2】:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;

class ZoomOnMouse 

    public static void main(String[] args) throws AWTException 
        final Robot robot = new Robot();
        Runnable r = new Runnable() 

            @Override
            public void run() 
                final int size = 256;
                final BufferedImage bi = new BufferedImage(
                        size, size, BufferedImage.TYPE_INT_RGB);
                final JLabel gui = new JLabel(new ImageIcon(bi));
                ActionListener zoomListener = new ActionListener() 

                    @Override
                    public void actionPerformed(ActionEvent e) 
                        PointerInfo pi = MouseInfo.getPointerInfo();
                        Point p = pi.getLocation();
                        BufferedImage temp = robot.createScreenCapture(
                                new Rectangle(p.x-(size/4), p.y-(size/4), 
                                (size/2), (size/2)));
                        Graphics g = bi.getGraphics();
                        g.drawImage(temp, 0, 0, size, size, null);
                        g.dispose();
                        gui.repaint();
                    
                ;
                Timer t = new Timer(40, zoomListener);
                t.start();

                JOptionPane.showMessageDialog(null, gui);

                t.stop();
            
        ;
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    

【讨论】:

太棒了!非常感谢楼主!

以上是关于屏幕上鼠标位置周围区域的缩放框[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Three.js 3D缩放到鼠标位置

鼠标放到划过图片出现悬浮透明边框,别的图片位置不变

如何通过鼠标移动事件获取相机位置并在three.js中转换为屏幕坐标? [关闭]

使用 pynput 获取鼠标位置

鼠标指针的位置

实现搜索框跟随鼠标悬停菜单功能