I'm trying to paint a Welcome Screen for my game, but only when the game loads. I don't want it to repaint everytime during the game.
So I did this (where isStart is instantiated as true):
public myClass(String name){
setSize(800, 800);
setVisible(true);
setResizable(false);
runGame()
}
public void paint(Graphics g) {
if(nowStarting)
g.drawImage(WelcomeGameScreen, 0, 0, null);
isStart = false;
}
The problem is that the image will pop up for a second and then disappear? Oddly, it works when I leave out the if statement/isStart condition. What's wrong with this?
I am guessing that you have not copied verbatim the code, and there is an error in your code above. If your code is what I think it is...
public void paint(Graphics g) {
if(isStart)
g.drawImage(WelcomeGameScreen, 0, 0, null);
isStart = false;
}
Then at start it will draw your splash screen. But, because you are then setting isStart to false, the next time paint is called, the image will no longer be drawn. The paint method is called whenever the OS tells the screen that it needs to be refreshed (and when you force it with repaint).
The way you can get around this, is to set isStart to false in your application when the game has finished loading, and then call repaint.
I guess your newStarting boolean gets changed to false as soon as the panel is painted.
The reason it gets disappeared immediately is because of the repaints that are triggered by the Swing framework. Plus you have written the code for Welcome screen inside the overridden paint() method.
Refer this link for a detailed explanation of how to fire a splash window.
You also have a SplashScreen class in Java 1.6
Related
After calling a method with these two lines of code:
final Graphics canvasGraphics = screenCanvas.getGraphics();
canvasGraphics.fill3DRect(rectangleX, rectangleY, 500, 100, true);
The rectangle flashes when the program runs, and then disappears. However, when I put this same line in an anonymous inner class with my MouseListener:
screenCanvas.addMouseListener(new java.awt.event.MouseAdapter(){
public void mousePressed(MouseEvent event){
canvasGraphics.fill3DRect(rectangleX, rectangleY, 500, 100, true);
}
}
The rectangle stays there. What is causing this behavior?
first you have to know that the Graphics of a JComponent is changed (repainted) on every change (resizing, minimizing, moving ...), and this happen inside the method public void paintComponent(Graphics g).
in the first scenario the two lines of code are executed just once and they have an effect (you noticed the flash), but the the Component is painted very often on every change, so when launching your application the canvasGraphics is filled by the two lines of code, but just after that the method paintComponent is called , so it repaints the Graphics again and you changes are lost.
in the second scenario you are not drawing on the Graphics directly, the Graphics is changed once the mouse is clicked so the paintComponent get executed when the program launches and the window is shown, once the mouse is Pressed inside the Canvas your reimplemented method is called and the Graphics is changed but this time the paintComponent method will not ommit your changes because nothing is happening to the element.
a solution is to reimplement the paintComponent method on the wanted element to be sure that your changes is performed each time the elements get repainted.
Hello I i'm having random flicker every couple of seconds when running my program, it has a single image moving across screen. I'm using Graphics paint() method and repaint() in the thread's run() method. Here are the relevant parts of the code, i'll post entire code if necessary. Btw, pawns is an arraylist loaded with pawn objects, originally I had 5 threads for 5 images moving across but I tried with only 1 image and it still flickers so it's not that.
private BufferedImage helicopter;
helicopter = ImageIO.read(new File("white.png"));
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(count=0; count<pawns.size(); count++){
g.drawImage(helicopter, pawns.get(count).getX(), pawns.get(count).getY(), null);
}
g.setColor(Color.BLACK);
g.drawLine(350, 0, 350, 600);
}
public void run() {
while(true) {
randSleep = (int)(Math.random()*100);
randMove = (int)(Math.random()*2);
pawn.setX(pawn.getX()+randMove);
try{
Thread.sleep(40);
}
catch(Exception e) {
e.printStackTrace();
}
repaint();
}
}
On components with complex output, repaint() should be invoked with
arguments which define only the rectangle that needs updating, rather
than the no-arg version, which causes the entire component to be
repainted.
Swing's implementation of paint() factors the call into 3 separate
callbacks: paintComponent() paintBorder() paintChildren() Extensions
of Swing components which wish to implement their own paint code
should place this code within the scope of the paintComponent() method
( not within paint()).
source: Painting in AWT and Swing: Good Painting Code Is the Key to App Performance
You should notice in the source quoted and linked that repaint() (no arguments) will call update(), which by default clears the background before drawing. I suspect that this is the source of the flicker, when the component is cleared before calling paint().
If you are using Swing components you should not implement your own double buffering but instead use the functionality provided by Swing.
First try, calling a repaint with arguments to avoid update clearing the entire background. Or write an override for the update method. If that does not solve the problem next try putting your drawing code in the paintComponent method of a Swing component.
The following code just brings up a canvas in a window which just fills its content with red. However, when resizing the window it flickers a lot, because before each repaint the canvas appears to clear itself. After a bit of trivial searching it seemed to be because the update() method called g.clearRect(), but I've overriden that now and the flicker remains there, the canvas is still clearing itself before repaint.
I've played around with double buffering and that as far as I can tell that doesn't seem to fix things - I'm not sure how helpful it'd be anyway since the issue appears to be more with preventing the canvas from clearing before its repaint.
As an aside before everyone rushes in and suggests it, I have to use Canvas in this instance, not JPanel, because at a different point in time I'm using the same Canvas for native video playing with VLCJ.
public class MyCanvas extends Canvas {
#Override
public void update(Graphics g) {
paint(g);
}
#Override
public void paint(Graphics g) {
//By the time we get here, the canvas has been cleared to its background colour
g.setColor(Color.RED);
g.fillRect(0, 0, getWidth(), getHeight());
}
public static void main(String[] args) {
JFrame frame = new JFrame();
MyCanvas c = new MyCanvas();
frame.add(c);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Double buffering, as expected, turned out not to be the issue - either way the canvas was cleared before painting from code that seemed to be deep in the internals of the AWT library.
After tracing through the relevant internals, it seems that the "clear before paint" behaviour can be overridden by a property:
System.setProperty("sun.awt.noerasebackground", "true");
Adding the following stopped the background from being erased on the canvas before repainting, and therefore the associated flickering.
It should be noted this property is obviously Sun VM specific, so no idea if you get similar behaviour or not on another VM. It does however work perfectly for my use case.
I have the following code being called:
while(true){
view.onTick();
trySleep(55);
}
The onTick() method is described as such:
public void onTick() {
mainFrame.paintAll(mainFrame.getGraphics());
}
Here is where I set up my JFrame and JPanels etc (mainFrame is a JFrame):
private void runProgramSetup(){
JPanel canvas = new JPanel();
canvas.setLayout(new BoxLayout(canvas, BoxLayout.Y_AXIS));
mainFrame.getContentPane().add(canvas);
//create the main game panel
mapPanel = new MapPanel(model, this);
mapPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MAP_PANEL_HEIGHT));
mapPanel.setBackground(Color.cyan);
//create the menu panel
menuPanel = new MenuPanel(model, this);
menuPanel.setLayout(new BoxLayout(menuPanel, 0));
menuPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MENUS_PANEL_HEIGHT));
//add the panels to the window
canvas.add(mapPanel);
canvas.add(menuPanel);
//make both panels visible
mapPanel.setVisible(true);
menuPanel.setVisible(true);
}
Now here is my problem. Everything repaints when repaintAll() is called EXCEPT mapPanel's overridden paint(Graphics g) method:
#Override
public void paint(Graphics g) {
transformedImages.transformAndStoreImages(model);
paintGrid(g);
paintScenery(g);
paintElements(g);
paintDraggedElement(g);
paintUIOverlay(g);
}
It is only called once. That is it. However, every other component continues to repaint. It is only mapPanel that paints once. Here is what is even more strange. I am running on Ubuntu and get this problem. The rest of my team is running on Macs and they do not have this problem. The only way I have been able to solve this is to replace onTick() with two paint calls:
public void onTick() {
mainFrame.repaint();
mainFrame.paintAll(mainFrame.getGraphics());
}
This is all that has worked for me. I need both calls. Neither works alone. I don't like doing this though obviously because of inefficiency.. :/
Any ideas?
Thanks!
you should be overriding JPanel's
paintComponent(Graphics g)
not paint
The reason mainFrame.repaint() forces the map to refresh is because repaint() calls repaint(0, 0, 0, width, height), which marks the entire mainFrame's area to be marked as "dirty" for the RepaintManager. This is designed this way on purpose, because you usually do not want to repaint every pixel in the JFrame just to update a component. So, in onTick(), when mainFrame.paintAll() is being called, my guess is that the mapPanel's area has not been marked dirty, so the RepaintManager skips it, to save processing time. If you are very sure that you want to repaint the whole mapPanel every time onTick() is called, the simplest way would be to call mapPanel.repaint() inside of your onTick() method. This will mark the whole mapPanel as dirty so it will be redrawn asap. Also, if your menuPanel is just using regular swing JComponents, you don't need to manually cause them to repaint, they will be repainted when their values change, if you are using the API correctly.
I know this is a kind of old question, but I figured I'd answer in case anyone else runs into something similar.
A mouse listener calls repaint() and I can see that it does actually go past the drawing part because I see globalcounter incremented in the output of System.out.println(). On the screen, however, I don't see any change until I minimize the window and maximize again, resize it, or move it out of the visible screen area and back in. Obviously I'd like it to update without my intervention.
class GUI extends javax.swing.JFrame {
int globalcounter=0;
class MyCanvas extends Canvas {
#Override
public void paint(Graphics g) {
globalcounter++;
g.drawString(globalcounter,100,100);
System.out.println(globalcounter);
}
}
}
(Originally I was loading an image from a file that got constantly updated (webcam) and painting it on the canvas. When I dragged it partly out of the visible screen area and back in, the part that has been 'outside' was refreshed, the rest not.)
revalidate() instead of repaint() didn't change anything.
I know this post is a duplicate to Java repaint not working correctly but when I posted it there it was deleted.
Why are you adding an AWT component, Canvas, to a Swing component, JFrame? You should stick with Swing components only. And also do you know the size of your MyCanvas, and how have you added it to the JFrame as you don't show this code.
Consider
using a JPanel instead of a Canvas object,
drawing in its paintComponent method,
showing us an sscce if you're still stuck.
And also, if all you're doing is drawing text, use a JLabel rather than drawing in paint/paintComponent, and change its text with its setText(...) method.