my application looks like that, i am getting a null pointer exception at the draw() method, to be exact at g.drawImage(img, 0, 0, null)
package com.ochs.game;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements Runnable{
private static final long serialVersionUID = 8229934361462702491L;
public static final int WIDTH = 320;
public static final int HEIGHT = 240;
public static final int SCALE = 2;
public boolean isRunning;
private BufferedImage img;
private Graphics2D g2d;
public Game() {
setFocusable(true);
requestFocus();
start();
}
public void start() {
isRunning = true;
new Thread(this).start();
}
public void stop() {
isRunning = false;
}
public void run() {
long start;
init();
while(isRunning) {
start = System.currentTimeMillis();
update();
render();
draw();
try {
Thread.sleep(5 - (System.currentTimeMillis() - start));
} catch (Exception e) {
}
}
}
public void init() {
img = new BufferedImage(WIDTH*SCALE, HEIGHT*SCALE, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) img.getGraphics();
}
public void update() {
}
public void render() {
}
public void draw() {
Graphics g = getGraphics();
g.drawImage(img, 0, 0, null); // <<<<< getting null pointer here!
}
public static void main(String[] args) {
Dimension size = new Dimension(WIDTH*SCALE, HEIGHT*SCALE);
Game gameComponent = new Game();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(size);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gameComponent);
}
}
Now my question is: why do i get a null pointer exception when trying to draw the bufferedimage called img? I also tried just outputting some string by using drawString() but this just gives myself a nullpointerexception, too.
does anyone has an advice?
You're likely trying to get the Graphics context via getGraphics() before the JPanel has been rendered, and thus the method returns null. Don't do this. There are problems with using getGraphics() on a component to get the Graphics context, one of which is the problem you're seeing above, and another is that the Graphics context obtained will not persist. There are occasions when this is necessary to do, but usually we do passive drawing via paintComponent(...). Often a Swing Timer can be used for the animation loop.
I think it's because you're trying to draw using getGraphics() instead of the conventional override of paintComponent. You want to use something like this: drawImage is not drawing (see the top answer).
the Component must first be visible
try this before you start the thread in Game/panel
frame.add(panel)
frame.setVisible(true)
then start the thread in Game/panel
getGraphics() method will return null if Component is not rendered till that statement and thus you will get NullPointerException, also if it is rendered Graphics will not be stable and better to use a paintComponents...
See also: Any alternative to calling getGraphics() which is returning null
import java.awt.*;
import java.awt.event.*;
class myframe extends Panel
{
public void paint(Graphics g)
{
g.setColor(Color.red);
g.fillRect(10,12,300,150);
}
public static void main(String args[])
{
Frame f=new Frame();
f.add(new myframe());
f.setSize(400,400);
f.setVisible(true);
}
}
Related
I was trying to copy a repaint() and a paintComponent() method from a tutorial. After I copied the two methods my paintComponent did not get called and so the rectangle is not being showed. Here is my code:
public class Main {
GameWindow gw;
Main() {
gw = new GameWindow();
}
void start() {
gw.setWindow();
}
public static void main(String[] args) {
new Main().start();
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameWindow extends JPanel implements Runnable {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
Thread animation;
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
void startAnimation() {
animation = new Thread(this);
animation.start();
}
#Override
public void run() {
while (animation != null) {
update();
repaint();
}
}
public void update() {
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
I already tried some solutions from stackOverflow, but they did not work or they were not relevant to my problem. Now the code above is what I tried myself using the video, but after using a println in the method I saw it was not getting called. I expected it to work after watching the tutorial, but it didn't. Does anyone know how I can fix this? Thanks in advance!
Swing is single threaded - never block the Event Dispatching Thread with long running or blocking operations
Swing is NOT thread safe - never update the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Swing makes use of a passive rendering engine - you don't control the painting process and you need to work within in it's intended design/workflow, see Painting in AWT and Swing and Performing Custom Painting for more details.
So, what's the solution? In it's simplest form, use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
private Timer timer;
#Override
public Dimension getPreferredSize() {
return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
public void update() {
System.out.println("Updatey update");
repaint();
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("Painty paint paint");
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
}
You should now see a bunch of text been printed to the console.
Also - you might not, I've made some structural changes to your code, there is not reason for a JPanel based class to create it's own window (or if you really wanted to do this, I'd create a static method to do it, but then I'd be questioning why).
Oh, and also what DontKnowMuchBut Getting Better said in the comments!
Here is the specific rectangle problem with your code, I will not mention other things because is not in the question, you are not adding GameWindow itself as component of your JFrame, and you are not calling your paintComponent method anywhere, I put it on update and it worked fine, but I'm afraid that your code will block and you will not able to move the window and interact with it since you are always calling update/repaint over and over again inside your while statement.
edit.: As the guy in the comment said, we must not call paintcomponent directly, so I fixed.
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.add(GameWindow.this);
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
I have tried to make a simple GUI in Java Using graphics2D and JFrame.
I have added a background-color on JFrame typing this.setBackground(new Color(54, 71, 99)) inside initWindow() method. It turned out that backBuffer was clearing that background and not repainting the line that causes this is in render() method, last line backBuffer.show().
How Do I make it NOT clear the main background?
package asteroids;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
public class Main extends Canvas implements KeyListener {
private boolean gameOver;
private BufferStrategy backBuffer;
private Dimension dimension = new Dimension(Config.WINDOW_WH[0], Config.WINDOW_WH[1]);
private List<Star> stars = new ArrayList<Star>();
private HashMap<Integer,Boolean> keyDownMap = new HashMap<Integer, Boolean>();
public Main() {
// Initialize Window
initWindow();
addKeyListener(this);
this.setBackground(new Color(54, 71, 99));
this.createBufferStrategy(2);
backBuffer = this.getBufferStrategy();
// init variables
gameOver = false;
// Generating stars
generateStars();
// Init loop
gameLoop();
}
public void initWindow(){
JFrame window = new JFrame("Asteroids");
setPreferredSize(dimension);
window.add(this);
window.pack();
window.setResizable(false);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBackground(new Color(54, 71, 99));
window.requestFocus();
}
public void update() {
if(keyDownMap.containsKey(KeyEvent.VK_ESCAPE)){
gameOver = false;
System.exit(0);
}
}
public void render(){
Graphics2D g = (Graphics2D) backBuffer.getDrawGraphics();
g.setColor(Color.WHITE);
for(Star s: stars) {
g.fillOval(s.posx - (s.width/2), s.posy - (s.height/2), s.width, s.height);
}
g.dispose();
backBuffer.show();
}
public void generateStars() {
for(int i = 0;i < 20;i++) {
int starX = new Random().nextInt(Config.WINDOW_WH[0]-10)+5;
int starY = new Random().nextInt(Config.WINDOW_WH[1]-10)+5;
stars.add(new Star(starX, starY));
}
}
public void gameLoop(){
while(!gameOver){
update();
render();
try{ Thread.sleep(20);}catch(Exception e){};
}
}
public static void main(String[] args) {
new Main();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
keyDownMap.put(e.getKeyCode(), true);
}
#Override
public void keyReleased(KeyEvent e) {
keyDownMap.remove(e.getKeyCode());
}
}
How Do I make it NOT clear the main background?
You can't. Apart from Canvas been non-transparent (which can't be changed), BufferStrategy also has more then one page onto which it paints it's content (thus allowing to perform page flipping). Combined, this would make it impossible to maintain the background of the parent container.
Instead, you should (in fact, you must) clear the Graphics context of the buffer you painting to do, every time render is called, otherwise you will be painting onto what ever was previously painted on to it.
One technique might be to generate a BufferedImage with the "static" content and simply paint that to the buffer first
I am unable to find any post that clearly demonstrates how to wrap Xuggler within a JPanel. I can see some posts for JFrame though, but the solution doesn't easily translate to JPanel. (Or maybe I am too naive!)
To be more specific, the following method does not work for JPanel as it does for JFrame -
private class MyVideoFrame extends JFrame
{
....
public void paint(Graphics g)
{
if (image != null)
{
g.drawImage(image, 0, 0, null);
}
}
}
Thanks!
The VideoImage source actually uses JComponent, which is not the best choice, but is the parent to JPanel.
The fact they've overridden paint and failed to call super.paint is a really bad example.
Instead, they should have used a JPanel (it's not transparent by default), overridden paintComponent and called super.paintComponent before rendering the frame.
So based on DecodeAndPlayAudioAndVideo or DecodeAndPlayVideo
You should be able to replace the reference to com.xuggle.xuggler.demos.VideoImage with the one below, which uses a JPanel as the main container for the vide frame container for displaing...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class VideoImage extends JFrame {
private final ImagePane mOnscreenPicture;
public VideoImage() {
super();
mOnscreenPicture = new ImagePane();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(mOnscreenPicture);
this.setVisible(true);
this.pack();
}
public void setImage(final BufferedImage aImage) {
mOnscreenPicture.setImage(aImage);
}
//....Inner class here....//
public class ImagePane extends JPanel {
private BufferedImage mImage;
public void setImage(BufferedImage image) {
SwingUtilities.invokeLater(new ImageRunnable(image));
}
#Override
public Dimension getPreferredSize() {
return mImage == null ? new Dimension(200, 200) : new Dimension(mImage.getWidth(), mImage.getHeight());
}
private class ImageRunnable implements Runnable {
private final BufferedImage newImage;
public ImageRunnable(BufferedImage newImage) {
super();
this.newImage = newImage;
}
#Override
public void run() {
ImagePane.this.mImage = newImage;
Dimension size = getPreferredSize();
final Dimension newSize = new Dimension(mImage.getWidth(), mImage.getHeight());
if (!newSize.equals(size)) {
VideoImage.this.invalidate();
VideoImage.this.revalidate();
VideoImage.this.pack();
repaint();
}
repaint();
}
}
public ImagePane() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (mImage != null) {
g.drawImage(mImage, 0, 0, this);
}
}
}
}
English is not my native language, sorry for any mistakes. I have to make a Bubble Shooter game in Java. I want to use images for the bubbles and I want the images to be picked randomly. I used Random and ImageIcon classes. My program doesn't show anything when I compile it and I don't know where the problem is. I'm a beginner in Java.
This is the code for my Game class:
import java.awt.Graphics;
import java.awt.Image;
import java.util.Vector;
import javax.swing.JPanel;
public class Game extends JPanel{
private static final long serialVersionUID = 1L;
//what the balls are like
public final static int START_BALLS=40;
public static Vector<Ball> balls = new Vector<Ball>();
private Image img;
private Graphics graphics;
public Game() {
for(int i=0; i<START_BALLS; i++) {
balls.add(new Ball());
}
}
public void paint(Graphics g) {
img = createImage(null);
graphics = img.getGraphics();
paintComponent(graphics);
g.drawImage(img, 0, 0, null);
repaint();
}
public void paintComponet(Graphics g) {
for(int i=0; i<balls.size(); i++) {
Ball b=(Ball)balls.get(i);
b.draw(g);
}
}
public static void main(String [] args) {
new Frame();
Game game = new Game();
new Game();
Window.window.add(game);
}
}
and the class for the bubbles:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.Random;
import javax.swing.ImageIcon;
public class Ball {
Random random = new Random();
final String[] image_paths = new String[] {"balls/peg_0.png",
"balls/peg_1.png","balls/peg_2.png","balls/peg_3.png",
"balls/peg_4.png","balls/peg_5.png"};
String randomBalls;
public Image image;
public Ball(){
randomBalls = image_paths[random.nextInt(image_paths.length)];
ImageIcon poza = new ImageIcon(randomBalls);
image=poza.getImage();
}
public void draw(Graphics g){
g.drawImage(image, 0, 0, null, null);
}
}
What is wrong with my program?
Look over my comments, look carefully at the comments in the code, see how I rearranged the organization of the classes.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.Vector;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.imageio.ImageIO;
import java.net.URL;
public class Game extends JPanel {
Random random = new Random();
final String[] image_path = new String[]{
"http://i.stack.imgur.com/gJmeJ.png",
"http://i.stack.imgur.com/IHARa.png",
"http://i.stack.imgur.com/wCF8S.png",
"http://i.stack.imgur.com/T5uTa.png"
};
private static final long serialVersionUID = 1L;
//what the balls are like
public final static int START_BALLS = 40;
public static Vector<Ball> balls = new Vector<Ball>();
private Image img;
// A Graphics instance is typically transient.
// There is rarely, if ever, a need to store them
//private Graphics graphics;
public Game() {
for (int i = 0; i < image_path.length; i++) {
balls.add(new Ball(image_path[i]));
}
//I have no idea what you were trying to achieve here, but it fails horribly
// img = createImage(null);
img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
// graphics = img.getGraphics();
ActionListener al = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
};
Timer timer = new Timer(400, al);
timer.start();
}
#Override // very handy!
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.drawImage(img, 0, 0, null); A JPanel IS A ImageObserver
g.drawImage(img, 0, 0, this);
Ball b = (Ball) balls.get(random.nextInt(4));
b.draw(g);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Game");
f.add(new Game());
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
class Ball {
String randomBalls;
public Image image;
public Ball(String url) {
try {
image = ImageIO.read(new URL(url));
} catch (Exception e) {
e.printStackTrace();
}
}
public void draw(Graphics g) {
g.drawImage(image, 0, 0, null, null);
}
}
Tips
Exception in thread "AWT-EventQueue-0" java.lang.UnsupportedOperationException: getGraphics() not valid for images created with createImage(producer) at img = createImage(null); I have no idea what you thought that code statement does, but ..nothing good.
When doing custom painting in a JPanel, we should override only paintComponent(Graphics) and leave the paint(Graphics) method as it is. When overriding the former, immediately call the super method.
Adding a call to repaint() inside paint(Graphics) will cause an infinite loop.. If the code needs to loop, establish a Swing Timer to call repaint()
paintComponet(Graphics g) should be paintComponent(Graphics g) Use #Override notation when appropriate. It would have warned you of the incorrectly spelled method name.
More general tips
For better help sooner, post an MCVE.
One way to get image(s) for an example is to hot-link to the images seen in this answer.
By the time of deployment, those images will likely become an embedded-resource. That being the case, they must be accessed by URL instead of File. See the info page for the tag, for a way to form an URL.
I looked and the codes seems fine to me. Got an error but hopefully it's the source code, not something wrong with the cpu I have nor JDK.
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.*;
import javax.swing.JFrame;
public class Game extends Canvas implements Runnable {
public static int width = 300;
public static int height = width / 16*9;
public static int scale = 3;
private Thread thread;
private boolean running = false;
private JFrame frame;
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try{
thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
}
public void run(){
while(running){
tick();
render();
}
}
public void tick() {
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if(bs==null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
bs.dispose();
bs.show();
}
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
frame = new JFrame();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("Title");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
Then I got this error, even when I countlessly modified the source code I had.
Exception in thread "Display" java.lang.NullPointerException
at java.awt.Component$BltBufferStrategy.showSubRegion(Component.java:4307)
at java.awt.Component$BltBufferStrategy.show(Component.java:4255)
at com.thecherno.Rain.Game.render(Game.java:58)
at com.thecherno.Rain.Game.run(Game.java:39)
at java.lang.Thread.run(Thread.java:695)
Im starting to seem if it because of an outdated JDK. Current Version I have is JDK 6.
You state:
What Im trying to do is change color as seen in the render method. The background to be black.
Use Swing components such as a JComponent or JPanel.
Simply call setBackground(Color.BLACK) on the component will do.
You appear to be creating a game loop of some type. Consider using a Swing Timer for this.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Game2 extends JPanel {
private static final int PREF_W = 300;
private static final int PREF_H = PREF_W / 16 * 9;
private static final int SCALE = 3;
private static final Color BACKGROUND = Color.BLACK;
private static final int TIMER_DELAY = 20;
private Timer swingTimer;
public Game2() {
setBackground(BACKGROUND);
swingTimer = new Timer(TIMER_DELAY, new TimerListener());
swingTimer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add any custom painting here
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W * SCALE, PREF_H * SCALE);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// TODO add code that gets called in game loop
}
}
private static void createAndShowGui() {
Game2 mainPanel = new Game2();
JFrame frame = new JFrame("Game2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that this code is based on your stated requirements and what I'm guessing are other requirements based on your code. If there are further requirements not mentioned, please elaborate them for us.
Try using g.dispose(); followed by bs.show(); and then
g = (Graphics2D)bs.getDrawGraphics();. I know it looks weird, but you are emptying the canvas and then refilling it using your strategy. You may also need to do an initial check for g being null and initialize it before the first display loop.