I’m completely stuck on a problem i’m having with this program where I have to draw a city with swing. Basically what i’m trying to do is make it so that the windows don’t change every frame. I’ve tried just about everything I can think of and nothing has worked yet.
Here is the main class that draws everything
import java.applet.Applet;
import java.awt.*;
import java.util.*;
import javax.swing.JFrame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class Skyline extends JFrame implements MouseMotionListener
{
private int mX, mY; //Mouse cooddinates
private Image mImage; //Image buffer
//private Image mImage2; //Image buffer
private int num = 0;
private Building bldg1 = new Building(305, 110, 30);
private Building bldg2 = new Building(380, 125, 170);
private Building bldg3 = new Building(245, 200, 325);
private Building bldg4 = new Building(470, 170, 555);
private Building bldg5 = new Building(395, 200, 755);
private Background bg = new Background();
public void init ()
{
}
public static void main(String []args)
{
Skyline f = new Skyline();
f.setSize(1017, 661); //Sets size of window
f.setTitle("Skyline"); //Sets title of window
f.show();
}
public void paintOffscreen(Graphics page)
{
//Draws the background
bg.draw(page);
//Moving square
num++;
if (num > 1200)
num = 0;
page.setColor(Color.yellow);
page.fillRect(num,100,100,100);
//Draws the buildings
bldg1.draw(page);
bldg2.draw(page);
bldg3.draw(page);
bldg4.draw(page);
bldg5.draw(page);
//Mouse move square
int s = 100;
page.setColor(Color.yellow);
page.fillRect(mX - s / 2, mY - s / 2, s, s);
repaint();
}
//====================================BUFFER CODE========================================
public void paint(Graphics g)
{
//Clear the buffer
Dimension d = getSize();
checkOffscreenImage();
Graphics offG = mImage.getGraphics();
offG.setColor(getBackground());
offG.fillRect(0, 0, d.width, d.height);
//Save frame to buffer
paintOffscreen(mImage.getGraphics());
//Draw the buffer
g.drawImage(mImage, 0, 0, null);
}
private void checkOffscreenImage()
{
Dimension d = getSize();
if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height)
mImage = createImage(d.width, d.height);
}
//=======================================================================================
//==================================MOUSE MOVE CODE======================================
public Skyline()
{
addMouseMotionListener(this);
setVisible(true);
}
public void mouseMoved(MouseEvent me)
{
Graphics g = getGraphics();
mX = (int) me.getPoint().getX();
mY = (int) me.getPoint().getY();
update(g);
//repaint();
}
public void mouseDragged(MouseEvent me)
{
mouseMoved(me);
}
//=======================================================================================
}
And here is the window class that might be able to be fixed somehow.
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
import javax.swing.JFrame;
public class Windows extends JFrame
{
private Random gen = new Random();
private int height, width, locX;
private int onOff = 0;
public Windows()
{
height = 305;
width = 110;
locX = 30;
}
public Windows(int height, int width, int locX)
{
this.height = height;
this.width= width;
this.locX = locX;
}
public void draw(Graphics page)
{
page.setColor (Color.darkGray);
page.fillRect (locX, 550 - height, width, height);
for (int i = 550 - height + 5; i < 550; i += 15)
{
for (int x = locX + 5; x < locX + width; x += 15)
{
onOff = gen.nextInt(2);
if(onOff == 0)
page.setColor(Color.black);
else
page.setColor(Color.yellow);
page.fillRect (x,i,10,10);
}
}
}
}
Heres the building class just in case.
import java.applet.Applet;
import java.awt.*;
import javax.swing.JFrame;
public class Building extends JFrame
{
private int height, width, locX;
private int onOff;
private Windows windows1;// = new Windows(height, width, locX);
public Building()
{
height = 305;
width = 110;
locX = 30;
windows1 = new Windows(height, width, locX);
}
public Building(int height, int width, int locX)
{
this.width = width;
this.height = height;
this.locX = locX;
windows1 = new Windows(height, width, locX);
}
public void draw(Graphics page)
{
page.setColor (Color.darkGray);
page.fillRect (locX, 550 - height, width, height);
windows1.draw(page);
}
}
And the bg class just to be safe
import java.applet.Applet;
import java.awt.*;
public class Background extends Applet
{
private int height, width;
public Background()
{
height = 400;
width = 2000;
}
public Background(int height, int width)
{
this.height = height;
this.width = width;
}
public void draw(Graphics page)
{
//Draws the sky
page.setColor(Color.cyan);
page.fillRect(0,0,2000,2000);
//Draws the grass
page.setColor (Color.green);
page.fillRect (0,500,width,height);
}
}
A number of things jump out at me immediately…
You’re trying to use a off screen buffer, but you’re recreating it each time you paint to the screen…
Additionally, the last call in the method is to
repaint. This is a bad idea. This could cause youpaintmethod to be recalled, again and again and again…You would be better of rendering the backing buffer only when it needs to change…
Now, this is going to raise some issues with invalidating the buffer. This can be achieved by overriding
invalidateand setting themImagetonullYou’re extending most of your components from
JFrame???Building,WindowandBackgrounddo no painting of there own (from the content of Swing), you are simply calling thedrawmethod. There is no need to extend fromJFrameorJApplet, they’re adding no benefit to your program and are simply confusing the issues.You should, only very rarely, need to override
painton a top level container likeJFrame. You are better off using something likeJPaneland override thepaintComponentmethod, if for no other reason, they (top level containers) aren’t double buffered.I would move the logic for
Skylineinto aJPaneland then add it to aJFramefor displaying – IMHOUPDATED
I’ve gone through the code and updated it to work the (basic) way I think it should and found a couple of other things along the way…
This this is a bad idea…
There should never be any need for you to call
update(Graphics), besides, theGraphicscontext you got is simply a snap shot of the last repaint. This will drastically slow you painting process any way, as it is repeatedly callingpaint.So, this is my take…
Basically, I moved the core rendering of the skyling to it’s own panel and used
JComponent#paintComponentto render the skyline.I employed a
SwingWorkerto off load the rendering of the backing buffer to another thread, allowing the UI to remain responsive while the backing buffer was rendered.