paintComponent draws other components on top of my drawing
Asked Answered
T

2

8

I'm trying to build a simple paint tool. The mouseDrag events creates a new ellipse and causes my JPanel to repaint().

This works fine so far. However, if I press any button (or any other UI component) before firing the mouseDrag event for the first time, the button is painted in the upper left corner of my panel.

I have isolated the code into this test application:

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test extends JFrame
{
    public Test()
    {
        final JPanel paintPanel = new JPanel(){
            @Override
            protected void paintComponent(Graphics g)
            {
                Graphics2D g2d = (Graphics2D)g;
                g2d.setPaintMode();

                g2d.setStroke(new BasicStroke(1));
                g2d.fillRect(100, 100, 10, 10);
            }
        };

        paintPanel.setPreferredSize(new Dimension(300,300));
        paintPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                paintPanel.repaint();
            }
        });

        this.setLayout(new FlowLayout());

        this.add(paintPanel);
        this.add(new JButton("Dummy"));

        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public static void main(String... args)
    {
        new Test();
    }
}

TestApp before clicking on the Panel TestApp after clicking on the Panel

A Screenshot for "seeing" the problem in my Main application

Togs answered 13/11, 2012 at 9:58 Comment(4)
Ehm? What are you missing? I posted some code, described the problem short and posted a screenshot to see the effect?Togs
From the information you have provided, we can only try to guess what is going on.Acinaciform
I would like to get your working short compilable example run it and see the problem.Marcelline
Post an SSCCE (as @Marcelline (+1) said, just added link)Carlacarlee
C
9

+1 to @MadProgrammer's answers.

  • You should have super.paintComponent(..) as the first call in your overriden paintComponent()
  • Do not extend JFrame unnecessarily
  • Create and minipulate Swing components via EDT
  • Dont call setPrefferedSize() rather override getPrefferedSize()

Here is an example which incorporates my advice's and @MadProgrammer's:

enter image description here

import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

    JFrame frame;

    public Test() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final PaintPanel paintPanel = new PaintPanel();

        paintPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                paintPanel.addRect(e.getX(), e.getY());
            }
        });

        frame.setLayout(new FlowLayout());

        frame.add(paintPanel);
        frame.add(new JButton("Dummy"));

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String... args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Test();
            }
        });
    }
}

class PaintPanel extends JPanel {

    public PaintPanel() {
        addRect(100, 100);
    }
    ArrayList<Rectangle> rects = new ArrayList<>();

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setPaintMode();

        for (Rectangle r : rects) {
            g2d.setStroke(new BasicStroke(1));
            g2d.fillRect(r.x, r.y, r.width, r.height);
        }
    }

    public void addRect(int x, int y) {
        rects.add(new Rectangle(x, y, 10, 10));
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
}
Carlacarlee answered 13/11, 2012 at 10:41 Comment(2)
thanks for your advices, the snipped above was just a quick'n'dirty extract of my app's code. but always good to know the best practice! (my prof seems not to know -.-') (PS: Selected your answer as accepted one because of the excellent code example)Togs
@Togs its a pleasure. Please see updated code for working snippetCarlacarlee
R
6

You're not calling super.paintComponent.

The graphics context used for a paint cycle is shared between all the components begin painted, this means if you don't take care to clear it before painting onto, you will end up with what ever was painted before you.

One of the jobs of paintComponent is to prepare the graphics for painting

Rumen answered 13/11, 2012 at 10:19 Comment(4)
Yes. If I call paintComponent, the additional control is not painted. but also all ellipses i painted are lost, only the new ellipse is painted.Togs
Paint is expected to repaint/regenerate all damaged areas on each paint cycle. This means you need some way to regenerate your display on each paint cycle. Have a look at #12684033Rumen
PS: Marked Davids answer as accepted one because of the excellent example, but I got the clue with your link! ;-)Togs
Yeah, that's what you get for answering on the iPad ;)Rumen

© 2022 - 2024 — McMap. All rights reserved.