1

I want to set custom cursor in my java swing app, and then edit it.

I set a custom cusrsor after showing window (in "Window" class). 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. There is no errors or warnings, but the cursor isn't changing - just stays the same. I tried, and I can't find answer anywhere. I appreciate any help.

This is full code - 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:

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) {

    }
}
12
  • @PM77-1 I'm new, and I started learning like 3 months ago, and switch apparently works in that way (above java 14) betterprogramming.pub/… Anyway, in standard way, it won't work to... Commented Jan 20, 2023 at 18:37
  • You are right about the new switch syntax. Commented Jan 20, 2023 at 18:55
  • Please edit your question to explain what "won't work" means in your case. Any errors? Unexpected results? Commented Jan 20, 2023 at 18:57
  • How can you prove that the call to updateCursor() actually happened? Commented Jan 20, 2023 at 19:49
  • @PM77-1 I tested it with System.out.println(); - the method call and the switch works fine. Commented Jan 20, 2023 at 19:58

2 Answers 2

1

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.

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.

You could call setCursor directly on the instance MainMenu.

Now, if you want to "centralise" the functionality, I would start by creating a "manager" class, for example...

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...

// 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. 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
  • 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
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, it works now! But I have only one last question (it may be dumb, but I'm interested) - Why in try (main), did you use Logger.log() instead of just System.out.println(ex)?
@Adamm The logging system is very flexible, apart from been able to give you information about the class/file and line of the log message, you can also log it to a file our other output source
0

You are creating a new instance of your Window class in your keyPressed(..) method, therefore your cursor update does not work:

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

You need to pass your existing Window instance to your MainMenu class and call this instance.

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):

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):

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

}

2 Comments

Your example works, but when I tried to implement it to my code, there is no result (default windows cursor). I even copied it to difrent project, and it still the same.
I updated my answer. The problem is that in MainMenu.java, you create a new Window instance instead of using the existing instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.