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.
Related
(Sorry I can't get a working E.g. of the issue, I need help for my help!)
I'm in the process of creating a custom game engine and have come upon an issue whereby while the game is running - the game stops taking input
I've checked and the program seems to continue running in the background. It also doesn't seem to vary with different machines
(My main device is a Mac Book Pro 2011)
import java.awt.event.*;
import java.io.IOException;
import java.awt.*;
import javax.swing.*;
public class Focus extends JFrame implements KeyListener {
private static final long serialVersionUID = 1L;
char currKey = '\0';
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {public void run() {new UIManager();}});
}
public Focus() throws IOException {
Container contentPane = getContentPane();
contentPane.add(new DrawCanvas());
addKeyListener(this);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setFocusable(true);
setVisible(true);
}
private class DrawCanvas extends JPanel {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics pen) {
//Drawloop
if(currKey == 'k') {
//This is the code that randomly stops running
System.out.println("Yo");
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
#Override
public void keyTyped(KeyEvent e) {
currKey = e.getKeyChar();
}
#Override
public void keyPressed(KeyEvent e) {
currKey = '\0';
}
#Override
public void keyReleased(KeyEvent e) {
}
}
The code looks right to me (But then, it always does) and the only possible tripping point is that AWT is Instantiated in Main, Run in UIManager and the movement code resides in player though I don't know enough about AWT to know whether this would be the case and relocating the code in a backup lead to a program crash. Any help would be greatly appreciated.
Turns out it was an issue with Java and MacOS key repeats- fixed in a newer Java version
To fix update java
I'm having an error when trying to add a component to a JFrame.
This is the first class:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class FrameG extends JFrame
{
private static final String MOVE_UP = "move up";
public static int frameID = 1;
private static JFrame window = new JFrame();
private static openWin frame = new frame01();
public static void main(String[] args) {
window.setSize(1500,900);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setResizable(true);
frame.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_UP);
frame.getActionMap().put(MOVE_UP, new movement());
mainloop();
}
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
}
class movement extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("hi");
}
}
and the second class (it extends a class with an abstract paint method that extends JComponent):
import java.awt.Graphics;
import java.awt.*;
import javax.swing.JComponent;
import java.awt.geom.*;
public class frame01 extends openWin{
#Override
public void paint(Graphics g) {
Graphics2D pic = (Graphics2D) g;
pic.setBackground(Color.BLACK);
}
}
The error may be the invalid part, but i'm not sure what it is:
frameg.frame01[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=]
You code shows a distinct lack of understanding into how Swing works
This...
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
Is a bad idea of two reasons, the first been that it will eventually generate a StackOverflowException, the second been that it violates the single thread rules of Swing.
Normally, a while-loop would generally be better, but as you're dealing with Swing based components you should consider using either a Swing Timer or SwingWorker, depending on what you are hoping to achieve.
Constantly adding and remove components also seems like a weird idea, but there's no context to the question to know what it is you're hoping to achieve. Having said that, as a general rule, if you want to switch between views a CardLayout is generally considered the preferred solution
This...
public class frame01 extends openWin{
#Override
public void paint(Graphics g) {
Graphics2D pic = (Graphics2D) g;
pic.setBackground(Color.BLACK);
}
}
is simply doing nothing, other then breaking the paint chain, which could cause no end of weird paint artefacts to be generated. All you code does if temporarily changes the background color of the Graphics context, but since you don't paint anything with it, it's effectively meaningless
As a general rule, it's recommended to override the paintComponent method of Swing components (and call super.paintComponent before you do any custom painting).
I would highly recommend that you take a look at:
Painting in AWT and Swing
Performing Custom Painting
Concurrency in Swing
How to use Swing Timers
Worker Threads and SwingWorker
How to Use CardLayout
Your code has lots of problems. The line private static openWin frame = new frame01(); seems fishy as I said in my comment. But its not the worst part:
THIS:
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
Is BAD! And it probably breaks since you are constantly creating a new frame01() and adding it to your window.
Also DO NOT override paint but paintComponent. Another thing I dont get is why override it in a separate class and not in openWin itself.
Another thing, DO NOT SLEEP THE MAIN THREAD, if you really must then run your own and put IT to sleep.
What is the purpose of this code? I'm trying to understand it so I can help you redesign it.
PS: not really an answer, just a bit longer than a comment.
Never use a Thread and it's start and stops as a timer. Use javax.swing.Timer instead. Also you've created a recursive function to implement an infinite loop which is very wired and prone to fail. Swing has it's own thread and when you mix it up with another threads unconsciously it transform your code to a monster we are all afraid of.
i know multithreading a bit but not in vast and i think the problem is of multithreading. I am calling a method to set label's text by invoking a new thread and leaving it blank after a specified time. I am getting the desired output every time but not only the place which i am going to show you by my piece of code. I am expecting that message should be set and disappeared after the specified time and the window should be minimized after that time. But what actually happening is when it is going to the other thread main thread execution starts and goes for sleep for 5 sec and the message is not appearing and after 5 sec window is getting minimized without showing the message which i am setting on the label.
(Main thread)
Validation.setMessageOnLabel("Username and password has been copied", jLabel15,1.5F);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
setState(ICONIFIED);
validation.java (setMessageOnLabel())
static public void setMessageOnLabel(final String msg, final JLabel label, final float time)
{
new Thread(new Runnable() {
#Override
public void run() {
label.setText(msg);
try {
Thread.sleep((long) (time*1000));
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
label.setText("");
}
}).start();
}
Since you're calling setState() directly, I assume the first code snippet is part of a JFrame. In that case you're most probably sending the event dispatch thread to sleep for 5 seconds and thus prevent screen updates during that time.
Put the sleep into another thread or use a swing worker instead and call setState() on the EDT in the worker's callback method, since setState() is not labelled as thread-safe and calling it on a thread other than the EDT might result in unexpected behavior.
From the linked tutorial:
Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
Don't use Thread.sleep(5000);, that block EDT.
For that purposes you can use swing Timer, examine next example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class TestFrame extends JFrame {
private JLabel lbl;
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
lbl = new JLabel(" ");
JButton setText = new JButton("setText");
setText.addActionListener(getActionListener());
add(lbl);
add(setText,BorderLayout.SOUTH);
}
private ActionListener getActionListener() {
return new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("wait...");
Timer t = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("");
setState(JFrame.ICONIFIED);
}
});
t.setRepeats(false);
t.start();
}
};
}
public static void main(String args[]) {
new TestFrame();
}
}
When dealing with Swing components you shuld not use threads like that. Launch your own SwingWorker instead.
public class MySwingWorker extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
//your code here
//dont forget to repaint changed component or validate parent of it,
//if your text dont shows up.
return null;
}
}
you can also execute your own runnable via SwingUtilites
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//again your code here...
}
});
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.
I have a window with a MenuItem "maddbound3" with the following ActionListener:
maddbound3.addActionListener
(
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
menu_addbound3();
}
}
);
When the menu is clicked this listener calls menu_addbound3() below:
void menu_addbound3()
{
while(getEditMode() != EditMode.NONE)
{
System.out.println("!... " + getEditMode());
synchronized(this)
{
try
{
wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
A MouseClicked event alters the value of the edit mode and issues a notifyAll() so that the while loop should exit. However, tests have shown that when the system is running through the while loop, the MouseClicked event never occurs on clicking the mouse.
Does the ActionListener block the MouseClicked event? How can I resolve this issue?
Thanks
Don't have a while(true) on the Swing event thread, and likewise don't call wait() on the Swing event thread -- you'll freeze the whole GUI making it completely unresponsive. You need to understand that the main Swing event thread or "event dispatch thread" is responsible for all Swing drawing and user interaction, and so if you tie it up with long-running or freezing code, you lock your entire GUI.
Instead, change the state of your program -- perhaps by setting a variable or two, and have the behavior of your program depend on this state. If you need more specific advice, please tell us what behavior you're trying to achieve, and we can perhaps give you a better way of doing it.
For more on the Swing event thread, please read: Lesson: Concurrency in Swing
Edit
You state:
When the user clicks the menu item I want to obtain information via a series of "discrete" mouse clicks from the window. Hence, on clicking the menu, the user would be prompted to "select a point in the window". So, what I need is for my ActionListener function (menu_addbound3) to then wait for a mouse click. Hence the wait/notify setup. A mouse click changes the edit_mode and notifyAll() causes the wait in the while loop to exit which then causes the while loop to exit and I can then prompt for my next bit of information within the menu_addbound3 function, repeating this as as I need to.
Thanks for the clarification, and now I can definitely tell you that you are doing it wrong, that you most definitely do not want to use the while loop or wait or notify. There are many ways to solve this issue, one could be to use some boolean or enum variables to give the program a state and then alter its behavior depending on the state. Your EditMode enum can be used in the MouseListener to let it know that its active, and then you could also give the MouseListener class a boolean variable windowPointSelected, set to false, and then only set it true after the first click has been made.
Edit 2
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class ProgState extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final Color EDIT_COLOR = Color.red;
private EditMode editMode = EditMode.NONE;
private boolean firstPointSelected = false;
private JMenuBar jMenuBar = new JMenuBar();
private JTextField firstPointField = new JTextField(15);
private JTextField secondPointField = new JTextField(15);
public ProgState() {
add(firstPointField);
add(secondPointField);
JMenu menu = new JMenu("Menu");
menu.add(new JMenuItem(new AbstractAction("Edit") {
#Override
public void actionPerformed(ActionEvent arg0) {
setEditMode(EditMode.EDITING);
setFirstPointSelected(false);
}
}));
jMenuBar.add(menu);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mEvt) {
if (getEditMode() == EditMode.EDITING) {
Point p = mEvt.getPoint();
String pStr = String.format("[%d, %d]", p.x, p.y);
if (!isFirstPointSelected()) {
firstPointField.setText(pStr);
setFirstPointSelected(true);
} else {
secondPointField.setText(pStr);
setEditMode(EditMode.NONE);
}
}
}
});
}
public void setEditMode(EditMode editMode) {
this.editMode = editMode;
Color c = editMode == EditMode.NONE ? null : EDIT_COLOR;
setBackground(c);
}
public EditMode getEditMode() {
return editMode;
}
public void setFirstPointSelected(boolean firstPointSelected) {
this.firstPointSelected = firstPointSelected;
}
public boolean isFirstPointSelected() {
return firstPointSelected;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public JMenuBar getJMenuBar() {
return jMenuBar;
}
private static void createAndShowGui() {
ProgState progState = new ProgState();
JFrame frame = new JFrame("EditMode");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(progState);
frame.setJMenuBar(progState.getJMenuBar());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum EditMode {
NONE, EDITING
}
From the discussion it seems that having your class assume a number of states is the best way to proceed. We can achieve this by one or more enum variables. The reason I found this so hard to grasp initially is that I couldn't see the benefit of having all of ones code in the MouseClicked function. This is ugly and unmanageable at best.
However, using multiple enums and splitting processing into a number of external functions, we do indeed achieve a nice system for what we want.