0

I'm trying to make a rectangle to move across the screen, but instead it's just repainting and not getting rid of the preceding rectangle making it look like a giant rectangle across the screen. Any help would be appreciated, here's my code:

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;

@SuppressWarnings("serial")
public class Test extends JFrame implements ActionListener{
int x=50;
Timer tm = new Timer(30,this);

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

public Test(){
    this.setSize(700, 500);
    this.setTitle("Drawing Shapes");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.setVisible(true);
}

public void paint(Graphics g){
     Graphics2D graph2 = (Graphics2D)g;

     Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
     graph2.setColor(Color.RED);
     graph2.fill(Rect);
     tm.start();
    }

public void actionPerformed(ActionEvent e){
    x=10+x;
    repaint();
}
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433

3 Answers3

4
  • Draw in a JPanel that is held by and displayed within the JFrame. Don't draw directly in the JFrame as this risks drawing over things that shouldn't be messed with such as root panes, borders, child components,... Also you lose the benefits of automatic double buffering by drawing directly in the JFrame's paint method leading to choppy animation.
  • You should override the JPanel's paintComponent method not its paint method.
  • Always call the super's painting method in your own painting method (here it again should be paintComponent). This is your problem. So again, your paintComponent(Graphics g) override painting method should call super.paintComponent(g); on its first line. This will erase the old images.
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
2

You're breaking the paint chain...

public void paint(Graphics g){
    // Broken here...
    Graphics2D graph2 = (Graphics2D)g;

    Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
    graph2.setColor(Color.RED);
    graph2.fill(Rect);
    tm.start();

}

You MUST call super.paint. See Painting in AWT and Swing and Performing Custom Painting for more details about painting in Swing...

Top level containers are not double buffered and it is not recommended to paint directly to them, instead, create a custom component which extends from something like JPanel and override it's paintComponent method (calling super.paintComponent before performing any custom painting)

As a general rule of thumb, you should avoid extending from top level containers like JFrame, as you are not adding any new functionality to the class and they lock you into a single use-case, reducing the re-usability of your classes

DON'T call tm.start inside the paint method, you should do nothing in the paint methods except paint, never try and modify the state or otherwise perform an action which might indirectly modify the state of a component, this is a good way to have you program consume your CPU

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • for some reason, calling super.paint on his code will simply draw nothing. – Jean-François Savard Feb 09 '15 at 03:47
  • @Jean-FrançoisSavard Did you call it first? Are there any other components which you've added to the frame? – MadProgrammer Feb 09 '15 at 03:47
  • Yes, trying to figure out why, no added nothing... Does it work for you ? – Jean-François Savard Feb 09 '15 at 03:48
  • It's possible that when the frame is repainted, it's also painting the `JRootPane` and it's `contentPane`, painting over you...another good reason not to override `paint` of top level containers - Based on my quick testing, that seems to be the case... – MadProgrammer Feb 09 '15 at 03:50
  • Interesting, I'll try to debug more into this. – Jean-François Savard Feb 09 '15 at 03:53
  • @Jean-FrançoisSavard This is also why I recommended against overriding `paint` of a top level container like `JFrame` – MadProgrammer Feb 09 '15 at 03:56
  • @MadProgrammer I will take a look at the link as I'm not really sure how having the super paint component makes it so it destroys the preceding image. –  Feb 09 '15 at 05:01
  • @user3882522 One of the jobs of `paintComponent` is to prepare the `Graphics` context for painting by the component, typically this is to fill the `Graphics` context with the background color of the component – MadProgrammer Feb 09 '15 at 05:04
1

To add to what other have already stated,

I've noticed that you use this.setLocationRelativeTo(null) for a simple application. Not saying it is bad, but you might want to check this thread to make sure it is what you want.

How to best position Swing GUIs?

Community
  • 1
  • 1
Jean-François Savard
  • 20,626
  • 7
  • 49
  • 76
  • 1
    Great, now you've just painted over the entire frame including it's children :P, also, beware, Swing doesn't have to notify the parent containers when a child component is painted (they can be painted independently) which could cause no end of issues and artifacts...this is why it's not recommended to override `paint` and especially `paint` of top level containers...You're trying to stop a hemorrhage with a kiddy band aid... – MadProgrammer Feb 09 '15 at 03:54
  • @MadProgrammer I agree that this is not the best solution, just trying to give the OP an alternative as the other one does not work and maybe he don't want to re-design his application. – Jean-François Savard Feb 09 '15 at 03:57
  • 1
    IMHO If the OP want's a trouble free experience, they need to. They can either fight the API or work with it – MadProgrammer Feb 09 '15 at 04:03
  • @MadProgrammer Once again, I agree. However maybe his manager doesn't for money reason ? Or maybe this is a school project where re-writing the application would not really worth it. There are many possible reasons for him to not re-design and my solution would works without having to. The most important thing IMO is that he learn from his errors and does not repeat again. No need to re-design if it does not worth it here. – Jean-François Savard Feb 09 '15 at 04:07
  • 1
    *"However maybe his manager doesn't for money reason ?"* It'll cost more in maintenance to fix it later. Better to fix it now! BTW - I was about to down-vote till I saw the last paragraph + link and decided that plus one cancelled out the down-vote. ;) – Andrew Thompson Feb 09 '15 at 04:09
  • Sure, it's only gong to cost more money and/or time trying to rectify all the other issues associated with doing it wrong in the first place. – MadProgrammer Feb 09 '15 at 04:10
  • Once again I agree, but the point is that he maybe just want a fast alternative that `works` and mine does, while I know that re-designing would be a better solution. – Jean-François Savard Feb 09 '15 at 04:13
  • @Jean-FrançoisSavard Yes you're right, I appreciate your answer and it worked, thanks. –  Feb 09 '15 at 04:58
  • @AndrewThompson and MadProgrammer just so you know, now that the OP took in consideration the bad solution I gave him and know it is a possibility, I have edited the post for further reference to only keep the second part. – Jean-François Savard Mar 06 '15 at 18:16