3

I'm creating a sort of paint application. The user can move a circle in a JPanel by pressing/dragging the mouse.

I have a JCheckBoxMenuItem in one of my JMenus:

JCheckBoxMenuItem checkitem = new JCheckBoxMenuItem("Draw mode",false);
  • When it is not activated, the circle can only be moved (by dragging/pressing) and the previous circle will be erased.
  • When it is activated, the circle can only be moved, but the previous circle will not be erased when dragging/pressing the mouse ( This works the same way as a paint program )

Shortened version of my code:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class GUI extends JFrame implements MouseListener, MouseMotionListener, ActionListener, ItemListener
{
    JPanel mainPan, colorPan;
    Color color = Color.BLACK;
    JCheckBoxMenuItem checkitem;
    boolean clear = true;

    public GUI(String header)
    {
        maker();

        mainPan.addMouseListener(this);
        mainPan.addMouseMotionListener(this);

        add(mainPan , BorderLayout.CENTER);
        add(colorPan, BorderLayout.PAGE_END); 
    }

    public void maker()
    {
        colorPan = new JPanel();
        colorPan.setLayout(new GridLayout(1, 0));

        mainPan = new JPanel(){
            @Override
            public void paintComponent(Graphics g)
            {
                //g.setColor(Color.WHITE);
                //g.fillRect(0,0,getWidth(),getHeight());
                if(clear)
                    super.paintComponent(g); //Do the same thing as above(Clear JPanel)

                g.setColor(color);
                g.fillOval(x,y,50,50); //x and y are integer variables that I use in my full program
            }
        };

        checkitem = new JCheckBoxMenuItem("Draw mode",false);
        //After adding this to a JMenu,
        checkitem.addItemListener(this);
    }

    public void itemStateChanged(ItemEvent e)
    {
        if(e.getStateChange() == ItemEvent.SELECTED)
        {
            clear = false;
        }
        else
        {
            clear = true;
        }
    }
}

The below screenshot shows the output of my full program:

SCREENSHOT

colorPan is the JPanel full of JButtons of different colors. The top of it is mainPan.

Right now, the "Draw mode" doesn't work as expected. I had always thought that super.paintComponent(g); was the one that clears/resets the screen when repaint() is called. But I removed that and was quite surprised to see the program behave the same way.

Basically, my problem is here:

if(clear)
    super.paintComponent(g);

I need to prevent everything from being cleared when repaint() is called. How do I achieve what I want?

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
  • A complete example is cited [here](http://stackoverflow.com/a/11944233/230513). – trashgod May 14 '15 at 14:24
  • I don't understand it. Could you post an answer here? – Spikatrix May 14 '15 at 15:10
  • Possible duplicate: [Prevent Java from repainting the content of a JPanel while updating](http://stackoverflow.com/questions/4065456/prevent-java-from-repainting-the-content-of-a-jpanel-while-updating) – user3261344 May 14 '15 at 19:28

2 Answers2

3

It is not in this code where changes should be made. And it is not paint method which should be changed. Paint paints whenever is required either by your or by system. When window is resized or moved or partially covered - it uses paint to paint picture again. What you should really do is to stop updating coordinates for your painted oval. It could be done in mouse listener or in coordinates setter or, better, in control part which manages these coordinates. Your checkbox should control ability to change your model. It should not control painting. There is commonly used pattern Model-View-Controller - look at it. Maybe it could look like overkill for such small application but even Swing itself is built on this pattern so you already follow it. Issues rise when you try to break it. So - don't.

Alex
  • 4,457
  • 2
  • 20
  • 59
  • "*What you should really do is to stop updating coordinates for your painted oval*" -- The circle should be updated regardless of whether the checkbox is activated or not. When it is activated, previous drawings of the circle should not be erased. And when it is not activated, previous drawings of circles should be erased( This is what happens now even if the checkbox is activated ) – Spikatrix May 14 '15 at 14:22
  • Don't consider paint() as paint once. It is invoked a lot. To see it - drag some other window over your application. It will force paint to paint a lot of times and you will see that. – Alex May 14 '15 at 14:45
1

You can't "prevent the JPanel from being updated;" paintComponent() will be called asynchronously, as required by the system. Instead, condition attributes of your view class in a way that allows your implementation of paintComponent() to render everything whenever it is called.

In the example below, the foreground color is changed with each mouse click and paintComponent() uses the revised setting. In the more elaborate example cited here, ClearAction clears the List<Node> and List<Edge> that define the graph's model. Absent a call to super.paintComponent(g), otherwise required for an opaque component, a call to fillRect() in paintComponent() cleans up any leftover selection artifacts.

public void actionPerformed(ActionEvent e) {
    nodes.clear();
    edges.clear();
    repaint();
}

image

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see https://stackoverflow.com/a/5312702/230513 */
public class MouseDragTest extends JPanel {

    private static final String TITLE = "Drag me!";
    private static final Random r = new Random();
    private static final int W = 640;
    private static final int H = 480;
    private Point textPt = new Point(W / 2, H / 2);
    private Point mousePt;
    private Color color = Color.black;

    public MouseDragTest() {
        this.setFont(new Font("Serif", Font.ITALIC + Font.BOLD, 32));
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {
                mousePt = e.getPoint();
                setColor(Color.getHSBColor(r.nextFloat(), 1, 1));
                repaint();
            }
        });
        this.addMouseMotionListener(new MouseMotionAdapter() {

            @Override
            public void mouseDragged(MouseEvent e) {
                int dx = e.getX() - mousePt.x;
                int dy = e.getY() - mousePt.y;
                textPt.setLocation(textPt.x + dx, textPt.y + dy);
                mousePt = e.getPoint();
                repaint();
            }
        });
    }

    public void setColor(Color color) {
        this.color = color;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(W, H);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(color);
        int w2 = g.getFontMetrics().stringWidth(TITLE) / 2;
        g.drawString(TITLE, textPt.x - w2, textPt.y);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame f = new JFrame(TITLE);
                f.add(new MouseDragTest());
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045