I wrote some application and wanted to add some keyboard input to it.
My main class extends a JPanel so i could add the keyAdapter into the constructor.
The keyAdapter is a new class called "InputAdapter" extending keyadapter with it's keyPressed() and keyReleased() method. on click or release the console should print some string, e.g. here "Test"
I don't know why, but the console won't print any text. Also, when I tell it to turn a sprites visibility to false nothing happens as well.
So i guess the KeyAdapter isn't working properly, so could someone take a closer look into my codelines?
i guess this issue has nothing to do with the other implemented classes i wrote because when removing them, the issue with the non working keyboard input remains.
package com.ochs.game;
public class Game extends JPanel implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 3;
public boolean isRunning;
public Game() {
addKeyListener(new InputAdapter());
setFocusable(true);
requestFocus();
start();
}
public void start() {
isRunning = true;
new Thread(this).start();
}
public void stop() {
isRunning = false;
}
public void run() {
init();
while(isRunning) {
update();
repaint();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
System.out.println("Thread sleep failed.");
}
}
}
public void init() {
}
public void update() {
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
}
public static void main(String[] args) {
Game gameComponent = new Game();
Dimension size = new Dimension(WIDTH*SCALE, HEIGHT*SCALE);
JFrame frame = new JFrame("Invaders");
frame.setVisible(true);
frame.setSize(size);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(gameComponent);
}
public class InputAdapter extends KeyAdapter {
#Override
public void keyPressed(KeyEvent arg0) {
System.out.println("Test");
}
#Override
public void keyReleased(KeyEvent arg0) {
System.out.println("Test");
}
}
}
Your code works for me:
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.6) (6b27-1.12.6-1ubuntu0.12.04.2)
OpenJDK Client VM (build 20.0-b12, mixed mode, sharing)
Tip 1 - You should override paintComponent(Graphics g) i guess, not paint()
public void paintComponent(Graphics g){
super.paintComponent(g);
//...
}
Tip 2 - Use addNotify() on your JPanel:
public void addNotify(){
super.addNotify();
//start from here
new Thread(this).start();
}
Tip 3 - Launch your app this way, from the EDT Thread (see What does SwingUtilities.invokeLater do?)
SwingUtilities.invokeLater(new Runnable() {
public void run(){
//your code
}
});
Hope it helps!
There are many possible reasons why this might not work. KeyListener is very fussy. It requires that the component that is registered to not only be focusable, but have focus.
Even though your component seems to both these things, if, for some reason, focus is grabbed by another component, the KeyListener will stop working.
You should use requestFocusInWindow and requestFocus is unreliable, but a better solution would be to use Key bindings, which has the ability to over come all that messiness with focus...
You should avoid overriding paint and use paintComponent instead, check out Performing Custom Painting for more details.
Mixing threads with Swing is tricky, you will also want to be sure that you are not violating the single thread rules of Swing when you are updating the your state. Check out Concurrency in Swing for more details
Your basic code design is old AWT painting code. I echo everything MadProgrammer says for a better Swing design.
In addition:
there is no need for an empty init() method. Get rid of the method and the call to the method.
same for the update() method.
The big problem with the posted code is that you add the panel to the frame AFTER the frame is visible. You should always add components to the frame before making the frame visible:
JFrame frame = new JFrame("Invaders");
frame.add(gameComponent);
...
frame.setVisible(true);
Don't take the easy way out and just make the above change. Write code for a Swing program not an AWT program.
Related
What is the best way to listen for keyboard input in a Java Applet?
I have an applet which opens a JFrame and I am using a KeyListener to listen for keyboard input. This works fine in my development environment (eclipse), but when I run the applet through a browser (I have tried Firefox and IE) it does not respond to keyboard events.
However, if I run the applet and then minimize and maximize the frame, it works.
I have tried setting focus to the JFrame in many different ways and also programmatically minimizing and maximizing it, but to no effect.
I have also tried key bindings, but with the same problem.
I have trimmed my code down to the barest essentials of the problem and pasted it below.
Can someone see what I am doing wrong or suggest a better solution?
public class AppletTest extends Applet
{
private GuiTest guiTest;
public void init() {
guiTest = new GuiTest();
final AppletTest at = this;
guiTest.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent ke) {
at.keyPressed(ke);
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent e) {}
});
}
private void keyPressed(KeyEvent ke)
{
System.out.println("keyPressed "+KeyEvent.getKeyText(ke.getKeyCode()));
getGuiTest().test(KeyEvent.getKeyText(ke.getKeyCode()));
}
}
public class GuiTest extends JFrame {
String teststring = "?";
public GuiTest()
{
setSize(100,100);
setEnabled(true);
setVisible(true);
setFocusable(true);
requestFocus();
requestFocusInWindow();
toFront();
}
public void test(String t)
{
teststring = t;
repaint();
}
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
g.drawString(teststring, 50, 50);
}
}
I solved the problem. If I create the JFrame following a button press or mouse event on the applet, the key listener on the JFrame works. Apparently, creating the frame from Applet.init() means that key listeners do not function correctly when opened through a browser.
However, the question remains - why? If someone can explain this, I would greatly appreciate it.
I thought it might be because the frame should be created on the event dispatch thread, but using SwingUtilities.invokeLater or invokeAndWait did not work.
I think you are running into the plugin focus issue: in many modern browser a plugin only gains focus through either the user clicking on it or using Javascript. This typically affects Flash but it might be that it also affects applets. Try Adobe's recommendations at http://kb2.adobe.com/cps/155/tn_15586.html.
Let me know if that works for you.
first questing for me here. Been searching forever but cant seem to find the answer.
Im working on a school assignment. Got given an ui and are supposed to make the different panels in it do different things in separate threads. Anyway, Im trying to make a triangle rotate inside one of the JPanels. I have managed to draw it and somewhat rotate it, but when I try to make a loop to update it it just blinks and then disappears again. Heres the code Ive written.
StartAssignment, starts the application
public class StartAssignment1 {
public static void main(String[] args) {
Controller controller = new Controller();
}
Controller, recieves calls from the ui and executes various functions in other classes
public class Controller {
private GUIAssignment1 gui = new GUIAssignment1(this);
private RotateShape rotateShape;
private Thread t1;
public Controller() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.Start();
}
});
}
public void startT1(JPanel panel) {
rotateShape = new RotateShape(panel);
t1 = new Thread(rotateShape);
t1.start();
}
public void t1Shutdown() {
rotateShape.shutdown();
}
RotateShape, where Im trying to rotate the damned thing
public class RotateShape implements Runnable {
JPanel panel;
private volatile boolean t1Running = true;
public RotateShape(JPanel panel) {
this.panel = panel;
}
private void draw() {
Graphics2D g2 = (Graphics2D) panel.getGraphics();
g2.rotate(Math.toRadians(10));
g2.drawPolygon(new int[] {50, 100, 150}, new int[] {150, 50, 150}, 3);
}
public void shutdown() {
t1Running = false;
System.out.println("Shutdown");
}
#Override
public void run() {
while (t1Running) {
try {
draw();
Thread.sleep(500);
System.out.println("loop working");
panel.repaint();
panel.revalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I'm not certain because I don't have access to your GUI code and therefore cannot test it myself, but here's an idea:
It looks like you're rotating your Graphics2D object by a fixed amount on every re-render (i.e. every invocation of draw()). It's possible that the internal JPanel code initiates the Graphics2D at a default rotation before every re-render, which may be why your rotation only causes the shape to "somewhat rotate."
Maybe try storing a variable for the radian offset (e.g. double offset;) as a member field of the RotateShapeclass, incrementing it on every re-render, then calling g2.rotate(Math.toRadians(offset));?
Addendum 1:
The reason that it may draw on top of the previous render is because the Graphics2D object is not clearing the canvas between re-renders. One way to fix this is to "clear" the canvas at the beginning of the draw() method by filling a rectangle that covers the whole canvas.
Addendum 2:
When you call panel.repaint(), it triggers the paintComponent() method of the JPanel object. So, by calling repaint() and your own draw() method, you are doing two separate renders, which may cause some graphical errors. If you were able to extends the JPanel class and use that, you should define an override to replace draw():
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add your rendering operations here
}
This way, calls to repaint() should trigger this method.
How do I override a paintComponent method with response to a state change?
Error message: void is an invalid type for the variable paintComponent
public class MyContainer extends Container {
public void paintComponent(Graphics m){
m.drawArc(100,100,100,100,100,100);
m.setColor(Color.green);
m.fillArc(100,100,100,100,100,100);
}
public static void main(String[] args){
Container y = new Container();
JFrame x = new JFrame();
JPanel gg = new JPanel();
x.add(y);
x.setTitle(" Shape Changer");
x.setBounds(100,50,500,300);
x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x.getContentPane().add(new ContentPanel());
x.getContentPane().add(new ContnetPanel());
x.setContentPane(new ContnetPanel());
x.setVisible(true);
}
static class ContentPanel extends JPanel{
private Graphics g;
private JPanel ss;
public void paint(Graphics g){
g.drawArc(100,100,100,100,100,100);
g.fillRect(100, 100,100,100);
}
public ContentPanel(){
}
}
static class ContnetPanel extends JPanel implements ActionListener, ChangeListener{
JComboBox comboerbox;
class appres {
public void paint(Graphics h){
h.drawRect(100,100,100,100);
h.setColor(Color.red);
h.fillRect(100,100,100,100);
}
}
public ContnetPanel(){
comboerbox = new JComboBox();
comboerbox.addItem("Red Square");
comboerbox.addItem("Blue Square");
comboerbox.addItem("Green Square");
comboerbox.setSelectedIndex(1);
add(comboerbox);
setLayout(new GridLayout(2,1));
}
#Override
protected void paintComponent(Graphics h){
super.paintComponent(h);
h.drawArc(100,100,100,100,100,100);
h.setColor(Color.blue);
h.fillArc(100,100,100,100,100,100);
repaint();
}
int yy = 0;
public void actionPerformed(ActionEvent evt){
switch(comboerbox.getSelectedIndex()){
case 0:yy=0;
case 1: yy=1;
case 2: yy=2;
}
}
//evt.getSource()==comboerbox
public void stateChanged(ChangeEvent evt){
if(evt.getSource()==comboerbox){
#Override
protected void paintComponent(Graphics h){
super.paintComponent(h);
h.drawArc(100,100,100,100,100,100);
h.setColor(Color.blue);
h.fillArc(100,100,100,100,100,100);
repaint();
}
}
else
{
System.out.println("DONE");
}
}
}
}
Of course, the paintComponent method isn't a variable. How would I override paintComponent here? Or is a better way to change the shape with response to state change? That would be great too!
Thanks in advance, love you guys!
In your last question: How do I make the superclass go beyond just the content pane? you were given a link to the Swing tutorial for some Swing basics.
Well there is also a section on Custom Painting for you to read. You can then download the example and play with it to understand how painting works.
Basically the Container class doesn't have a paintComponent() method so you should not be trying to do custom painting in that class.
If you want to change a painting property, then you need to add a method to your class to change the state of the property and then invoke repaint() on itself.
So from the tutorial example in Step 3 you can see how the moveSquare(...) method changes the state of the class and then invokes repaint().
Note you should never invoke repaint() in the paintComponent() method since this will cause the painting to be continually rescheduled.
What I am looking to do is have the user to be able to change perspectives from a KeyListener. If the user hits the specified key, than the perspective should change. Any ideas?
Even if I override the methods they still do not work. I have also tried KeyAdapter
package com.development.gameOne.environment.component;
import java.applet.Applet;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import com.development.gameOne.environment.applet.drawing.Perspective;
import com.development.gameOne.environment.applet.perspectives.p1.FirstPerspective;
import com.development.gameOne.environment.applet.perspectives.p2.SecondPerspective;
public class Component extends Applet implements KeyListener {
private static final long serialVersionUID = 1L;
private Dimension size = new Dimension(1280, 720);
private ArrayList<Perspective> perspectives = new ArrayList<Perspective>();
private boolean running = true;
private boolean switchPerspective = false;
public Component() {
setPreferredSize(size);
loadPerspectives();
addKeyListener(this);
setFocusable(true);
setVisible(true);
start();
}
private void loadPerspectives() {
perspectives.add(new FirstPerspective());
perspectives.add(new SecondPerspective());
}
public static void main(String[] args) {
new Component();
}
#Override
public void paint(Graphics g) {
while (running) {
for (Perspective p : perspectives) {
System.out.println(p.getPerspective());
while (!switchPerspective) {
System.out.println("Rendering");
p.start(g);
sleep(100);
}
switchPerspective = false;
}
sleep(10);
}
}
public static void sleep(int renderSpeed) {
try {
Thread.sleep(renderSpeed);
}
catch (Exception e) {}
}
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_SHIFT:
System.out.println("KeyPressed");
switchPerspective = true;
break;
}
}
public void keyTyped(KeyEvent e) { }
public void keyReleased(KeyEvent e) {}
}
The program runs, but doesn't switch perspectives. I cannot seem to get the KeyListener
to work at all. I really have no idea what to do.
I don't think the issue is with your KeyListener, but is with your paint process
#Override
public void paint(Graphics g) {
while (running) {
for (Perspective p : perspectives) {
System.out.println(p.getPerspective());
while (!switchPerspective) {
System.out.println("Rendering");
p.start(g);
sleep(100);
}
switchPerspective = false;
}
sleep(10);
}
}
This will block the Event Dispatching Thread, preventing it from ever been able to process new events coming into the system
Take a look at Painting in AWT and Swing for details about how painting works in AWT.
The (simple) solution, in this case, would be to provide a other Thread which handles the timing between updates and simple call repaint when you want the UI updated.
A better solution would be take take advantage of the a BufferStrategy instead. It still require a Thread, but stops you from breaking the painting chain.
As a side note. AWT Applets are woefully out-of-date and were replaced by JApplet before 2000. Having said that, I would recommend against using applets at all, as they have enough problems which only increases the difficulty of starting development and focus on something like a JPanel added to an instance of a JFrame instead.
Take a look at Performing Custom Painting and Creating a GUI With JFC/Swing
I'd also drop the use of KeyListener as soon as you can in favour of Swing's Key bindings API. See How to Use Key Bindings for more details
I'd also avoid calling your applet Component, there already is a class called Component and this is just going to confuse matters...
And applets, definitely, should not have a main method. They are expected to be loaded by the browser directly and have a different, defined, life cycle.
The JFrame will not shut down when the default "X" button is clicked. I think this problem has something to do with the main thread not being read, but I don't understand the intricacies of swing or honestly, threads in general. "Window" is an extension of JFrame, "Boxy" drives the program. Program is only in initial stages. Also, I'd like to know how to get the main thread run on every loop-over. Couldn't find anything about this in other questions.
public class Window extends JFrame implements KeyListener{
private static final long serialVersionUID = 1L;
JPanel panel;
public Window(){
super("FileTyper");
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setSize(200,100);
super.setResizable(false);
panel = new JPanel();
super.getContentPane().add(panel);
super.setFocusable(true);
addKeyListener(this);
super.setVisible(true);
}
public void update(){
}
public void render(Graphics2D g){
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_F9:
break;
case KeyEvent.VK_F10:
break;
}
}
#Override
public void keyTyped(KeyEvent arg0) {
}
}
public class Boxy {
public Window window;
public static void main (String args[]){
start();
}
public Boxy(){
init();
boolean forever = true;
while(forever){
update();
render();
delay();
}
}
private void init(){
window = new Window();
}
private void update(){
window.update();
}
private void render(){
Graphics2D g2 = (Graphics2D) window.getContentPane().getGraphics();
window.render(g2);
g2.fillRect(0, 0, 100, 100);
}
private void delay(){
try {Thread.sleep(20);} catch (InterruptedException ex) {System.out.println("ERROR: Delay compromised");}
}
public static void start(){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Boxy box = new Boxy();
}
});
}
}
I would suggest that you are blocking the Event Dispatching Thread with
while(forever){
update();
render();
delay();
}
This is preventing the Event Queue from processing the event that would close the window.
Start by taking a look at Concurrency in Swing. I would suggest you might like to take a look at something like javax.swing.Timer to start with, but if you want more control of the frame rate, you're going to need to use some kind of Thread. Remember though, Swing expects all updates to be executed from within the context of the Event Dispatching Thread.
Custom painting in Swing is not done by using something like...
Graphics2D g2 = (Graphics2D) window.getContentPane().getGraphics();
The Graphics context is short lived, anything your paint to it (using this method) will be destroyed on the next paint cycle.
Instead, you should use something like a JPanel as the bases for your painting and override it's paintComponent method and render the state from within it, when ever it is called.
You would then simply need to call repaint when you want to update the component.
Take a look at Performing Custom Painting for more details.
I would also recommend that you take a look at How to use Key Bindings as an aletrnative to KeyListener
Your program's "game" loop is incorrect:
while(forever){
update();
render();
delay();
}
Rather than looping the program, it freezes it by tying up the Swing event thread or EDT (for Event Dispatch Thread). You should use a Swing Timer instead for this functionality.