java - 如何在 Java 中创建大尺寸自定义游标?

标签 java swing size custom-cursor

我正在为屡获殊荣的密码保护系统开发一个 Java Swing 应用程序,我需要一个大的自定义光标 [80 x 80],您可能会问为什么这么大,您可以查看一个在线网络演示了解为什么它需要这么大:http://gatecybertech.net

在上面链接的登录页面上使用了那个大光标。当然,您需要先创建一个测试密码,然后才能尝试登录过程。

但无论如何,在我的 Swing 应用程序中,我将最大可能的自定义光标限制为 32 x 32,我的代码如下所示:

Image cursorImage = toolkit.getImage("Cursor_Crosshair.PNG");
Tabular_Panel.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(cursorImage,new Point(0,0),"custom cursor"));

Cursor_Crosshair.PNG 的图像大小为:80 x 80

但屏幕上显示的是它的缩小版:32 x 32

所以我的问题是:如何绕过客户光标图像的大小限制,并使光标以 80 x 80 的大小显示?

我知道操作系统可能是限制的原因,有没有办法克服它?

最佳答案

这是我对玻璃板绘画方法的看法。这被设置为表现得非常像设置自定义光标。显示自定义光标时隐藏默认“箭头”光标,当组件设置了其他光标(例如文本框)时自定义光标隐藏。

不幸的是,它最终似乎需要相当多的 Swing 黑魔法,所以我不太喜欢它,但它似乎确实可以正常工作。我以前也做过这样的游标,不过是为了简单一些,所以没有碰到这些问题。

我遇到的一些问题是:

  • 玻璃 Pane 拦截光标更改(例如在 SO here 中描述)。我能够找到的唯一解决方案是覆盖 Component.contains(int,int)返回 false(描述为 here ,显示为 here ),但为什么它有效并且似乎没有破坏其他任何东西是个谜。

  • 鼠标退出事件有时会返回组件边界内的位置,因此我认为除了使用计时器外没有可靠的方法知道鼠标何时离开窗口。

    <
package mcve;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.imageio.*;
import java.net.*;
import java.io.*;

public class LargeCursor {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();

            JPanel glass = new CustomGlassPane();
            glass.add(new CursorPanel(), BorderLayout.CENTER);
            frame.setGlassPane(glass);
            // This next call is necessary because JFrame.setGlassPane delegates to the root pane:
            // - https://docs.oracle.com/javase/9/docs/api/javax/swing/RootPaneContainer.html#setGlassPane-java.awt.Component-
            // - http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/javax/swing/JFrame.java#l738
            // And JRootPane.setGlassPane may call setVisible(false):
            // - https://docs.oracle.com/javase/9/docs/api/javax/swing/JRootPane.html#setGlassPane-java.awt.Component-
            // - http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/javax/swing/JRootPane.java#l663
            glass.setVisible(true);

            JPanel content = createTestPanel();
            content.setCursor(BlankCursor.INSTANCE);

