简体   繁体   English

在 java swing window 中更新自定义 cursor

[英]Updating custom cursor in java swing window

I want to set custom cursor in my java swing app, and then edit it.我想在我的 java swing 应用程序中设置自定义 cursor,然后编辑它。

I set a custom cusrsor after showing window (in "Window" class).在显示 window(在“Window”类中)后,我设置了一个自定义光标。 Later in code (in the other class), I want to chainge it again, so i call this updateCursor() funcion (in "Window" class again), and it and it won't work.稍后在代码中(在另一个类中),我想再次链接它,所以我调用了这个updateCursor()再次在“Window”class 中),并且它不起作用。 There is no errors or warnings, but the cursor isn't changing - just stays the same.没有错误或警告,但 cursor 没有改变——只是保持不变。 I tried, and I can't find answer anywhere.我试过了,但我无法在任何地方找到答案。 I appreciate any help.我感谢任何帮助。

This is full code - Window.java :这是完整代码 - Window.java

import MainMenu;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;

public class Window {
    public static final int WIDTH = 817, HEIGHT = 640;

    JFrame frame = new JFrame("");

    public void open() {
        frame.pack();
        frame.setVisible(true);
        frame.setResizable(false);
        frame.setSize(WIDTH - 33, HEIGHT - 25);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setFocusable(true);
        frame.requestFocus();
        frame.setFocusTraversalKeysEnabled(true);

        frame.addKeyListener(new InputManager());
        frame.addMouseListener(new InputManager());
        frame.add(new MainMenu());
        frame.add(new Game());

        loadCursors();

        updateCursor(0);
    }

    public static final int NORMAL = 0, ACTIVE = 1, INACTIVE = 2;

    Cursor cursor_normal, cursor_active, cursor_inactive;

    public void loadCursors() {
        try {
            cursor_normal = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_normal)), new Point(0, 0), "custom cursor (normal)");
            cursor_active = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_active)), new Point(0, 0), "custom cursor (active)");
            cursor_inactive = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_inactive)), new Point(0, 0), "custom cursor (inactive)");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateCursor(int cursorType) {
        switch (cursorType) {
            case NORMAL -> frame.setCursor(cursor_normal);
            case ACTIVE -> frame.setCursor(cursor_active);
            case INACTIVE -> frame.setCursor(cursor_inactive);
        }
    }
}

MainMenu.java :主菜单.java

import Window;

import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class MainMenu extends JPanel implements KeyListener {

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        // testing
        new Window().updateCursor(Window.ACTIVE);
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

You are creating an new instance of your Window class in your keyPressed(..) method, therefore your cursor update does not work:您正在 keyPressed(..) 方法中创建 Window class 的新实例,因此您的 cursor 更新不起作用:

new Window().updateCursor(Window.ACTIVE);新窗口().updateCursor(Window.ACTIVE);

You need to pass your existing Window instance to your MainMenu class and call this instance.您需要将现有的 Window 实例传递给 MainMenu class 并调用此实例。

Here's a working example:这是一个工作示例:

Main App :主要应用程序

public class MyApp extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MyApp app = new MyApp();
                app.setVisible(true);
            }
        });
    }

    private MyApp() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600, 600);

        // Create cursors
        Cursor c1 = Util.getCursor("c1.png");
        Cursor c2 = Util.getCursor("c2.png");

        setCursor(c1);

        JButton button1 = new JButton("Change to Cursor1");
        button1.setActionCommand("c1");
        button1.addActionListener(new MyActionListener(this));

        JButton button2 = new JButton("Change to Cursor2");
        button2.setActionCommand("c2");
        button2.addActionListener(new MyActionListener(this));

        add(button1, BorderLayout.NORTH);
        add(button2, BorderLayout.SOUTH);
    }

}

ActionListener (handles the button clicks): ActionListener (处理按钮点击):

public class MyActionListener implements ActionListener {

    private JFrame jFrame;

    public MyActionListener(JFrame jFrame) {
        this.jFrame = jFrame;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        switch (e.getActionCommand()){
            case "c1":
                jFrame.setCursor(Util.getCursor("c1.png"));
                System.out.println("switch to c1");
                break;
            case "c2":
                jFrame.setCursor(Util.getCursor("c2.png"));
                System.out.println("switch to c2");
                break;
        }
    }

}

