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) {
}
}
CodePudding user response:
Without more context, it's difficult to spot the error. Potential reasons:
- The frame instance your window class and your other class are not the same
- You cursor is not set on frame but another component
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);
}
}
CodePudding user response:
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,JFrameis 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 aJPanelas your base component and simply create an instance ofJFrameor `JDialog or what ever top level container you want to use, when you need it KeyListeneris 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
