I'm making a 2d game engine and trying to improve the input system. When I try to use the WASD keys, the KeyListener sometimes won't detect the key being pressed but it still detects it being released. I tried using KeyBindings instead but I get the same problem. Anyone know what's going on?
(maybe it has something to do with my IDE(netbeans), mac, or java?)
edit: here's someone else's code that has the same problem
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
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 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.setContentPane(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Surface surface;
public TestPane() {
setLayout(new BorderLayout());
surface = new Surface();
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
System.out.println("yo");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Pressed.left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Release.left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Pressed.right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Release.right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Pressed.up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Release.up");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Pressed.down");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Release.down");
actionMap.put("Pressed.left", surface.getLeftPressAction());
actionMap.put("Release.left", surface.getLeftReleaseAction());
actionMap.put("Pressed.right", surface.getRightPressAction());
actionMap.put("Release.right", surface.getRightReleaseAction());
actionMap.put("Pressed.up", surface.getUpPressAction());
actionMap.put("Release.up", surface.getUpReleaseAction());
actionMap.put("Pressed.down", surface.getDownPressAction());
actionMap.put("Release.down", surface.getDownReleaseAction());
add(surface);
}
}
public class Surface extends Canvas {
private String displayText = "...";
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
int x = (getWidth() - fm.stringWidth(displayText)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(displayText, x, y);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public Action getLeftPressAction() {
return new TextAction("Left");
}
public Action getLeftReleaseAction() {
return new ClearAction();
}
public Action getRightPressAction() {
return new TextAction("Right");
}
public Action getRightReleaseAction() {
return new ClearAction();
}
public Action getUpPressAction() {
return new TextAction("Up");
}
public Action getUpReleaseAction() {
return new ClearAction();
}
public Action getDownPressAction() {
return new TextAction("Down");
}
public Action getDownReleaseAction() {
return new ClearAction();
}
public class TextAction extends AbstractAction {
private String text;
public TextAction(String text) {
this.text = text;
}
#Override
public void actionPerformed(ActionEvent e) {
displayText = text;
repaint();
}
}
public class ClearAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
displayText = "...";
repaint();
}
}
}
}
I just tried using the jar file in a windows 10 and it worked perfectly... It may have something to do with my operating system or my version of java.
Related
I am trying to get a circle to move through the input of a keyboard. I am not able to move the object at all. Can someone help me figure out what is wrong? Here is my code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
public class AlienInvader extends JPanel implements KeyListener{
Constants constant = new Constants();
public void update() {
constant.x += constant.xvel;
addKeyListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
g.fillOval(constant.x, constant.y, 30, 30);
repaint();
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println(constant.x);
switch(e.getKeyCode()) {
case KeyEvent.VK_LEFT:
constant.xvel = -1;
break;
case KeyEvent.VK_RIGHT:
constant.xvel = 1;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_LEFT:
constant.xvel = -1;
break;
case KeyEvent.VK_RIGHT:
constant.xvel = 1;
break;
}
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
I am not sure what I am doing wrong. I thought it was because I wasn't calling the update method, but when I added a if statement in paintComponent (so it only calls itself once) and tried it, I had no luck.
To start with, don't call repaint within any paintXxx method. Paint methods are typically called in response to a call to repaint, therefore you are creating a nasty, never ending, ever consuming loop of resource hell.
Secondly, KeyListeners only respond to key events when 1- The component the are registered to are focusable 2- When the component they are registered to have focus.
They are a poor choice in this case. Use Key bindings instead
Thirdly, you are not providing a preferredSize hint for layout managers to use. This may or may not be a bad thing in your case, but it's possible that you component will be laid out with a size of 0x0
Example
Something like....
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MoveCircle {
public static void main(String[] args) {
new MoveCircle();
}
public MoveCircle() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int xDelta = 0;
private int keyPressCount = 0;
private Timer repaintTimer;
private int xPos = 0;
private int radius = 10;
public TestPane() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "pressed.left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "pressed.right");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "released.left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "released.right");
am.put("pressed.left", new MoveAction(-2, true));
am.put("pressed.right", new MoveAction(2, true));
am.put("released.left", new MoveAction(0, false));
am.put("released.right", new MoveAction(0, false));
repaintTimer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
if (xPos < 0) {
xPos = 0;
} else if (xPos + radius > getWidth()) {
xPos = getWidth() - radius;
}
repaint();
}
});
repaintTimer.setInitialDelay(0);
repaintTimer.setRepeats(true);
repaintTimer.setCoalesce(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawOval(xPos, 0, radius, radius);
g2d.dispose();
}
public class MoveAction extends AbstractAction {
private int direction;
private boolean keyDown;
public MoveAction(int direction, boolean down) {
this.direction = direction;
keyDown = down;
}
#Override
public void actionPerformed(ActionEvent e) {
xDelta = direction;
if (keyDown) {
if (!repaintTimer.isRunning()) {
repaintTimer.start();
}
} else {
repaintTimer.stop();
}
}
}
}
}
For example...
I was given the task to create a custom swing component. I have my component functioning properly in a test application which includes JSlider that is used to zoom in and out on an Image. However I am required to present my custom component in a Model, UIDelegate, and Component class format and I am totally lost on how to convert my code so that it follows this format. Here is the code for my test application.
package test;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class ZoomDemo extends JComponent implements ChangeListener {
JPanel gui;
/**
* Displays the image.
*/
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public ZoomDemo() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
ZoomDemo app = new ZoomDemo();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
The following code is the class format i need to follow
Component Class
package component;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.plaf.ComponentUI;
public class ProgressBar extends JComponent {
public static ComponentUI createUI(JComponent c) {
return new ZoomUI();
}
public void installUI(JComponent c){
}
public void uninstallUI (JComponent c){
}
}
Model CLass
public class ZoomModel extends JSLider {
}
UIDelegate Class
public class ZoomUI extends ComponentUI implements ChangeListener{
}
Any help on how I can implement my custom component in this format would be greatly appreciated. I am very new to Swing and documentation I have found on custom components has been very confusing and of little help.
test application
package test;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import component.ZoomComponent;
public class ZoomDemo extends JPanel implements PropertyChangeListener, ActionListener {
ZoomComponent zoomer;
JPanel board;
private BufferedImage image;
public ZoomDemo( ) {
super(true);
setLayout(new BorderLayout( ));
board = new JPanel(true);
board.setPreferredSize(new Dimension(300, 300));
board.setBorder(new LineBorder(Color.black, 5));
zoomer = new ZoomComponent();
add(board, BorderLayout.NORTH);
add(zoomer, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
UIManager.getDefaults().put("ZoomComponentUI", "component.BasicZoomUI");
ZoomDemo s= new ZoomDemo();
JFrame frame = new JFrame("Sample Sketch Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(s);
frame.pack( );
frame.setVisible(true);
}
}
Okay, so that was a fun adventure into parts of the API I don't use :), start by having a read through How to Write a Custom Swing Component and it's associated links, this will give you the ground work to understand what is about to happen...
Model
The Interface
Personally, I always start with an interface, life is better with interfaces and it gives you more flexibility. Now, which model should you extend from (based on your requirements)...?
Well, the best choice I could find was the BoundedRangeModel, which is also used by the JSlider...this actually means that I can not only pass this model to the view, but to a JSlider and without any extra work, have the slider change the image!! Win-Win
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.BoundedRangeModel;
public interface ZoomModel extends BoundedRangeModel {
public Image getImage();
public Dimension getScaledSize();
}
The Abstract
Next, I like to make an abstract version, this is where I put "common" functionality, which is likely to be the same for most implementations, in this case, it might not be required, but I'm finckle like this...
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.DefaultBoundedRangeModel;
public abstract class AbstractZoomModel extends DefaultBoundedRangeModel implements ZoomModel {
public AbstractZoomModel() {
super(100, 0, 0, 200);
}
#Override
public Dimension getScaledSize() {
Dimension size = new Dimension(0, 0);
Image image = getImage();
if (image != null) {
double scale = getValue() / 100d;
size.width = (int) Math.round(image.getWidth(null) * scale);
size.height = (int) Math.round(image.getHeight(null) * scale);
}
return size;
}
}
So, you can see here, I've defined some basic properties, a starting zoom level of 100, a max level of 200 and a minimum level of 0, plus I've implemented the getScaledSize, which is used a bit and makes life easier...
The Default...
Now, because we like been nice, we provide a "default" implementation of the model. This is pretty basic in that all it does it takes a reference to an image...
import java.awt.Image;
public class DefaultZoomModel extends AbstractZoomModel {
Image image;
public DefaultZoomModel(Image image) {
this.image = image;
}
#Override
public Image getImage() {
return image;
}
}
You could create implementations that download images from an URL for example...
The View
Okay, this is the actually component itself, which gets added to your UI. It contains the basic functionality need to construct and prepare the UI delegate and manage the model. The key thing of interest here is the use of the property change support to provide notification of the change to the model, this is important as you will see...
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.UIManager;
public class ZoomComponent extends JComponent {
private static final String uiClassID = "ZoomComponentUI";
private ZoomModel model;
public ZoomComponent() {
setBackground(Color.black);
setFocusable(true);
updateUI();
}
public void setModel(ZoomModel newModel) {
if (model != newModel) {
ZoomModel old = model;
this.model = newModel;
firePropertyChange("model", old, newModel);
}
}
public ZoomModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
ZoomModel model = getModel();
Dimension size = new Dimension(100, 100);
if (model != null) {
size = model.getScaledSize();
}
return size;
}
public void setUI(BasicZoomUI ui) {
super.setUI(ui);
}
#Override
public void updateUI() {
if (UIManager.get(getUIClassID()) != null) {
ZoomUI ui = (ZoomUI) UIManager.getUI(this);
setUI(ui);
} else {
setUI(new BasicZoomUI());
}
}
public BasicZoomUI getUI() {
return (BasicZoomUI) ui;
}
#Override
public String getUIClassID() {
return uiClassID;
}
}
The UI Delegate
Now the other fun stuff...If we follow standard convention, you would normally provide an abstract concept of the UI delegate, for example...
import javax.swing.plaf.ComponentUI;
public abstract class ZoomUI extends ComponentUI {
}
From this, other delegates will grow...
Basic UI Delegate
Convention would normally suggest you provide a "basic" implementation, doing a lot of the heavy lifting, but allowing other implementations the opportunity to jump in change things to there likely
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;
public class BasicZoomUI extends ZoomUI {
private ZoomComponent zoomComponent;
private MouseAdapter mouseHandler;
private ChangeListener changeHandler;
private Action zoomIn;
private Action zoomOut;
private PropertyChangeListener propertyChangeHandler;
protected ChangeListener getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
protected void installMouseListener() {
mouseHandler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
zoomComponent.requestFocusInWindow();
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int amount = e.getWheelRotation();
ZoomModel model = zoomComponent.getModel();
if (model != null) {
int value = model.getValue();
model.setValue(value + amount);
}
}
};
zoomComponent.addMouseListener(mouseHandler);
zoomComponent.addMouseWheelListener(mouseHandler);
}
protected void installModelPropertyChangeListener() {
propertyChangeHandler = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
ZoomModel old = (ZoomModel) evt.getOldValue();
if (old != null) {
old.removeChangeListener(getChangeHandler());
}
ZoomModel newValue = (ZoomModel) evt.getNewValue();
if (newValue != null) {
newValue.addChangeListener(getChangeHandler());
}
}
};
zoomComponent.addPropertyChangeListener("model", propertyChangeHandler);
}
protected void installKeyBindings() {
zoomIn = new ZoomInAction();
zoomOut = new ZoomOutAction();
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", zoomIn);
actionMap.put("zoomOut", zoomOut);
}
protected void installModelChangeListener() {
ZoomModel model = getModel();
if (model != null) {
model.addChangeListener(getChangeHandler());
}
}
#Override
public void installUI(JComponent c) {
zoomComponent = (ZoomComponent) c;
installMouseListener();
installModelPropertyChangeListener();
installKeyBindings();
installModelChangeListener();
}
protected void uninstallModelChangeListener() {
getModel().removeChangeListener(getChangeHandler());
}
protected void uninstallKeyBindings() {
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "donothing");
AbstractAction blank = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
}
};
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", blank);
actionMap.put("zoomOut", blank);
}
protected void uninstallModelPropertyChangeListener() {
zoomComponent.removePropertyChangeListener(propertyChangeHandler);
propertyChangeHandler = null;
}
protected void uninstallMouseListener() {
zoomComponent.removeMouseWheelListener(mouseHandler);
mouseHandler = null;
}
#Override
public void uninstallUI(JComponent c) {
uninstallModelChangeListener();
uninstallModelPropertyChangeListener();
uninstallKeyBindings();
uninstallMouseListener();
mouseHandler = null;
zoomComponent = null;
}
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
paintImage(g);
}
protected void paintImage(Graphics g) {
if (zoomComponent != null) {
ZoomModel model = zoomComponent.getModel();
Image image = model.getImage();
Dimension size = model.getScaledSize();
int x = (zoomComponent.getWidth() - size.width) / 2;
int y = (zoomComponent.getHeight() - size.height) / 2;
g.drawImage(image, x, y, size.width, size.height, zoomComponent);
}
}
public static ComponentUI createUI(JComponent c) {
return new BasicZoomUI();
}
protected ZoomModel getModel() {
return zoomComponent == null ? null : zoomComponent.getModel();
}
protected class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
zoomComponent.revalidate();
zoomComponent.repaint();
}
}
protected class ZoomAction extends AbstractAction {
private int delta;
public ZoomAction(int delta) {
this.delta = delta;
}
#Override
public void actionPerformed(ActionEvent e) {
ZoomModel model = getModel();
if (model != null) {
model.setValue(model.getValue() + delta);
}
}
}
protected class ZoomOutAction extends ZoomAction {
public ZoomOutAction() {
super(-5);
}
}
protected class ZoomInAction extends ZoomAction {
public ZoomInAction() {
super(5);
}
}
}
From here you could go and devise platform specific implementations, but I've decided to stick with the basic delegate...
Putting it all together
If that wasn't enough, before you can use any of it, you must install the delegate...
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
nb: Change your.awesome.package.name to reflect your actual package name...
Runnable Example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestZoom100 {
public static void main(String[] args) {
new TestZoom100();
}
public TestZoom100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
try {
DefaultZoomModel model = new DefaultZoomModel(ImageIO.read(new File("/your/awesome/image.jpg")));
model.setValue(50);
ZoomComponent zoomComp = new ZoomComponent();
zoomComp.setModel(model);
JSlider slider = new JSlider(model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(zoomComp));
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
}
Don't forget to change the package name for the BasicZoomUI to the package name you have it stored in and actually specify a image file ;)
I was given the task to create a custom swing component. I have my component functioning properly in a test application which includes JSlider that is used to zoom in and out on an Image. However I am required to present my custom component in a Model, UIDelegate, and Component class format and I am totally lost on how to convert my code so that it follows this format. Here is the code for my test application.
package test;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
public class ZoomDemo extends JComponent implements ChangeListener {
JPanel gui;
/**
* Displays the image.
*/
JLabel imageCanvas;
Dimension size;
double scale = 1.0;
private BufferedImage image;
public ZoomDemo() {
size = new Dimension(10, 10);
setBackground(Color.black);
try {
image = ImageIO.read(new File("car.jpg"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void setImage(Image image) {
imageCanvas.setIcon(new ImageIcon(image));
}
public void initComponents() {
if (gui == null) {
gui = new JPanel(new BorderLayout());
gui.setBorder(new EmptyBorder(5, 5, 5, 5));
imageCanvas = new JLabel();
JPanel imageCenter = new JPanel(new GridBagLayout());
imageCenter.add(imageCanvas);
JScrollPane imageScroll = new JScrollPane(imageCenter);
imageScroll.setPreferredSize(new Dimension(300, 100));
gui.add(imageScroll, BorderLayout.CENTER);
}
}
public Container getGui() {
initComponents();
return gui;
}
public void stateChanged(ChangeEvent e) {
int value = ((JSlider) e.getSource()).getValue();
scale = value / 100.0;
paintImage();
}
protected void paintImage() {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
BufferedImage bi = new BufferedImage(
(int)(imageWidth*scale),
(int)(imageHeight*scale),
image.getType());
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = AffineTransform.getTranslateInstance(0, 0);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
setImage(bi);
}
public Dimension getPreferredSize() {
int w = (int) (scale * size.width);
int h = (int) (scale * size.height);
return new Dimension(w, h);
}
private JSlider getControl() {
JSlider slider = new JSlider(JSlider.HORIZONTAL, 1, 500, 50);
slider.setMajorTickSpacing(50);
slider.setMinorTickSpacing(25);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(this);
return slider;
}
public static void main(String[] args) {
ZoomDemo app = new ZoomDemo();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(app.getGui());
app.setImage(app.image);
// frame.getContentPane().add(new JScrollPane(app));
frame.getContentPane().add(app.getControl(), "Last");
frame.setSize(700, 500);
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
The following code is the class format i need to follow
Component Class
package component;
import javax.swing.JComponent;
import javax.swing.JSlider;
import javax.swing.plaf.ComponentUI;
public class ProgressBar extends JComponent {
public static ComponentUI createUI(JComponent c) {
return new ZoomUI();
}
public void installUI(JComponent c){
}
public void uninstallUI (JComponent c){
}
}
Model CLass
public class ZoomModel extends JSLider {
}
UIDelegate Class
public class ZoomUI extends ComponentUI implements ChangeListener{
}
Any help on how I can implement my custom component in this format would be greatly appreciated. I am very new to Swing and documentation I have found on custom components has been very confusing and of little help.
test application
package test;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.*;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;
import component.ZoomComponent;
public class ZoomDemo extends JPanel implements PropertyChangeListener, ActionListener {
ZoomComponent zoomer;
JPanel board;
private BufferedImage image;
public ZoomDemo( ) {
super(true);
setLayout(new BorderLayout( ));
board = new JPanel(true);
board.setPreferredSize(new Dimension(300, 300));
board.setBorder(new LineBorder(Color.black, 5));
zoomer = new ZoomComponent();
add(board, BorderLayout.NORTH);
add(zoomer, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
UIManager.getDefaults().put("ZoomComponentUI", "component.BasicZoomUI");
ZoomDemo s= new ZoomDemo();
JFrame frame = new JFrame("Sample Sketch Application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(s);
frame.pack( );
frame.setVisible(true);
}
}
Okay, so that was a fun adventure into parts of the API I don't use :), start by having a read through How to Write a Custom Swing Component and it's associated links, this will give you the ground work to understand what is about to happen...
Model
The Interface
Personally, I always start with an interface, life is better with interfaces and it gives you more flexibility. Now, which model should you extend from (based on your requirements)...?
Well, the best choice I could find was the BoundedRangeModel, which is also used by the JSlider...this actually means that I can not only pass this model to the view, but to a JSlider and without any extra work, have the slider change the image!! Win-Win
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.BoundedRangeModel;
public interface ZoomModel extends BoundedRangeModel {
public Image getImage();
public Dimension getScaledSize();
}
The Abstract
Next, I like to make an abstract version, this is where I put "common" functionality, which is likely to be the same for most implementations, in this case, it might not be required, but I'm finckle like this...
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.DefaultBoundedRangeModel;
public abstract class AbstractZoomModel extends DefaultBoundedRangeModel implements ZoomModel {
public AbstractZoomModel() {
super(100, 0, 0, 200);
}
#Override
public Dimension getScaledSize() {
Dimension size = new Dimension(0, 0);
Image image = getImage();
if (image != null) {
double scale = getValue() / 100d;
size.width = (int) Math.round(image.getWidth(null) * scale);
size.height = (int) Math.round(image.getHeight(null) * scale);
}
return size;
}
}
So, you can see here, I've defined some basic properties, a starting zoom level of 100, a max level of 200 and a minimum level of 0, plus I've implemented the getScaledSize, which is used a bit and makes life easier...
The Default...
Now, because we like been nice, we provide a "default" implementation of the model. This is pretty basic in that all it does it takes a reference to an image...
import java.awt.Image;
public class DefaultZoomModel extends AbstractZoomModel {
Image image;
public DefaultZoomModel(Image image) {
this.image = image;
}
#Override
public Image getImage() {
return image;
}
}
You could create implementations that download images from an URL for example...
The View
Okay, this is the actually component itself, which gets added to your UI. It contains the basic functionality need to construct and prepare the UI delegate and manage the model. The key thing of interest here is the use of the property change support to provide notification of the change to the model, this is important as you will see...
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JComponent;
import javax.swing.UIManager;
public class ZoomComponent extends JComponent {
private static final String uiClassID = "ZoomComponentUI";
private ZoomModel model;
public ZoomComponent() {
setBackground(Color.black);
setFocusable(true);
updateUI();
}
public void setModel(ZoomModel newModel) {
if (model != newModel) {
ZoomModel old = model;
this.model = newModel;
firePropertyChange("model", old, newModel);
}
}
public ZoomModel getModel() {
return model;
}
#Override
public Dimension getPreferredSize() {
ZoomModel model = getModel();
Dimension size = new Dimension(100, 100);
if (model != null) {
size = model.getScaledSize();
}
return size;
}
public void setUI(BasicZoomUI ui) {
super.setUI(ui);
}
#Override
public void updateUI() {
if (UIManager.get(getUIClassID()) != null) {
ZoomUI ui = (ZoomUI) UIManager.getUI(this);
setUI(ui);
} else {
setUI(new BasicZoomUI());
}
}
public BasicZoomUI getUI() {
return (BasicZoomUI) ui;
}
#Override
public String getUIClassID() {
return uiClassID;
}
}
The UI Delegate
Now the other fun stuff...If we follow standard convention, you would normally provide an abstract concept of the UI delegate, for example...
import javax.swing.plaf.ComponentUI;
public abstract class ZoomUI extends ComponentUI {
}
From this, other delegates will grow...
Basic UI Delegate
Convention would normally suggest you provide a "basic" implementation, doing a lot of the heavy lifting, but allowing other implementations the opportunity to jump in change things to there likely
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;
public class BasicZoomUI extends ZoomUI {
private ZoomComponent zoomComponent;
private MouseAdapter mouseHandler;
private ChangeListener changeHandler;
private Action zoomIn;
private Action zoomOut;
private PropertyChangeListener propertyChangeHandler;
protected ChangeListener getChangeHandler() {
if (changeHandler == null) {
changeHandler = new ChangeHandler();
}
return changeHandler;
}
protected void installMouseListener() {
mouseHandler = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
zoomComponent.requestFocusInWindow();
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
int amount = e.getWheelRotation();
ZoomModel model = zoomComponent.getModel();
if (model != null) {
int value = model.getValue();
model.setValue(value + amount);
}
}
};
zoomComponent.addMouseListener(mouseHandler);
zoomComponent.addMouseWheelListener(mouseHandler);
}
protected void installModelPropertyChangeListener() {
propertyChangeHandler = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
ZoomModel old = (ZoomModel) evt.getOldValue();
if (old != null) {
old.removeChangeListener(getChangeHandler());
}
ZoomModel newValue = (ZoomModel) evt.getNewValue();
if (newValue != null) {
newValue.addChangeListener(getChangeHandler());
}
}
};
zoomComponent.addPropertyChangeListener("model", propertyChangeHandler);
}
protected void installKeyBindings() {
zoomIn = new ZoomInAction();
zoomOut = new ZoomOutAction();
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "zoomOut");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "zoomIn");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "zoomOut");
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", zoomIn);
actionMap.put("zoomOut", zoomOut);
}
protected void installModelChangeListener() {
ZoomModel model = getModel();
if (model != null) {
model.addChangeListener(getChangeHandler());
}
}
#Override
public void installUI(JComponent c) {
zoomComponent = (ZoomComponent) c;
installMouseListener();
installModelPropertyChangeListener();
installKeyBindings();
installModelChangeListener();
}
protected void uninstallModelChangeListener() {
getModel().removeChangeListener(getChangeHandler());
}
protected void uninstallKeyBindings() {
InputMap inputMap = zoomComponent.getInputMap(JComponent.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "donothing");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "donothing");
AbstractAction blank = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
}
};
ActionMap actionMap = zoomComponent.getActionMap();
actionMap.put("zoomIn", blank);
actionMap.put("zoomOut", blank);
}
protected void uninstallModelPropertyChangeListener() {
zoomComponent.removePropertyChangeListener(propertyChangeHandler);
propertyChangeHandler = null;
}
protected void uninstallMouseListener() {
zoomComponent.removeMouseWheelListener(mouseHandler);
mouseHandler = null;
}
#Override
public void uninstallUI(JComponent c) {
uninstallModelChangeListener();
uninstallModelPropertyChangeListener();
uninstallKeyBindings();
uninstallMouseListener();
mouseHandler = null;
zoomComponent = null;
}
#Override
public void paint(Graphics g, JComponent c) {
super.paint(g, c);
paintImage(g);
}
protected void paintImage(Graphics g) {
if (zoomComponent != null) {
ZoomModel model = zoomComponent.getModel();
Image image = model.getImage();
Dimension size = model.getScaledSize();
int x = (zoomComponent.getWidth() - size.width) / 2;
int y = (zoomComponent.getHeight() - size.height) / 2;
g.drawImage(image, x, y, size.width, size.height, zoomComponent);
}
}
public static ComponentUI createUI(JComponent c) {
return new BasicZoomUI();
}
protected ZoomModel getModel() {
return zoomComponent == null ? null : zoomComponent.getModel();
}
protected class ChangeHandler implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
zoomComponent.revalidate();
zoomComponent.repaint();
}
}
protected class ZoomAction extends AbstractAction {
private int delta;
public ZoomAction(int delta) {
this.delta = delta;
}
#Override
public void actionPerformed(ActionEvent e) {
ZoomModel model = getModel();
if (model != null) {
model.setValue(model.getValue() + delta);
}
}
}
protected class ZoomOutAction extends ZoomAction {
public ZoomOutAction() {
super(-5);
}
}
protected class ZoomInAction extends ZoomAction {
public ZoomInAction() {
super(5);
}
}
}
From here you could go and devise platform specific implementations, but I've decided to stick with the basic delegate...
Putting it all together
If that wasn't enough, before you can use any of it, you must install the delegate...
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
nb: Change your.awesome.package.name to reflect your actual package name...
Runnable Example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestZoom100 {
public static void main(String[] args) {
new TestZoom100();
}
public TestZoom100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
UIManager.getDefaults().put("ZoomComponentUI", "your.awesome.package.name.BasicZoomUI");
try {
DefaultZoomModel model = new DefaultZoomModel(ImageIO.read(new File("/your/awesome/image.jpg")));
model.setValue(50);
ZoomComponent zoomComp = new ZoomComponent();
zoomComp.setModel(model);
JSlider slider = new JSlider(model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(zoomComp));
frame.add(slider, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
}
Don't forget to change the package name for the BasicZoomUI to the package name you have it stored in and actually specify a image file ;)
So I have seen a few threads about this already and I need some help about how to fix it specifically. When you hold down a key, Java will read the first key press, then there will be a small delay, and then it will continuously read the key press until you release the key.
public void keyPressed(KeyEvent key) {
int code = key.getKeyCode();
if (code == KeyEvent.VK_DOWN) {
//Do stuff
}
if (code == KeyEvent.VK_LEFT) {
//Do stuff
}
if (code == KeyEvent.VK_RIGHT) {
//Do stuff
}
if (code == KeyEvent.VK_UP) {
//Do stuff
}
}
That is my current code. I heard that to fix this you can create a timer which rapidly checks for key presses, but I'm not really sure how to do that. Would appreciate some help here or if there is a better solution.
The basic answer to your question is, you can't, the delay is OS specific.
The longer answer is, you should be ignoring the individual events themselves and monitor a change in state (between press and release) through the use of appropriate flags.
This means, that when a key is pressed, you set some flag which you program can use to change the state of the program and when released, you reset it.
This disassociates the event from state change and provides you with much more flexibility, as your program doesn't care what caused the state change, only that the state has changed and it should react to it.
This will require you to have some kind of "loop" whose responsibility it is, is to monitor this change and react to it accordingly. In gaming, this is commonly known as a "game-loop", but can also been known as "main-loop".
It's this "loops" responsibility to update the state of the program and get it painted.
Below is a very simple example which uses the key bindings API and a javax.swing.Timer to demonstrate the basic concepts
import com.sun.glass.events.KeyEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MoveMe {
public static void main(String[] args) {
new MoveMe();
}
public MoveMe() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MovementState {
public int xDirection;
public int yDirection;
}
public class TestPane extends JPanel {
private MovementState movementState;
private Rectangle box;
public TestPane() {
movementState = new MovementState();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "down-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "down-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "up-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "up-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "left-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "left-released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right-pressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "right-released");
am.put("down-pressed", new YDirectionAction(movementState, 2));
am.put("down-released", new YDirectionAction(movementState, 0));
am.put("up-pressed", new YDirectionAction(movementState, -2));
am.put("up-released", new YDirectionAction(movementState, 0));
am.put("left-pressed", new XDirectionAction(movementState, -2));
am.put("left-released", new XDirectionAction(movementState, 0));
am.put("right-pressed", new XDirectionAction(movementState, 2));
am.put("right-released", new XDirectionAction(movementState, 0));
box = new Rectangle(90, 90, 20, 20);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box.x += movementState.xDirection;
box.y += movementState.yDirection;
if (box.x < 0) {
box.x = 0;
} else if (box.x + box.width > getWidth()) {
box.x = getWidth() - box.width;
}
if (box.y < 0) {
box.y = 0;
} else if (box.y + box.height > getHeight()) {
box.y = getHeight() - box.height;
}
repaint();
}
});
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();
g2d.setColor(Color.RED);
g2d.fill(box);
g2d.dispose();
}
}
public abstract class AbstractDirectionAction extends AbstractAction {
private final MovementState movementState;
private final int value;
public AbstractDirectionAction(MovementState movementState, int value) {
this.movementState = movementState;
this.value = value;
}
public MovementState getMovementState() {
return movementState;
}
public int getValue() {
return value;
}
}
public class YDirectionAction extends AbstractDirectionAction {
public YDirectionAction(MovementState movementState, int value) {
super(movementState, value);
}
#Override
public void actionPerformed(ActionEvent e) {
getMovementState().yDirection = getValue();
}
}
public class XDirectionAction extends AbstractDirectionAction {
public XDirectionAction(MovementState movementState, int value) {
super(movementState, value);
}
#Override
public void actionPerformed(ActionEvent e) {
getMovementState().xDirection = getValue();
}
}
}
Im newish to keylistener, but recently I made a little program which will be below, which uses keylistener to make the A,W,S,D keys move a square. Well I used dropbox to get the program on this laptop, and now it doesn't work. It seems weird to me that it works on a keyboard, but not on a laptop keyboard. Any ideas why? Here is the code, well a portion:
public class Game extends JApplet implements KeyListener
{
public boolean isMoving = false;
int test = 0;
Rect r = new Rect();
public void keyPressed(KeyEvent e){}
public void keyReleased(KeyEvent e){} // ignore
public void keyTyped(KeyEvent e)
{
char keyChar = e.getKeyChar();
if (keyChar == KeyEvent.VK_S)
{
r.yVelocity -= 1;
}
if (keyChar == KeyEvent.VK_W)
{
r.yVelocity+=1;
}
if (keyChar == KeyEvent.VK_A)
{
r.xVelocity -=1;
}
if (keyChar == KeyEvent.VK_D)
{
r.xVelocity +=1;
}
}
KeyListener has focus issues. That is, it will only respond to key events when is focusable AND has focus.
The simply solution would be to make the component which the KeyListener is registered to focusable and use requestFocusInWindow, but this makes no gurentees that the component will recieve key board focus.
A better solution would be to use the Key Bindings API which allows you to configure the focus level required for key events to be raised...
Updated with a key bindings example/test
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
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 TestKeyBindings {
public static void main(String[] args) {
new TestKeyBindings();
}
public TestKeyBindings() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new KeyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class KeyPane extends JPanel {
private Triangle up;
private Triangle down;
private Triangle left;
private Triangle right;
private Set<String> activeKeys;
public KeyPane() {
activeKeys = new HashSet<>(4);
up = new Triangle(20);
down = new Triangle(20);
left = new Triangle(20);
right = new Triangle(20);
AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(-90), 10, 10);
left.transform(at);
at = AffineTransform.getRotateInstance(Math.toRadians(180), 10, 10);
down.transform(at);
at = AffineTransform.getRotateInstance(Math.toRadians(-270), 10, 10);
right.transform(at);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "leftPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "rightPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "leftReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "rightReleased");
ActionMap am = getActionMap();
am.put("upPressed", new DirectionAction("up", true));
am.put("downPressed", new DirectionAction("down", true));
am.put("leftPressed", new DirectionAction("left", true));
am.put("rightPressed", new DirectionAction("right", true));
am.put("upReleased", new DirectionAction("up", false));
am.put("downReleased", new DirectionAction("down", false));
am.put("leftReleased", new DirectionAction("left", false));
am.put("rightReleased", new DirectionAction("right", false));
}
public void addKey(String name) {
activeKeys.add(name);
repaint();
}
public void removeKey(String name) {
activeKeys.remove(name);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - (up.getBounds().width * 3)) / 2;
int y = (getHeight() - 10) / 2;
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
if (activeKeys.contains("left")) {
g2d.fill(left.createTransformedShape(at));
} else {
g2d.draw(left.createTransformedShape(at));
}
at = AffineTransform.getTranslateInstance(x + 40, y);
if (activeKeys.contains("right")) {
g2d.fill(right.createTransformedShape(at));
} else {
g2d.draw(right.createTransformedShape(at));
}
at = AffineTransform.getTranslateInstance(x + 20, y - 20);
if (activeKeys.contains("up")) {
g2d.fill(up.createTransformedShape(at));
} else {
g2d.draw(up.createTransformedShape(at));
}
at = AffineTransform.getTranslateInstance(x + 20, y + 20);
if (activeKeys.contains("down")) {
g2d.fill(down.createTransformedShape(at));
} else {
g2d.draw(down.createTransformedShape(at));
}
g2d.dispose();
}
public class DirectionAction extends AbstractAction {
private String name;
private boolean pressed;
public DirectionAction(String name, boolean pressed) {
this.name = name;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent e) {
if (pressed) {
addKey(name);
} else {
removeKey(name);
}
}
}
}
public class Triangle extends Path2D.Double {
public Triangle(int size) {
moveTo(size / 2, 0);
lineTo(size, size);
lineTo(0, size);
closePath();
}
}
}