0

I am trying out a program with Java Swing that draws Random Lines on a JPanel. For this I have overridden the paintComponent(Graphics g) method of the JPanel.

I'm also using a Thread to continuously repaint the component with a fixed delay between repaints.

The thread is being controlled by a JButton that can start and stop the painting of the component.

The whole panel is then embedded in a JFrame and shown on screen.

When I execute the code and Start the painting, I see that the "Start" Button is "bleeding" on to the JPanel where I'm painting the random lines.

Can anyone explain me why this is happening?

Here's an executable Java code for recreating the issue. I'm also adding screenshots for more clarity.


package application;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class RandomLines extends JPanel implements Runnable{
    private static final long serialVersionUID = 3312914356515718206L;

    private boolean running = false;
    private boolean init = false;

    private Point startPoint = getRandomPoint();
    private Point endPoint = getRandomPoint();

    public RandomLines() {
        setBounds(0, 0, 400, 400);
        setPreferredSize(new Dimension(400, 400));
    }

    @Override
    public synchronized void paintComponent(Graphics g) {
        if(init == false) { 
            g.setColor(Color.cyan);
            g.fillRect(0, 0, getWidth(), getHeight());
            g.setColor(Color.black);

            init = true;
        }
        g.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
    }

    public void run() {
        while(running) {
            repaint();
            delay();
            startPoint = endPoint;
            endPoint = getRandomPoint();
        }
    }

    private void delay() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }
    }

    private Point getRandomPoint() {
        int w = getWidth();
        int h = getHeight();

        int randX = (int) (Math.random() * 10000 % w);
        int randY = (int) (Math.random() * 10000 % h);

        return new Point(randX, randY);
    }

    public void createAndShowGui() {
        JFrame frame = new JFrame("Random Lines");
        frame.setSize(400, 400);
        frame.add(this);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton b = new JButton("Start");
        b.addActionListener(al -> {
            if (running == false) {
                frame.setVisible(true);
                running = true;
                b.setText("Stop");
                Thread t = new Thread(RandomLines.this);
                t.start();
            } else {
                frame.setVisible(false);
                frame.dispose();
                running = false;
                b.setText("Start");
            }
        });
        JOptionPane.showOptionDialog(null, b, "Control Random Lines", JOptionPane.OK_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"Close"}, null);
    }

    public static void main(String[] args) {
        RandomLines rl = new RandomLines();
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                rl.createAndShowGui();
            }
        });
    }

}

Here is a screen shot explaining the "bleeding" problem..

Here is a screen shot explaining the "bleeding" problem..

I hope I've detailed the question enough for others to understand. Let me know if you need more details.

Update

One observation I had was that when I use multiple monitors and have the JOptionPane and JFrame on two different monitors. The problem does not occur.

But why does it even happen in the first place?


anacron
  • 6,443
  • 2
  • 26
  • 31
  • 4
    You broke the paint chain and failed to call `super.paintComponent` – MadProgrammer Feb 16 '17 at 08:22
  • And I have to admit: I find that design rather strange. Why do you do **everything** using `invokeLater()`. I would rather create/init your frame using the "main thread"; and then have a *scheduled* job make the required calls to update the UI via `invokeLater()` ... as said: your solution looks really strange too me; not like a really good practice. For example I am wondering if your call to invokeLater() will return at all ?! – GhostCat Feb 16 '17 at 08:24
  • 1
    I'd also suggest you have a look at [How to use Swing Timers](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) for a better solution, which won't need you to worry about synchonrising and multiple thread related issues and Swing thread safety – MadProgrammer Feb 16 '17 at 08:26
  • I added the call to `super.paintComponent(g);` Now, the "bleeding" problem went away. But it is also clearing out the lines that had been created in the previous iterations of `repaint();` – anacron Feb 16 '17 at 08:28
  • @MadProgrammer. Okay, I read through your comments on the other question's Answer. So I will need to repaint all those lines all over again! – anacron Feb 16 '17 at 08:32
  • @anacron Yes, that's how painting works, it's destructive. Alternatively, you could paint the lines to a `BufferedImage` and simply paint that each time `paintComponent` is called, there are pros and cons to both approaches – MadProgrammer Feb 16 '17 at 08:38
  • Hmm.. Thank you. I'll try out the `BufferedImage` option. Let's see how it works out for me. Thanks again! – anacron Feb 16 '17 at 08:39

0 Answers0