Here is my Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class Main {
// code main
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(460, 500);
frame.setTitle("Circles generator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
String input = JOptionPane.showInputDialog("Enter n:");
CustomComponents0 component = new CustomComponents0();
frame.add(component);
frame.getContentPane().validate();
System.out.println("work before");
frame.getContentPane().repaint();
System.out.println("work");
frame.getContentPane().repaint();
System.out.println("work after");
}
// why is not JComponent
static class CustomComponents0 extends JLabel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(200, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
#Override
public void paintComponent(Graphics g) {
System.out.println("paint");
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}
here is the code and I want run repaint before work and run repaint after work.
When I run it, it should print
work before
paint
work
paint
work after
but there are only one paint and it's after work, Why is that happen? How can I fix that?
Thanks.
You must construct and manipulate Swing GUI objects only on the event dispatch thread. Because your program is incorrectly synchronized, any result is possible. It will depend in part on how far the initial thread gets before starting the EventQueue. Moreover, println() itself may be synchronized, and "events being posted to the EventQueue can be coalesced."
The variation below reliably shows the following output because events are dispatched "In the same order as they are enqueued." Note in particular how the calls to repaint() are coalesced. While this approach is illustrative, it is needlessly cumbersome for your likely goal. Instead, use javax.swing.Timer to pace animation as shown here.
Console:
paint
work before
work
work after
paint
Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
/** #see https://stackoverflow.com/a/44212328/230513 */
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setTitle("Circles generator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CustomComponent component = new CustomComponent();
frame.add(component);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
EventQueue.invokeLater(() -> { System.out.println("work before"); });
EventQueue.invokeLater(() -> { frame.repaint(); });
EventQueue.invokeLater(() -> { System.out.println("work"); });
EventQueue.invokeLater(() -> { frame.repaint(); });
EventQueue.invokeLater(() -> { System.out.println("work after"); });
});
}
static class CustomComponent extends JLabel {
private static final int N = 10;
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paint");
g.setColor(Color.red);
g.fillRect(N, N, getWidth() - N * 2, getHeight() - N * 2);
}
}
}
Related
My objective is to put a button and a circle on the JFrame. When i click the button the circle should move randomly on the panel/frame.
But when i click the button the circle just move once only and after putting SOP statements i found that "frame.repaint()" is getting called multiple times but this call is triggering the "paintComponent" method only once, the very first time (defined in class Panel1). Its very strange!
I have also provided another code which works as expected but has no buttons to trigger the animation. I have read that repaint() requests are coalesced together and executed once, then how come the second program works?
import java.awt.event.*;
import java.awt.Graphics.*;
import javax.swing.*;
import java.awt.*;
public class SimpleGui3c_4 {
public static void main(String args[]) {
Frame1 frame = new Frame1();
frame.go();
}
}
class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.setSize(500,500);
frame.setVisible(true);
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener());
}
class ColorActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<130;i++) {
System.out.println("Frame repaint started");
frame.repaint();
try {
Thread.sleep(5000);
}catch(Exception ex) {}
System.out.println("Frame repaint ended");
}
}
}
class Panel1 extends JPanel {
public void paintComponent(Graphics g) {
System.out.println("Inside the paint Component method");
int x = (int)(Math.random()*100);
int y = (int)(Math.random()*100);
g.setColor(Color.BLUE);
g.fillOval(x,y,100,100);
System.out.println("Exiting the paint component method");
}
}
}
Code which works but has no button to trigger the animation, it works as soon as i run the code. I am not sure why the below program works and the above program fails!
import javax.swing.*;
import java.awt.*;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String args[]) {
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for(int i = 0;i<130;i++) {
//x++;
//y++;
drawPanel.repaint();
try {
Thread.sleep(50);
}catch(Exception ex) {}
}
}//close go
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,this.getWidth(), this.getHeight());
int x = (int)(Math.random()*70);
int y = (int)(Math.random()*70);
g.setColor(Color.green);
g.fillOval(x,y,40,40);
}
}
}
I have also provided another code which works as expected but has no buttons to trigger the animation.
The difference between the two pieces of code is context within which they are been called.
The code that "works" is actually been updated out side the context of the Even Dispatching Thread, in the "main" thread, which means that doing something like Thread.sleep won't prevent the UI from been updated.
The code which does not work is been updated from with the content of the Event Dispatching Thread (from within the ActionListener), which is prevent the EDT from processing new paint requests until after the actionPerformed method returns
Another issue you will face relates to when you decide to update the position of the circle.
paintComponent can be called for all a number of different reasons, many which you don't control. Painting should focus on painting the current state and should never modify it (directly or indirectly). Instead, you should use some kind of update method, whose job it is, is to update the x/y position of the circle and trigger a new paint cycle.
I would highly recommend that you stop and take the time to read through:
Concurrency in Swing for a more detail explanation
How to use Swing Timers for a possible solution
Performing Custom Painting and Painting in AWT and Swing for a better understanding into how painting actually works in Swing.
Your problem is rookie mistake which comes about from not understanding how the API actually works and not understanding the tools available to solve it
There are a number of other "issues" which would result in undesirable behaviour, like not calling setVisible last, so the UI doesn't need be updated again to ensure that the components been added are visible.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleGui3c_4 {
public static void main(String args[]) {
new SimpleGui3c_4();
}
public SimpleGui3c_4() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Frame1 frame = new Frame1();
frame.go();
}
});
}
public interface Animatable {
public void update();
}
public class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener(p));
frame.pack();
frame.setVisible(true);
}
class ColorActionListener implements ActionListener {
private Animatable parent;
public ColorActionListener(Animatable parent) {
this.parent = parent;
}
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
btn.setEnabled(false);
Timer timer = new Timer(5000, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Frame repaint started");
parent.update();
counter++;
if (counter >= 130) {
((Timer)e.getSource()).stop();
btn.setEnabled(true);
}
}
});
timer.setInitialDelay(0);
timer.start();
}
}
class Panel1 extends JPanel implements Animatable {
private int xPos, yPos;
public Panel1() {
update();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Inside the paint component method");
g.setColor(Color.BLUE);
g.fillOval(xPos, yPos, 100, 100);
System.out.println("Exiting the paint component method");
}
#Override
public void update() {
System.out.println("Inside the update method");
xPos = (int) (Math.random() * 100);
yPos = (int) (Math.random() * 100);
repaint();
}
}
}
}
import javax.swing.*;
import java.awt.*;
public class Test1 {
int x = 70;
int y = 70;
public static void main (String[] args) {
Test1 gui = new Test1 ();
gui.go();
}
public void go() {
JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for (int i = 0; i < 130; i++) {
x++; y++;
drawPanel.repaint();
try { Thread.sleep(50);
} catch(Exception ex) { } }
}// close go() method
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillOval(x,y,40,40);
}
} // close inner class
} // close outer class
page1page2
According to the page 2, the circle should be smeared in the frame... but actually, when I ran it, it just moved without smearing. Why was that?
btw, if these codes were not able to make a smearing circle, how could I make a smearing one?
cheers
As shown here, "If you do not honor the opaque property you will likely† see visual artifacts." Indeed, running your example on Mac OS X with Java 6 produces a series of circles that appear "smeared."
How could I make a smearing one?
Do not rely on painting artifacts to get the desired result; instead, render a List<Shape> as shown below.
Use javax.swing.Timer to pace animation.
Construct and manipulate Swing GUI objects only on the event dispatch thread.
Override getPreferredSize() to establish a drawing panel's initial geometry.
Code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test2 {
public static void main(String[] args) {
EventQueue.invokeLater(new Test2()::display);
}
public void display() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyDrawPanel drawPanel = new MyDrawPanel();
frame.add(drawPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
private int x = 30;
private int y = 30;
private final List<Shape> list = new ArrayList<>();
public MyDrawPanel() {
new Timer(50, (new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x++;
y++;
list.add(new Ellipse2D.Double(x, y, 40, 40));
repaint();
}
})).start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.green);
for (Shape s : list) {
g2d.fill(s);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
}
†Emphasis mine.
I never worked with Timers before so my problem is probably stupid one really. My program draws a circle which is red and after random seconds the circle should change its color to green. I just made a swing timer as you can see below in the code. And it enters actionPerformed() method but it doesn't change color. Could you help me somehow fix my problem with changing colors?
My code:
package igrica;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ChangingCircle implements ActionListener{
JFrame frame;
Timer timer;
Random r;
public static void main(String[] args) {
ChangingCircle gui = new ChangingCircle();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(100, 100, 100, 100);
Random r = new Random();
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.out.println("Timer out");
g.setColor(Color.green);
g.fillOval(100, 100, 100, 100);
}
});
timer.start();
}
}
}
There's quite the mess in your code. Try this:
public class ChangingCircle {
Color color = Color.RED;
MyPanel panel = new MyPanel();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ChangingCircle gui = new ChangingCircle();
gui.go();
});
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
Random r = new Random();
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.out.println("Timer");
color = Color.GREEN;
panel.repaint();
}
});
timer.setRepeats(false);
timer.start();
}
class MyPanel extends JPanel {
private int size = 100, loc = 100;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(loc, loc, size, size);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(size + loc, size + loc);
}
}
}
The idea is that the timer only changes the property of the shape to be drawn and then calls repaint() to reflect the change. The paintComponent is called whenever it is needed, even in quick succession and should return quickly.
Specific Notes:
Start Swing from the EDT.
Create and start the timer from outside of paintComponent since it is called many times and that will create and start many timers.
You should probably set the timer not to repeat.
Call super.paintComponent(g); as the first thing inside paintComponent.
You seem to have an ActionListener that does nothing.
General tips:
Use the #Override annotation when applicable.
Call pack() on the frame instead of setting its size manually and #Override the getPreferredSize method of the component you paint on. Return a meaningful size based on what you draw.
Use add(component, location) and not the other way around (deprecated).
Don't use fields when local variables will do (Random r for example).
Use uppercase constant names (Color.RED instead of Color.red).
Don't initiate a Timer from within a paintComponent method. This method should be for painting and painting only. Instead start the Timer in your constructor and within your Timer's actionPerromed and call repaint(), change the state of a field of the class, and use that information within the paintComponent use that field to draw any new information.
e.g.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ChangingCircle {
JFrame frame;
public static void main(String[] args) {
ChangingCircle gui = new ChangingCircle();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
class MyPanel extends JPanel {
private Random r = new Random();
private boolean draw = false;
public MyPanel() {
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
draw = true;
repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (draw) {
g.setColor(Color.red);
g.fillOval(100, 100, 100, 100);
}
}
}
}
Also, don't forget to call the super's paintComponent method from within your override.
If you need to change colors, give the JPanel a Color field, say called color and change it's value from within the Timer, and then call repaint(). Again within paintComponent, use the value of that field to draw the oval with. Also in this situation, the Timer should repeat, so get rid of timer.setRepeats(false) in that situation.
The timer works asynchronously and paintComponent finishes before finishing the work of timer.
I have been struggling with Paint methods, and paint components, and extends JFrame, and have been trying all sorts of ways to get a simple rectangle to draw. here is a class called Window:
import javax.swing.*;
import java.awt.Graphics;
public class Window extends JFrame
{
/**
*
*/
private static final long serialVersionUID = 1L;
Window()
{
setSize(300,300);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
g.drawRect(300,300,300,300);
}
}
then class Main
public class Main {
public static void main(String args[])
{
Window mainWindow = new Window();
mainWindow.setBounds(100,100,300,300);
}
}
The sole purpose of this program is just draw a damn rectangle. I have no idea what I could be doing wrong, and I have been trying to drawRect or drawString for several days now, to no avail. I also tried with a panel.
Don't override paint of top level containers like JFrame, this is the quickest way to end up with a world of weird and unexpected results.
Between the frames actual surface and the use, there is a JRootPane, a contentPane and possibly a glassPane...
All of these can interface/erase what you've painted in the paint method.
Instead, start with a JPanel and override it's paintComponent. Create an instance of this and place it on an instance of a JFrame when you want to show it.
Have a look at Painting in AWT and Swing and Performing Custom Painting for more details
Also, beware that when painting, 0x0 is the top/left of your component, so in your example, you start painting at 300x300, but your frame is only 300x300, so you're actually painting of the screen
As an example:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestPaint {
public static void main(String[] args) {
new TestPaint();
}
public TestPaint() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
public PaintPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawRect(10, 10, getWidth() - 20, getHeight() - 20);
g2d.dispose();
}
}
}
could you try this code
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
public static void main(String[] a) {
JFrame f = new JFrame();
f.setSize(400, 400);
f.add(new Main());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public void paint(Graphics g) {
g.fillRect (5, 15, 50, 75);
}
}
So, I'm currently working on making a ball that moves on keyboard command. My problem is that when I call repaint();, it gives me an error saying that it "Cannot make a static reference to the non-static method repaint() from the type Component." What am I doing wrong?
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
public class App extends JFrame{
JFrame f = new JFrame();
public static int keyVal = 0, x = 10, y = 10;
public static void main(String[] args) {
new App();
Ball();
while(true){
System.out.println(keyVal);
try{
Thread.sleep(50);
}
catch(Exception e){}
}
}
public static void Ball(){
while(true){
if(keyVal == 65){
x = x -1;
}
else if(keyVal == 68){
x = x + 1;
}
repaint();
//repaint(x, y, 10, 20);
}
}
public App(){
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Pong");
f.setSize(30,40);
f.setLocationRelativeTo(null);
f.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e){
keyVal = e.getKeyCode();
}
public void keyReleased(KeyEvent e){
keyVal = 0;
}
public void keyTyped(KeyEvent e){}
});
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
class MyPanel extends JPanel {
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
return new Dimension(500,200);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("test");
g.setColor(Color.orange);
g.fillRect(x, y, 10, 20);
}
}
}
Your class is already a JFrame subclass. There's no need to create another JFrame. Take out the JFrame f = new JFrame() and all the f.method(..) just use method(..)
Don't use while(true) or Thread.sleep(). You will run into problem. Instead look into How to use a Swing Timer. Here is a simple example. You could also find many other examples just doing a simple google search on how to use Swing Timer
No need to setSize() to the frame, you already pack() it.
You should look into How to use Key Bindings. If not now, you will come to find that there are focus issues, among other things, with using a KeyListener.
Run your program from the Event Dispatch Thead, like this
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new App();
}
});
}
Freebie
A simple implementation of a javax.swing.Timer would be something like this
public App() {
...
Timer timer = new Timer(50, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
}
Here is the basic construct of the Timer
Timer( int dalay, ActionListener listener )
The delay it the amount of milliseconds delayed for each time the event is fired. So in the above code, for every 50 milliseconds, something will happen. This will achieve what you are trying to do with the Thread.sleep. You can call the repaint() from inside the actionPerformed
Here's a simple refactor of your code, that you can test out
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class App extends JFrame {
private MyPanel panel = new MyPanel();
public static int keyVal = 0, x = 10, y = 10;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new App();
}
});
}
public App() {
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x = x + 5;
panel.repaint();
}
});
timer.start();
add(panel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Pong");
pack();
setLocationRelativeTo(null);
setVisible(true);
}
class MyPanel extends JPanel {
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
return new Dimension(500, 200);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("test");
g.setColor(Color.orange);
g.fillRect(x, y, 10, 20);
}
}
}