1

How would I clear a single shape from my JFrame without making the screen flash white.

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;`

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;


public class Frame extends JFrame {
static Shape s;
Graphics2D g2D;
/**
 * 
 */
private static final long serialVersionUID = 7764273188525543212L;
private JPanel contentPane;
public Point startDrag = null, endDrag = null;
HashMap<Shape, Color> shapes = new HashMap<Shape, Color>();
/**
 * Launch the application.
 */
public static void main(String[] args) {
    Frame f = new Frame();
    f.setVisible(true);
}
/**
 * Create the frame.
 */
public Frame() {

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 450, 300);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    contentPane.setLayout(new BorderLayout(0, 0));
    setContentPane(contentPane);
    addMouseListener(new MouseAdapter() {

        @Override
        public void mousePressed(MouseEvent e) {
            startDrag = new Point(e.getX(), e.getY());
            endDrag = startDrag;
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            Shape s = makeRectangle(startDrag.x, startDrag.y, e.getX(),
                    e.getY());
            Color[] colors = {Color.MAGENTA, Color.CYAN, Color.RED,
                    Color.BLUE, Color.PINK, Color.yellow};
            Random r = new Random();
            int i = r.nextInt(colors.length - 1);
            shapes.put(s, colors[i]);
            endDrag = null;
            startDrag = null;
            repaint();
        }

    });
    addMouseMotionListener(new MouseMotionAdapter() {



        @Override
        public void mouseDragged(MouseEvent e) {
            endDrag = new Point(e.getX(), e.getY());
            repaint();

        }
    });
}
public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    paintBackground(g2);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
            0.50f));
    g2.setStroke(new BasicStroke(2));
    for (Entry<Shape, Color> s : shapes.entrySet()) {

        g2.setPaint(Color.BLACK);
        g2.draw(s.getKey());
        g2.setPaint(s.getValue());
        g2.fill(s.getKey());
    }
    if (startDrag != null) {
        if (endDrag != null) {

            g2.setStroke(new BasicStroke(5.0f));
            g2.setColor(Color.darkGray);

            s = makeRectangle(startDrag.x, startDrag.y, endDrag.x,
                    endDrag.y);
            g2.draw(s);
        }

    }
}
private Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
    return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2),
            Math.abs(x1 - x2), Math.abs(y1 - y2));
}
private void paintBackground(Graphics2D g2) {
    clear(g2);
    g2.setPaint(Color.red);
    g2.setStroke(new BasicStroke(0.4f));
    for (int i = 0; i < getSize().width; i += 10) {
        Shape line = new Line2D.Float(i, 0, i, getSize().height);
        g2.draw(line);
    }

    for (int i = 0; i < getSize().height; i += 10) {
        Shape line = new Line2D.Float(0, i, getSize().width, i);
        g2.draw(line);
    }

}

private void clear(Graphics g) {
    g.clearRect(0, 0, getWidth(), getHeight());

}

}

// If you try this code, just drag and let go to make a rectangle.

But when I drag, the screen flashes white, so it's hard for me to see what I'm doing. Is there anyway to make this work the same way, but that clears the JFrame faster than you can see?

Orion31
  • 556
  • 6
  • 28
  • 1
    See [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) and [Performing Custom Painting](http://docs.oracle.com/javase/tutorial/uiswing/painting/) for more details about painting in Swing – MadProgrammer May 06 '15 at 01:04
  • @MadProgrammer I know how to paint, but I just want it to look more smooth. – Orion31 May 06 '15 at 01:08
  • 2
    But you don't understand how the system works. Top level components AREN'T double buffered, this means that they paint DIRECTLY to the device, one step at a time, which is what is causing your problems. Instead, you should use a Swing based component and override it's `paintComponent` (as suggested by the documentation) as Swing components ARE double buffered. It also encourages you to decouple your code, which makes it more re-usable – MadProgrammer May 06 '15 at 01:16
  • Should I rename the `paint` override to `paintComponent`? – Orion31 May 06 '15 at 01:21
  • Which would be pointless, as top level containers (like `JFrame`) don't have a `paintComponent` method. You have to use a Swing based component (like `JComponent` or preferably, `JPanel`) – MadProgrammer May 06 '15 at 01:26
  • @MadProgrammer and how would I call paintComponent, because it requires a Graphic as its parameter – Orion31 May 06 '15 at 01:32
  • @R.S: the **same way that you don't call paint**. Let the JVM call it for you! – Hovercraft Full Of Eels May 06 '15 at 01:34
  • You don't. As we've said, the system will tell you when it want's something painted by calling `paintComponent` (in a round about way) of it's own accord, you simply respond to the event and update the painting of the current state of the component. You can make "suggestions" to the system to repaint your component by `calling` `repaint`, but the system will decide what and when something will get painting, as described in [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) – MadProgrammer May 06 '15 at 01:35
  • Probably because `JFrame` DOESN'T have a `paintComponent` method (I'm guessing, but this seems like what you are trying to do) – MadProgrammer May 06 '15 at 01:36
  • Then you're using it wrong. Make sure to have an @Override before it so that you **know** that it's overriding the JPanel's own paintComponent, same as for paint. Your comments suggest that you're making comments **before** checking the tutorial links -- don't do this please. Please see my example below. – Hovercraft Full Of Eels May 06 '15 at 01:37
  • Something like [this](http://stackoverflow.com/questions/15396258/drawpolygon-keeps-drawing-lines-from-starting-mousepressed-location-to-current/15398270#15398270) for example – MadProgrammer May 06 '15 at 01:37

1 Answers1

2

Don't draw directly in a JFrame. Instead draw in the paintComponent(Graphics g) method of a JPanel. Be sure to call the super.paintComponent(g) within your override. This will give you double buffering by default and may help some of the flashing.

e.g.,

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.*;

@SuppressWarnings("serial")
public class DrawPanel extends JPanel {
   private static final int PREF_W = 450;
   private static final int PREF_H = 300;
   private static final int SMALL_SQR_SIZE = 10;
   private static final Stroke STROKE_5 = new BasicStroke(5f);
   private static final Stroke STROKE_2 = new BasicStroke(2f);
   private static final Color[] COLORS = { Color.MAGENTA, Color.CYAN, Color.RED,
         Color.BLUE, Color.PINK, Color.yellow };
   private Random random = new Random();
   private BufferedImage background;
   private Map<Shape, Color> shapes = new LinkedHashMap<>(); // so order is maintained
   private Shape drawingShape = null;

   public DrawPanel() {
      background = createBackground();
      MyMouseAdapater myMouseAdapater = new MyMouseAdapater();
      addMouseListener(myMouseAdapater);
      addMouseMotionListener(myMouseAdapater);
   }

   private BufferedImage createBackground() {
      BufferedImage bg = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = bg.createGraphics();
      g2.setPaint(Color.red);
      // g2.setStroke(new BasicStroke(BASIC_STROKE_POINTS));
      for (int i = 0; i < PREF_W; i += SMALL_SQR_SIZE) {
         g2.drawLine(i, 0, i, PREF_H);
      }

      for (int i = 0; i < PREF_H; i += SMALL_SQR_SIZE) {
         g2.drawLine(0, i, PREF_W, i);
      }

      g2.dispose();
      return bg;
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (background != null) {
         g.drawImage(background, 0, 0, null);
      }
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setStroke(STROKE_2);
      for (Shape shape : shapes.keySet()) {
         g2.setColor(Color.black);
         g2.draw(shape);
         g2.setColor(shapes.get(shape));
         g2.fill(shape);
      }
      if (drawingShape != null) {
         g2.setStroke(STROKE_5);
         g2.setColor(Color.DARK_GRAY);
         g2.draw(drawingShape);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private class MyMouseAdapater extends MouseAdapter {
      private Point initP = null;
      @Override
      public void mousePressed(MouseEvent e) {
         if (e.getButton() != MouseEvent.BUTTON1) {
            return;
         }
         initP = e.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         if (initP == null) {
            return;
         }
         Point p = e.getPoint();
         drawingShape = makeRectangle(p.x, p.y, initP.x, initP.y);
         repaint();
      }

      @Override
      public void mouseReleased(MouseEvent e) {
         if (initP == null) {
            return;
         }
         Point p = e.getPoint();
         Shape newShape = makeRectangle(p.x, p.y, initP.x, initP.y);
         shapes.put(newShape, COLORS[random.nextInt(COLORS.length)]);

         drawingShape = null;
         initP = null;
         repaint();         
      }

      private Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
         return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2),
               Math.abs(x1 - x2), Math.abs(y1 - y2));
      }
   }

   private static void createAndShowGui() {
      DrawPanel mainPanel = new DrawPanel();

      JFrame frame = new JFrame("DrawPanel");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_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 I extended JPanel so that I can use its paintComponent method, and I called the super method. I also used a LinkedHashMap so that the Map's order of entry is maintained.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Should I use 'paintComponent(getGraphics())'? How should I do that? – Orion31 May 06 '15 at 01:06
  • @R.S: No -- definitely not. You will want to use the Graphics object given to the method by the JVM. Hang on... small example coming. Also draw your background image into a BufferedImage and display that in the paintComponent via `g.drawImage(...)` – Hovercraft Full Of Eels May 06 '15 at 01:07
  • Should I make a JComponent or just call paintComponent in the `public void paint(Graphics g)` override? – Orion31 May 06 '15 at 01:14
  • 1
    @R.S *" just call paintComponent in the public void paint(Graphics g) override"*- NO!! Read [Painting in AWT and Swing](http://www.oracle.com/technetwork/java/painting-140037.html) to understand HOW painting actually works. You don't want to override `paint` ever (or VERY, VERY rarely) and especially not from top level containers like `JFrame`. The first rule of painting in Swing, you DON'T control the paint process, the API does, it will TELL you when it want's something painted. You can make suggestions to the system when to paint by using `repaint`, but the API will decide what and when – MadProgrammer May 06 '15 at 01:19