I'm making a simple Java applet that displays a traffic light. If there are no keys being pressed the background is white. If you hit the "1", "2", or "3" keys on the numpad then the traffic light should change colors to respectfully: red, green, and yellow. It is not working because when I press any keys, nothing happens.
all isX booleans are initialized to false except for isReleased.
#Override
public void keyPressed(KeyEvent e) {
isReleased = false;
switch(e.getKeyCode()){
case KeyEvent.VK_NUMPAD1:
isRed=true;
break;
case KeyEvent.VK_NUMPAD2:
isGreen=true;
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
isReleased = true;
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(trafficLight, 0, 0, null);
if(isReleased==true){
g.drawImage(blank, 0, 0, this);
}else{
if(isRed==true){
g.drawImage(red, 0, 0, this);
}
if(isGreen==true){
g.drawImage(green, 0, 0, this);
}
}
}
NOTE
(updated) My full code can be seen here: http://pastebin.com/8ZNQUWJy
Let's ignore that fact that you code is incomplete, so it's impossible to know what you are extending from or if you've actually added a KeyListener.
KeyListener is notorious for being fickle about it's focus state. Basically, this means, KeyListener will only respond to keystrokes if the component it is registered to is focusable AND has keyboard focus...
A better solution would be to use the Key Bindings API, which provides better control over the focus level that will trigger key events.
You are also breaking the paint chain. Painting is a complex series of method calls, chaining together to produce the final result. By failing to call super.paint, you've broken this chain, introducing the possibility of paint artifacts.
You should also avoid overriding paint generally, but especially of top level containers, like windows, as they are not double buffered. It is recommended that instead, you extend from something JPanel or JComponent and override it's paintComponent method instead.
Take a look at Performing Custom Painting for more details.
Updated based on seeing full source
Avoid AWT based components (like Applet) the API is out of date and not many people use it any more, instead you should consider using a JApplet instead. But personally, I would avoid applets until you have a better understanding of the overall API.
Don't call setSize within an applet, the size is determined by the html tag which describes the applet to the browser
Your run method is pointless as it doesn't actually do anything useful and may actually be causing your application to "hang"
Your update method is only painting the trafficLight BufferedImage, but because you fail to call super.update, nothing else is painted. It would be better to grid rid of it and do your painting in the paint method, but make sure you are calling super.paint. But as I already said, you'd be better of using something like JPanel and overriding it's paintComponent method.
Take a look at Creating a GUI with Swing for more details
You may also find Why CS teachers should stop teaching Java applets of some worth...
Related
so i am dabbling in making a game and i am a fairly messy worker and then i go back and tidy it up. So please ignore any stupid naming conventions or methods or anything. As I go back later and tidy up.
So basically I am trying to make a main menu, i just want to draw something to the screen and have it stay there. I am writing the graphics to a image ATM and displaying the image as i tried just drawing to the canvas and it just flickered up onto the screen and then it went blank. I changed it to a runnable so i could put in sleep() to try and error hunt. I've removed all my error hunting code now. It turns out when i have a sleep() before or after the image is put on the screen for longer than 50ms the rectangle stays on screen and doesn't flicker away like 1ms after it was drawn.
I'm not sure what's going on. I am probably not that clear and I apologies.
import java.awt.Canvas;
import java.awt.Graphics;
import javax.swing.JFrame;
public class test extends Thread{
public static void main(String[] args){
test t = new test();
t.start();
}
#Override
public void run() {
JFrame f = new JFrame("Test");
Canvas c = new Canvas();
Graphics g;
c.setSize(400, 400);
f.add(c);
f.pack();
f.setVisible(true);
c.setVisible(true);
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g = c.getGraphics();
g.drawRect(10, 10, 40, 68);
}
}
Basically, this is not how painting in AWT works. Painting should be done within the context of a component's paint method. In AWT Canvas seems to be the preferred base component to use.
You should never use getGraphics, apart from been able to return null, it will only represent the Graphics context from the last paint cycle, anything added to it could be wiped about by newer paint events...which is probably happening to you.
Take a look at Painting in AWT and Swing for details about how painting works.
Having said all that, I would discourage you from using AWT based API as it was replaced by Swing some 15 years ago.
Take a look at Performing Custom Painting for more details about painting in Swing
You're kind of getting the idea, but there are a few standard design patterns of creating a game, for starters check out this code.
Basically, you'll need something called a game loop which will update and render to the display a certain amount of times every second (framerate or fps). When it renders, it will clear and redraw the image in a new location at a certain interval, if your fps was 60 you would be rendering (clearing, and redrawing) 60 times a second. We can also introduce more advanced concepts such as buffer strategies (shown in the example above) to reduce tearing and flickering.
To sum it up, you're kind of there, you just need to constantly do that g = c.getGraphics() so...
while (true) {
g = c.getGraphics();
// set color
// draw a rectangle
g.dispose();
}
Note how I added the dispose, this will just free up any unused memory. Just to clarify, this is by no means good code, but it will give you a place to start :)
The flickering is due to only having 1 screen, to stop this flickering you need a buffer strategy. Basically having two buffers is like having two screens, whilst you render on one 'screen' you can clear and draw the next step on the second screen and switch between the two buffers; this reduces the flickering.
-Hi all! I'm making a Java applet that simulates wave interference, which I have almost finished (will license under GPL). However, I have two questions regarding the AWT paint cycle that I am having difficulty finding answers to.
I want to make an 'about' overlay that appears when I press a button. The way I want to do this is to draw over the entire applet window with my static message and legend objects. The problem is stopping the AWT components from drawing themselves in the foreground without using remove(). Is there a way I can stop AWT from drawing itself temporarily?
For my standing waves mode I want to have node and anti-node markers calculated and drawn to a secondary graphics every time the standing wave reaches a maximum amplitude (all of which I can do myself), but drawn to the primary graphics (and thus displayed) every paint cycle. Could someone explain the steps to do so? I imagine it would involve creating a second graphics object, drawing to it once, then drawing it to the primary graphics every cycle.
If you are able to answer either of my questions I would be very grateful!
Cheers, Jack Allison
Responding to your first question:
You can't disable the paint()/paintComponent() method if you've inlcluded it in your code. If its there, it runs. However, you can create a flag so that only if the flag is true, the stuff gets drawn. Let me show you what I mean:
boolean flag;
...
public void paintComponent(Graphics comp) {
if (flag) {
Graphics2D comp2D = (Graphics2D) comp;
//drawing statements
}
}
public void actionPerformed(ActionEvent event) {
flag = true;
repaint();
}
I'm really confused with the program flow for how the paintComponent function works within my JPanel. Ideally I'd like to have access to the Graphics object to draw stuff from other functions based on my program flow. I'm thinking along the lines of the following:
private Graphics myG;
public void paintComponent(Graphics g) {
super.paintComponent(g);
myG = g; //I want a graphics object that I can just draw with.
//Should I just initialize to myG = new Graphics(); or something?
//private Graphics myG = new Graphics(); does not seem to work
}
//this is what's getting called, where I want to call other functions that
//can use myG to draw graphics as I please
public void startPanelView() {
myG.drawRect(350, 20, 400, 300); //doesn't work
otherFunctionForGraphics(); //which will also use myG.
}
I hope I've made myself clear here. I just want to be able to do use the Graphics class as I please. Currently I can only do stuff like g.drawRect(...) inside of the paintComponent() function. This could work for my purposes but I'd like more flexibility.
Thanks.
EDIT - Alright, I understand that I should not try and reference a Graphics object outside. But how should I go about separating application logic from the paintComponent function? Right now this class is looking a little messy because I have the following:
public void paintComponent(Graphics g) {
if (flag1 == true) {
//do some graphics stuff, that I would prefer to be in another function
}
if (flag2 == true) {
//do some other graphics stuff, again, which I would prefer
//to be in another function
}
//... etc. More of these cases.
}
And basically the paintComponent function is getting stupidly long and complicated for me, so I would like to break it up in whatever ways possible.
Call repaint from your startPanelView method on the panel object. You should never act on the graphics object outside of the paint methods, nor should you maintain a reference to the graphics object for use outside of the paint method.
The others are right that you cannot keep the Graphics object as a member variable, but if the problem is that your paintComponent method is getting too long, you can still pass the Graphics Swing gives you as an argument to other methods:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(350, 20, 400, 300);
doSomeStuff(g);
if (flag1) {
doMoreStuff(g);
} else {
doDifferentStuff(g);
}
}
I'd like to have access to the Graphics object to draw stuff from other functions based on my program flow. I'm thinking along the lines of the following:
No, you shouldn't do that in a standard Swing application as the Graphics object obtained will not persist and your drawings will disappear whenever the JVM or the operating system decide that a repaint is necessary. Consider creating Lists or other collections of objects to be drawn (such as from classes that implement Shape) and iterating through these collections in the paintComponent method. Another technique is to draw on a BufferedImage and then display it in your paintComponent method.
basic JComponent for Painting/Custom Painting/2D Graphics in Swing or for Image/ImageIcon too, is JLabel,
don't call another viod(s) or class(es) from 2D Graphics or CustomPainting, some valuable examples are here or here, search on this forum for excelent suggestions about Custom Painting and 2D Graphics
I have a question. Recently I was looking into ways to implement hardware rendering using Java. My goal was not to use an external library such as OpenGL. I found a post on a website that detailed how to do so.
This is what the code was (I renamed some items):
#Override
public void paint(Graphics g) {
createVolatileImage();
do {
GraphicsConfiguration gc = getGraphicsConfiguration();
Graphics offscreenGraphics = volatileImage.getGraphics();
int validationCode = volatileImage.validate(gc);
if (validationCode == VolatileImage.IMAGE_INCOMPATIBLE) {
createVolatileImage();
}
offscreenGraphics.setColor(getBackground());
offscreenGraphics.fillRect(0, 0, getSize().width, getSize().height);
offscreenGraphics.setColor(getForeground());
paint(offscreenGraphics);
g.drawImage(volatileImage, 0, 0, this);
} while (volatileImage.contentsLost());
}
private void createVolatileImage() {
GraphicsConfiguration gc = getGraphicsConfiguration();
volatileImage = gc.createCompatibleVolatileImage(getWidth(), getHeight());
}
Unfortunately, if I resize the window - the paint ( Graphics ) method (in the class Canvas) gets called like 1,000 times within a second, causing an OutOfMemoryException.
Has anyone encountered this before?
Thanks a lot in advance!
The reason you are getting an OutOfMemoryException is because you never clean up your VolatileImage. The way I see it, you are allocating a new VolatileImage every time paint() is called, which can happen many hundreds (or in your case over a thousand) times per second. Unless you free the memory used by the VolatileImage or fix things so that you make the allocation once instead of once per frame, your application's memory space will balloon until you crash the JVM. Try adding a call to offscreengraphics.dispose() at the end of your rendering loop. Also read the Javadoc.
EDIT:
Another useful reference.
I really don't think you should have a loop in your paint method. It should paint a single time for each action that needs a paint, then return.When you drag the screen, the OS/env will take care of dispatching repaint messages over and over to your app. You should repaint if possible then just return. No loops checking if content is lost, the OS will tell you when to do your painting.
As discussed in Painting in AWT and Swing, this is the expected behavior when painting in AWT. In particular, a system-triggered painting operation occurs when a component is resized. As #rjacks notes, you need to dispose() any resources created in paint().
I found a rendering bug in some code and have found a workaround, but I would like to know why I am getting different behaviour. In the old code, the background would (sometimes) be rendered as white, despite while debugging getBackground() would return the correct colour.
Old code:
#Override
public void paint(Graphics g) {
// Stuff
g.setColor(getBackground());
g.clearRect(0, 0, width, height); // Obviously wrong.
// More stuff
}
New code:
#Override
public void paint(Graphics g) {
// Stuff
g.setColor(getBackground());
g.drawRect(0, 0, width, height); // Correct usage with 'setColor' call.
// More stuff
}
As I put in the code, it is obvious that setColor(getBackground()) has no effect on the clearRect(...) call. Yet I would assume that calling clearRect(...) and calling setColor(getBackground()) followed by drawRect(...) would be semantically the same.
I have also considered the opaqueness property, but the parent lightweight components and ancestor heavyweight component all use the same background colour, and it is quite obvious that this component is the one with the incorrect behaviour (it is one of 8 of the same type of component owned by its parent - yet only the ones that get to this section of code have a problem).
I am using JDK 1.6.0_07 (for business reasons of course) if that helps.
Here's the information from the JavaDocs -
Clears the specified rectangle by filling it with the background color of the current drawing surface. This operation does not use the current paint mode.
Beginning with Java 1.1, the background color of offscreen images may be system dependent. Applications should use setColor followed by fillRect to ensure that an offscreen image is cleared to a specific color.
As this implies, clearRect is system dependent and the value of getBackground() is not taken into account.
The difference is this :
if you use the graphics method fillRect() you can't erase the color by using drawRect() over the same recatngle specified in pixels.
but if you use the graphics method fillRect() then clear it with clearRect() and after that drawRect(); you reach a satisfaction and a conclusion.