            frame.setContentPane(content);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }

    static class CustomGlassPane extends JPanel {
        CustomGlassPane() {
            super(new BorderLayout());
            super.setOpaque(false);
        }
        @Override
        public boolean contains(int x, int y) {
            return false;
        }
    }

    static class CursorPanel extends JPanel {
        final BufferedImage cursorImage;
        Point mouseLocation;

        CursorPanel() {
            try {
                cursorImage = createTransparentImage(
                    ImageIO.read(new URL("/image/9h2oI.png")));
            } catch (IOException x) {
                throw new UncheckedIOException(x);
            }

            setOpaque(false);

            long mask = AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK;

            Toolkit.getDefaultToolkit().addAWTEventListener((AWTEvent e) -> {
                switch (e.getID()) {
                    case MouseEvent.MOUSE_ENTERED:
                    case MouseEvent.MOUSE_EXITED:
                    case MouseEvent.MOUSE_MOVED:
                    case MouseEvent.MOUSE_DRAGGED:
                        capturePoint((MouseEvent) e);
                        break;
                }
            }, mask);

            // This turned out to be necessary, because
            // the 'mouse exit' events don't always have
            // a Point location which is outside the pane.
            Timer timer = new Timer(100, (ActionEvent e) -> {
                if (mouseLocation != null) {
                    Point p = MouseInfo.getPointerInfo().getLocation();
                    SwingUtilities.convertPointFromScreen(p, this);
                    if (!contains(p)) {
                        setMouseLocation(null);
                    }
                }
            });
            timer.setRepeats(true);
            timer.start();
        }

        void capturePoint(MouseEvent e) {
            Component comp = e.getComponent();
            Point onThis = SwingUtilities.convertPoint(comp, e.getPoint(), this);
            boolean drawCursor = contains(onThis);

            if (drawCursor) {
                Window window = SwingUtilities.windowForComponent(this);
                if (window instanceof JFrame) {
                    Container content = ((JFrame) window).getContentPane();
                    Point onContent = SwingUtilities.convertPoint(comp, e.getPoint(), content);
                    Component deepest = SwingUtilities.getDeepestComponentAt(content, onContent.x, onContent.y);
                    if (deepest != null) {
                        if (deepest.getCursor() != BlankCursor.INSTANCE) {
                            drawCursor = false;
                        }
                    }
                }
            }

            setMouseLocation(drawCursor ? onThis : null);
        }

        void setMouseLocation(Point mouseLocation) {
            this.mouseLocation = mouseLocation;
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            if (mouseLocation != null) {
                int x = mouseLocation.x - (cursorImage.getWidth() / 2);
                int y = mouseLocation.y - (cursorImage.getHeight() / 2);

                g.drawImage(cursorImage, x, y, this);
            }
        }
    }

    static final class BlankCursor {
        static final Cursor INSTANCE =
            Toolkit.getDefaultToolkit().createCustomCursor(
                new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB),
                new Point(),
                "BLANK");
    }

    static JPanel createTestPanel() {
        JPanel panel = new JPanel(new GridLayout(3, 3));
        panel.setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));

        for (int i = 0; i < 9; ++i) {
            if ((i % 2) == 0) {
                JTextField field = new JTextField("Text Field");
                field.setHorizontalAlignment(JTextField.CENTER);
                panel.add(field);
            } else {
                panel.add(new JButton("Button"));
            }
        }

        return panel;
    }

    static BufferedImage createTransparentImage(BufferedImage img) {
        BufferedImage copy =
            GraphicsEnvironment.getLocalGraphicsEnvironment()
                               .getDefaultScreenDevice()
                               .getDefaultConfiguration()
                               .createCompatibleImage(img.getWidth(),
                                                      img.getHeight(),
                                                      Transparency.TRANSLUCENT);
        for (int x = 0; x < img.getWidth(); ++x) {
            for (int y = 0; y < img.getHeight(); ++y) {
                int rgb = img.getRGB(x, y) & 0x00FFFFFF;
                int bright = (((rgb >> 16) & 0xFF) + ((rgb >> 8) & 0xFF) + (rgb & 0xFF)) / 3;
                int alpha = 255 - bright;
                copy.setRGB(x, y, (alpha << 24) | rgb);
            }
        }

        return copy;
    }
}

关于java - 如何在 Java 中创建大尺寸自定义游标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51333423/

相关文章:

c++ - QMdiSubWindow 的初始大小

c++ - 使用.find函数在C++中查找字符和另一个字符之间的大小

java - 将Java字符串插入另一个字符串而不连接?

java - volatile 是否会阻塞或涉及上下文切换?

java - Servlet 路径错误

java - 获取 Swing 表中的数据库信息作为输出

java - JTable insertRow ArrayIndexOutOfBoundsException

java - 为什么我们通过调用 Acceptor.accept() 而不是 Visitor.visit() 来启动 Visitor?

java - jtree 程序化多选

java ldapsearchException 超出 setMaxResults 大小限制