I am trying to create a full screen with a paint method. I want "This is the day" in pink with a blue background to show up. The problem is, is that when I run my program, It shows up with what I painted on my screen with my toolbar and applications instead of showing up a blue background with "This is the day" in the middle in pink. Some code is below.
public static void main(String args[])
{
DisplayMode dm1 =
new DisplayMode(800, 600, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
RunScreen runProgram = new RunScreen();
runProgram.run(dm1);
}
public void run(DisplayMode dm)
{
getContentPane().setBackground(Color.BLUE);
setForeground(Color.PINK);
setFont(new Font("Arial", Font.PLAIN, 24));
FullScreen s = new FullScreen();
try
{
s.setFullScreen(dm, this);
try
{
Thread.sleep(5000);
}
catch (Exception e)
{
}
}
finally
{
s.restoreScreen();
}
}
#Override
public void paint(Graphics g)
{
g.drawString("This is the day", 200, 200);
}
Thread.sleep(5000);
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling Thread.sleep(n) implement a Swing Timer for repeating tasks or a SwingWorker for long running tasks. See Concurrency in Swing for more details.
This seems to work just fine...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFullScreen {
public static void main(String[] args) {
new TestFullScreen();
}
public TestFullScreen() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setUndecorated(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
device.setFullScreenWindow(frame);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String text = "Hello";
FontMetrics fm = g.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g.drawString(text, x, y);
}
}
}
Beware, the "window" that you request to be made into a full screen window may not be the actually window used by the system.
You should avoid overriding paint and use paintComponent instead. You should also avoid overriding paint of top level containers (like JFrame).
Check out Performing Custom Painting and Painting in AWT and Swing for more details...
To hide toolbar and etc. Use setUndecorated
runProgram.setUndecorated(true);
runProgram.run(dm1);
To show up a blue background with "This is the day" in the middle in pink, set the color in paint (or paintComponent)
g.setColor(Color.BLUE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.PINK);
g.drawString("This is the day", 200, 200);
Second, you shouldn't involve with GUI out of the EDT (Event Dispatch Thread). You should do those thing like this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setFont(new Font("Arial", Font.PLAIN, 24));
}
});
Third, you shouldn't overrides paint of top level containers. (I don't mean the #Override notation). You should do as Andrew Thompson mentioned:
Repaint method is not entered when the frame is full screen. So this way is useful for repainting for swing JFrame.
public void actionPerformed(ActionEvent arg0) {
if (getExtendedState() == 6) {//6 full-screen, 0 normal, -1 minimized
repaint();
} else {
repaint();
}
}
Related
Take the example from Oracle, there are some examples in your documentation.
My idea is the following:
I have achieved that my application has a transparent background, but the minimize and close application buttons do not appear
This is my code:
main
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;
public class Textmovie extends JFrame {
/*
public Textmovie() {
//setLayout(new GridBagLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
*/
public static void main(String[] args) {
JFrame jf = new JFrame("");
jf.setUndecorated(true);
jf.setBackground(new Color(0,0,0,10));
//jf.setOpacity(0.55f);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new texscroll());
jf.setSize(720,480);
jf.setVisible(true);
}
}
Part 2
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
/**
*
* #author inide
*/
public class texscroll extends JPanel {
int x =510 , y = 25;
public texscroll() {
setOpaque(false);
}
#Override
public void paint(Graphics g){
super.paint(g);
Graphics2D g2 = (Graphics2D)g;
Font font = new Font("Arial",Font.BOLD + Font.PLAIN,15);
g2.setFont(font);
g2.setColor(Color.BLACK);
String string = "stackoverflow stackoverflow stackoverflow stackoverflow";
g2.drawString(string ,x,y);
try{Thread.sleep(14);}
catch(Exception ex)
{
};
x-=1;
if(x==-10*string.length()){
x= 510;
}
repaint();
// System.out.println(string.length() );
}
}
And this is shown when running in NetBeans IDE 8.0.2
They can explain to me what I have to do to make the buttons appear (minimize and close application).
If you actually dig into the code based on the exception:
Exception in thread "AWT-EventQueue-0" java.awt.IllegalComponentStateException: The frame is decorated
at java.desktop/java.awt.Frame.setBackground(Frame.java:989)
You'll find that it's impossible to make a frame transparent AND be decorated...
#Override
public void setBackground(Color bgColor) {
synchronized (getTreeLock()) {
if ((bgColor != null) && (bgColor.getAlpha() < 255) && !isUndecorated()) {
throw new IllegalComponentStateException("The frame is decorated");
}
super.setBackground(bgColor);
}
}
The fact that the tutorials show it working is irrelevant and an error on the part of the tutorials.
It "might" have been possible in earlier "unreleased" versions of the API (using AWTUtilities), but it simply no longer possible
Now, we've got that out the way, this, inside paint...
try {
Thread.sleep(14);
} catch (Exception ex) {
};
x -= 1;
if (x == -10 * string.length()) {
x = 510;
}
repaint();
is not how you do animation in Swing
This is just going to cause you no end of issues, as nothing is committed to the native peer until AFTER the paintComponent exist (this is how double buffering works)
See Concurrency in Swing for more details.
A more appropriate solution might look something like...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Textmovie extends JFrame {
public static void main(String[] args) {
new Textmovie();
}
public Textmovie() throws HeadlessException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame jf = new JFrame("");
jf.setUndecorated(true);
jf.setBackground(new Color(0, 0, 0, 10));
//jf.setOpacity(0.55f);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(new texscroll());
jf.pack();
jf.setLocationRelativeTo(null);
jf.setVisible(true);
}
});
}
public static class texscroll extends JPanel {
private int x = 510, y = 25;
private String string = "stackoverflow stackoverflow stackoverflow stackoverflow";
public texscroll() {
Font font = new Font("Arial", Font.BOLD + Font.PLAIN, 15);
setFont(font);
setForeground(Color.BLACK);
setOpaque(false);
Timer timer = new Timer(14, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
x -= 1;
if (x == -10 * string.length()) {
x = 510;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(720, 480);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2 = (Graphics2D) g;
g2.drawString(string, x, y);
}
}
}
See How to Use Swing Timers for more details
jf.setUndecorated(true);
makes the title bar invisible and that includes the minimize and close buttons so you should remove that line (because it's false by default)
It’s because you’re calling jf.setUndecorated(true). This method removes the the title bar, which contains the minimize and maximize buttons.
Unfortunately, the window have to be undecorated to have a system title bar, but the look and feel can provide a title bar. To enable it, you have to call this before your frame is made visible:
JFrame.setDefaultLookAndFeelDecorated(true);
I want to create a line and after 2 sec break another line and so on.
If i draw a line and pause program execution with Thread.sleep() and again draw a line within paintComponent() method then what happens is first the program stops for 2 second and then both lines are drawn simultaneously.
how to overcome this problem?
This is realitvely commong requirement and issue. You should start by having a look at Concurrency in Swing and How to use Swing Timers for some basic information on how to solve the your basic problem.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more information about how painting works in Swing
The core design choices you want to focus on are:
Don't block the Event Dispatching Thread, this will prevent it from painting anything or responding to new events
Don't update the UI from outside the context of the EDT
Painting should paint state. It should not be focused on making logical decisions where possible, instead, it should reliant on one or more models to provide it the information it needs to paint itself
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Shape> shapes;
private int yPos = 0;
public TestPane() {
shapes = new ArrayList<>(25);
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += 10;
Line2D line = new Line2D.Double(0, yPos, getWidth(), yPos);
shapes.add(line);
repaint();
}
});
timer.setInitialDelay(2000);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape line : shapes) {
g2d.draw(line);
}
g2d.dispose();
}
}
}
I have a setup where there are several JInternalFrames within a main JFrame. What I want to do is paint some things on the main JFrame but under the JInternalFrame. If I set up the code like
public void paint(Graphics graphics){
try{
Graphics2D g = (Graphics2D) s.getDrawGraphics();
super.paint(g);
super.paintComponents(g);
//Insert Paint Code Here
s.show();
super.repaint();
}catch(Exception ex){}
}
the painted content shows up above the JInternalFrames (which are components under a JDesktopFrame). If I try putting the painting code before super.paint(g) or super.paintComponents(g), the painted content does not show up at all. How do I make it so that the painted content shows up, but under the internal frames?
Note: s is a BufferStrategy and the class inherits from an abstract JFrame class.
You don't seem to have an understanding into how painting works in Swing, have a look at Painting in AWT and Swing and Performing Custom Painting for more details.
Basically, if you want to paint something under the JInternalFrame's, the place to start is by overriding the paintComponent method of the JDesktopPane. The paintComponent method is the lowest virtual level in the paint chain, with paintBorder and paintChildren painted ontop of it
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
CustomDesktopPane pane = new CustomDesktopPane();
JInternalFrame inFrm = new JInternalFrame("Ontop", true, true, true, true);
inFrm.setSize(100, 100);
inFrm.setLocation(150, 150);
inFrm.setVisible(true);
pane.add(inFrm);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CustomDesktopPane extends JDesktopPane {
public CustomDesktopPane() {
setFont(UIManager.getFont("Label.font").deriveFont(24f));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
String text = "All your base are belong to us";
Font font = g2d.getFont();
FontMetrics fm = g2d.getFontMetrics();
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y);
g2d.dispose();
}
}
}
I'm trying to make a simple GUI program without using JComponents.
Currently, I have a BufferedImage that I draw to off screen so that it doesn't flicker (or so I thought).
I made a new program here to replicate the issue:
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
}
}
private static class Panel extends JPanel {
private final BufferedImage offScreen = new BufferedImage(1000, 750, BufferedImage.TYPE_INT_ARGB);
private final Graphics graphics = offScreen.getGraphics();
#Override
protected void paintComponent(Graphics graphics) {
graphics.drawImage(offScreen, 0, 0, null);
}
public void setBackgroundColour(Color colour) {
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 750);
}
}
}
In the example above, I made the screen turn black, and then white (offscreen).
What I'd expect is that paintComponent() only displays the white screen.
Instead, a black screen is showed as well, but everything is flickered.
Am I just using Graphics2D incorrectly, or should I just use BufferStrategy to incorporate my double buffering needs?
My best guess is you have a race condition, where your while-loop is trying to update the BufferedImage, but Swing is also trying to paint it, meaning they are getting dirty updates between them. Also, you might be thrashing the Event Dispatching Thread, which could have it's own, long term issues.
After some playing around, I was able to get something like this to work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
});
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private static class Panel extends JPanel {
private BufferedImage offScreen = new BufferedImage(1000, 700, BufferedImage.TYPE_INT_ARGB);
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.drawImage(offScreen, 0, 0, this);
}
public void setBackgroundColour(Color colour) {
Graphics graphics = offScreen.getGraphics();
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 700);
graphics.dispose();
}
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
}
All it does is injects a small delay (25fps) between the updates, allowing Swing time to render the result.
You have to remember at two things with Swing, repaint doesn't happen immediately and may not happen at all, depending on what the RepaintManager decides to do. Second, you don't control the painting process.
Swing uses a passive rendering algorithm, meaning that painting will occur when it's needed, many times without your knowledge or intervention. The best you can do is make suggestions to the framework when you want something updated
See Painting in AWT and Swing and Performing Custom Painting for more details.
I have a piece of code that moves a rectangle whenever i press a button. it it set up so that the x or y coordinate decreases or increases by 1 pixel each time the key is pressed. however, i am noticing that if i press the same key more than once in a row, the 1 pixel compounds itself somehow , stacks may be a better word, and increases exponentially. is there anything in the code that you can see why it would do this?
public class drawingComponent extends JComponent implements KeyListener
{
Rectangle hello = new Rectangle(300, 100, 50, 50);
public void paintComponent (Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.setColor(new Color(255, 25, 0));
g2.setFont(new Font("monospace", Font.BOLD + Font.ITALIC, 30));
g2.drawString("nothing yet", 300, 320);
g2.fill(hello);
setFocusable(true);
requestFocus();
addKeyListener(this);
}
#Override
public void keyPressed (KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_W)
{
hello.y = hello.y - 1;
hello.setLocation(hello.x, hello.y);
repaint();
System.out.println(hello.y);
}
if (e.getKeyCode() == KeyEvent.VK_S)
{
hello.y = hello.y + 1;
hello.setLocation(hello.x, hello.y);
repaint();
}
if (e.getKeyCode() == KeyEvent.VK_A)
{
hello.x = hello.x - 1;
hello.setLocation(hello.x, hello.y);
repaint();
}
if (e.getKeyCode() == KeyEvent.VK_D)
{
hello.x = hello.x + 1;
hello.setLocation(hello.x, hello.y);
repaint();
}
}
#Override
public void keyReleased (KeyEvent e)
{
}
#Override
public void keyTyped (KeyEvent e)
{
// TODO Auto-generated method stub
}
}
You're adding your KeyListener on each repaint of your component:
public void paintComponent(Graphics g){
//..
addKeyListener(this); // adds another KeyListener
}
And every call of repaint(); adds another KeyListener that will do his job, if keyPressed is called. That means, you have one listener first, then two, then four, then sixteen and so on.
Don't do that. Add your listener once in the constructor or so.
The constructor can look like this:
public drawingComponent() {
// init other stuff
addKeyListener(this);
}
You are trying to overcome an inherent issue with an API by using hacks and workarounds which may or may not work on other systems or platforms.
Do not change the state of a component from within a paint method, painting should paint the current state, nothing else. By requesting focus within the paint method, you could be triggering another repaint request to be made, resulting in a vicious cycle which will consume your CPU cycles.
The core problem is (as highlighted by Tom), you're adding ANOTHER KeyListener everytime paint is called. Painting will occur, a lot, and usually without your knowledge or request.
Instead of fighting this problem, you should use the API which was designed to fix it, the key bindings API. This API will allow you to define the level of focus required to trigger the key events, the following example mimics the default behaviour of the KeyListener (as I don't know your other requirements), but has an option to allow the component to become focusable.
Some tips...
Always caller super.paintComponent before doing custom painting, this is easy to forget and can result in some seriously weird graphics glitches.
There's no need for paintComponent to be public, you never want anyone to ever call it.
You might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
As a simple example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingComponent());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawingComponent extends JComponent {
Rectangle hello = new Rectangle(300, 100, 50, 50);
public DrawingComponent() {
setFocusable(true);
InputMap im = getInputMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "Move.up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "Move.down");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "Move.left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "Move.right");
ActionMap am = getActionMap();
am.put("Move.up", new DeltaAction(0, -1));
am.put("Move.down", new DeltaAction(0, 1));
am.put("Move.left", new DeltaAction(-1, 0));
am.put("Move.right", new DeltaAction(1, 0));
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
requestFocusInWindow();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(255, 25, 0));
g2.setFont(new Font("monospace", Font.BOLD + Font.ITALIC, 30));
g2.drawString("nothing yet", 300, 320);
g2.fill(hello);
}
public class DeltaAction extends AbstractAction {
private int xDelta;
private int yDelta;
public DeltaAction(int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
hello.x += xDelta;
hello.y += yDelta;
repaint();
}
}
}
}
"But why should I use the key bindings API" I hear you asking? Apart from providing you more flexibility and configurability (including setting the level of focus required to trigger the events, you could also change the keys which trigger the events more easily and or have a multiple set of keys - think the arrow keys as well), it can be used with other parts of the Swing API, including buttons.
A single Action can be applied to JMenuItems, JButtons and key bindings, no extra coding required...
See How to Use Key Bindings for more details