Util (read cursor image): Util (读取cursor图像):

public class Util {
    public static Cursor getCursor(String fileName) {
        BufferedImage img = null;
        try {
            img = ImageIO.read(Util.class.getResourceAsStream(fileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return Toolkit.getDefaultToolkit().createCustomCursor(img, new Point(0, 0), fileName);
    }

}

You can't do...你不能做...

new Window().updateCursor(Window.ACTIVE);

and magically expect the other instance of Window to be updated, in fact, you don't need to do this at all.并神奇地期望更新Window的另一个实例,实际上,您根本不需要这样做。

This is going to create another instance/copy of Window , which is not present on the screen and it will have no effect on the instance which is been displayed.这将创建Window的另一个实例/副本,它不在屏幕上,并且对显示的实例没有影响。

You could call setCursor directly on the instance MainMenu .您可以直接在实例MainMenu上调用setCursor

Now, if you want to "centralise" the functionality, I would start by creating a "manager" class, for example...现在,如果你想“集中”功能,我会先创建一个“经理”class,例如......

public class CursorManager {

    public enum CusorType {
        NORMAL, ACTIVE, INACTIVE;
    }

    private Cursor cursorNormal, cursorActive, cursorInactive;

    public CursorManager() throws IOException {
        cursorNormal = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_normal)), new Point(0, 0), "custom cursor (normal)");
        cursorActive = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_active)), new Point(0, 0), "custom cursor (active)");
        cursorInactive = Toolkit.getDefaultToolkit().createCustomCursor(ImageIO.read(new File(new SpritesManager().cursor_inactive)), new Point(0, 0), "custom cursor (inactive)");
    }

    public void setCursor(CusorType cursorType, Component comp) {
        switch (cursorType) {
            case NORMAL ->
                comp.setCursor(cursorNormal);
            case ACTIVE ->
                comp.setCursor(cursorActive);
            case INACTIVE ->
                comp.setCursor(cursorInactive);
        }
    }
}

I would then create this instance of the manager during the initialisation phase of your code然后我会在你的代码的初始化阶段创建这个管理器实例

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
                CursorManager cursorManager = new CursorManager();
                //.. Every thing else...
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    });
}

And then pass this instance to every class that might need it...然后将此实例传递给可能需要它的每个 class ...

// You'll need to update Window to accept this parameter
new Window(cursorManager).open();

And...和...

public class MainMenu extends JPanel implements KeyListener {

    private CursorManager cursorManager;
    
    private MainMenu(CursorManager cursorManager) {
        this.cursorManager = cursorManager;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // testing
        cursorManager.setCursor(CursorManager.CusorType.ACTIVE, this);
    }

    //...
}

This is commonly known as "dependency injection" and is VERY powerful这通常被称为“依赖注入”并且非常强大

Feedback反馈

Just as a side note, if I was doing something like this, it would be very different, but I tried to keep it simple.附带说明一下,如果我正在做这样的事情,那将是非常不同的,但我尽量保持简单。

  • We're generally discouraged from extending from top level containers like JFrame , as stated, JFrame is not a simple component and you're not actually adding any new functionality to the class and in the process, locking yourself into a single use, there by reducing re-usability.我们通常不鼓励从JFrame等顶级容器扩展,如前所述, JFrame不是一个简单的组件,您实际上并没有向 class 添加任何新功能,并且在这个过程中,将自己锁定在一次使用中,因此减少可重用性。 Better to start with a JPanel as your base component and simply create an instance of JFrame or `JDialog or what ever top level container you want to use, when you need it最好从JPanel作为基础组件开始,然后在需要时简单地创建一个JFrame或 `JDialog 或您想要使用的任何顶级容器的实例
  • KeyListener is a poor choice for monitoring keyboard input (seriously, just do a search for "my key listener won't work". Instead, take a look at How to Use Key Bindings KeyListener是监视键盘输入的一个糟糕选择(说真的,只需搜索“我的密钥侦听器将无法工作”。相反,请查看如何使用密钥绑定

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM