如何在Java中创建大型自定义光标? [英] How To create A Large Size Custom Cursor In Java?

查看:70
本文介绍了如何在Java中创建大型自定义光标?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing a Java Swing app for an award winning password protection system, and I need a large custom cursor [ 80 x 80 ], you might ask why so large, there is an online web demo you may look at to learn why it needs to be so large : http://gatecybertech.net

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

That large cursor is used on the login page in the above link. Of course you need to create a test password first before you can try the login process.

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

But anyway, in my Swing app, I hit a limit of 32 x 32 for the largest possible custom cursor, my code looks like the following :

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

The image size of Cursor_Crosshair.PNG is : 80 x 80

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

But what shows up in the screen is a shrinked version of it at : 32 x 32

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

So my question is : how can I bypass the size limit on customer cursor image, and make the cursor to show up at the size of 80 x 80 ?

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

I know the OS might be the reason for the limit, is there a way to overcome it ?

推荐答案

这是我对玻璃窗格绘画方法的看法.设置它的行为非常类似于设置自定义光标.显示自定义光标时,默认的箭头"光标被隐藏,而当组件设置了其他光标(例如文本框)时,自定义光标被隐藏.

Here's my take on the glass pane painting approach. This is set up to behave pretty much like setting a custom cursor. The default "arrow" cursor is hidden while the custom cursor is shown, and the custom cursor is hidden when a component has some other cursor set, such as a text box.

不幸的是,它最终似乎需要大量的Swing黑魔法,所以我不太喜欢它,但是它确实可以正常工作.我以前做过这样的游标,但这只是为了简单起见,所以我没有遇到这些问题.

Unfortunately, it ended up seeming to require quite a bit of Swing black magic, so I don't like it very much, but it does seem to work correctly. I've done a cursor like this before, but it was for something simpler, so I didn't run in to these issues.

我遇到的一些问题是:

  • 玻璃窗格拦截了光标更改(例如,在此处中进行了描述).我唯一能找到的解决方案是覆盖

  • The glass pane intercepts cursor changes (described e.g. on SO here). The only solution I've been able to find is to override Component.contains(int,int) to return false (described here, shown here), but why that works and doesn't seem to break anything else is mysterious.

鼠标退出事件有时会返回该组件范围内的位置,因此,我认为除了使用计时器之外,没有一种可靠的方法来知道鼠标何时离开窗口.

Mouse exit events sometimes return a location inside the bounds of the component, so I don't think there's a reliable way to know when the mouse leaves the window except to use a timer.

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("https://i.stack.imgur.com/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中创建大型自定义光标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