Java flickering JPanel

300 Views Asked by At

I have a problem related to flickering in JPanel. I don't know why but balls in the window flicker from time to time. I tried several things, like double-buffering, BufferStrategy, Canvas but none of them worked. The main idea is to use thread pools and when a ball has bound several times delete it and create another thread. There must many balls at the same time.

Here is my code:

public class BallWindow extends JFrame {

private static final long serialVersionUID = 1L;
private BallPanel panel;
private final int WINDOW_WIDTH = 700;
private final int WINDOW_HEIGHT = 700;

public BallWindow() {
    super("Balls");
    this.panel = new BallPanel();
    this.panel.setPreferredSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
    this.add(panel);
    this.pack();
}

public int getPanelWidth() {
    return panel.getWidth();
}

public int getPanelHeight() {
    return panel.getHeight();
}

public void initGUI() {
    this.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setVisible(true);
}
//Balls have to tell to the window to paint them, just once
public void drawMe(BallThread ball) {
    this.panel.drawMe(ball);
}
}


class BallPanel extends JPanel {

private static final long serialVersionUID = 1L;
private Queue<BallThread> queue;

public BallPanel() {
     this.queue = new LinkedList<>();
}

public void drawMe(BallThread ball) {
    this.queue.add(ball);
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    while(queue.peek() != null) {
        queue.poll().drawBall(g2);
    }
} 
}

And here are the balls (they are threads):

public class BallThread implements Runnable {

private int x;
private int y;
private int dx;
private int dy;
private int diameter;
private int bounces;
private BallWindow window;

public BallThread(int x, int y, int dx, int dy, int diameter, int lBounces, BallWindow window) {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.diameter = diameter;
    this.bounces = lBounces;
    this.window = window;
}

public int getX() {
    return x;
}

public void setX(int x) {
    this.x = x;
}

public int getY() {
    return y;
}

public void setY(int y) {
    this.y = y;
}

public void setWindow(BallWindow window) {
    this.window = window;
}

public void drawBall(Graphics2D g) {
    g.setColor(Color.BLUE);
    g.fillOval(this.x, this.y, this.diameter, this.diameter);
}

private void move() {
    this.x += this.dx;
    this.y += this.dy;
    bounce();
    this.window.drawMe(this);
}

private void bounce() {
    if(this.x < 0) {
        this.x = 0;
        this.dx = -dx;
        this.bounces--;
    } else if(this.x+diameter > this.window.getPanelWidth()) {
        this.x = this.window.getPanelWidth()-diameter;
        this.dx = -dx;
        this.bounces--;
    }

    if(this.y < 0) {
        this.y = 0;
        this.dy = -dy;
        this.bounces--;
    } else if(this.y+diameter > this.window.getPanelHeight()) {
        this.y = this.window.getPanelHeight()-diameter;
        this.dy = -dy;
        this.bounces--;
    }
}

@Override
public void run() {
    while(this.bounces > 0) {
        this.move();
        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    GameLoop.decreaseThreadsWorking();
}

}

And the game loop:

public class GameLoop {

private final int N_BALLS = 20;
private static int nThreadsNow = 0;
private BallWindow window;
private ExecutorService poolThreads;

public GameLoop() {
    this.poolThreads = Executors.newFixedThreadPool(N_BALLS);
    gameLoop();
}

private void gameLoop() {
    window = new BallWindow();
    window.initGUI();

    while(true) {
        while(GameLoop.nThreadsNow < this.N_BALLS) {
            BallThread ball = BallFactory.getInstance().getBall(window);
            this.poolThreads.execute(ball);
            GameLoop.nThreadsNow++;
        }
        window.repaint();

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void decreaseThreadsWorking() {
    GameLoop.nThreadsNow--;
}

public static void main(String[] args) {
    new GameLoop();
}
}
1

There are 1 best solutions below

1
On BEST ANSWER

There is a lot of strong coupling here, and what looks odd is the balls repeatably adding themselves to a queue to be drawn.

Take that piece out and have the frame just repaint itself on a schedule. This includes repainting all the balls instead of trying to keep up with a queue that is constantly being increased.