To understand why it's not working, you need to understand how the paint system actually works
Just by looking at this snippet it should be obvious something is wrong.
public class Main extends JFrame implements ActionListener {
//...
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
You've declare a method called paintComponent but are calling the super method paintComponents (note the s at the end).
Further, when ever you "think" you're overriding a method, you should make use of the #Override attribute, this will cause a compiler error when you've done something wrong
public class Main extends JFrame implements ActionListener {
//...
@Overrride
public void paintComponent (Graphics g) {
super.paintComponents(g);
//...
}
}
The above code will now fail to compile, as JFrame doesn't declare a paintComponent method.
As a general rule, you should avoid extending directly from JFrame (or other top level containers), they are compound components and have a complex hierarchy and functionality.
A better place to start might be with a JPanel

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
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.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
public TestPane() {
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
}
});
paintPane = new PaintPane();
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
g1.drawOval(3, 5, 45, 46); // The ball
g1.fillOval(20, 30, 40, 40);
}
}
}
You should take the time to have a look at Painting in Swing and Performing Custom Painting for more details.
Some other concepts you might like to take the time to learn:
- Single Responsibility Principle - a class should do one thing and do it well
- Observer Pattern - This typically represent in Swing as the listener API
- Model-View-Controller - this encompasses the above and defines different layers of responsibility for different parts of the program, it will helper you understand the basic structure of Swing as well
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
Ok, so this is where the "model" part of the MVC will play it's part.
So lets start by defining the basic properties we expect the model to support...
public interface ShapeModel {
public Point getPoint();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
Here is supports a Point to act as the location and a ChangeListener to act as the observer pattern, which will notify interested parties that the state of the model has changed.
Why start with a interface? As a general concept, you should always prefer to code to interface instead of implementation. In this case, one aspect of the interface which hasn't been defined is, how does the Point get updated? That's of little interest to most parties who want to work with the model, they just want to know when it changes, the mutation of the model can be expressed either directly via the implementation or a "mutable" interface which extends from the this interface
Next, we define a default implementation...
public class DefaultShapeModel implements ShapeModel {
private Point point = new Point(40, 40);
private List<ChangeListener> listeners = new ArrayList<>(25);
@Override
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
fireStateChanged();
}
protected void fireStateChanged() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
@Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
}
This does define how the paint is to be updated.
Finally, we update the TestPane and PaintPane to support the model...
public class TestPane extends JPanel {
JButton left;
JButton right;
JPanel paintPane;
private DefaultShapeModel model;
public TestPane() {
model = new DefaultShapeModel();
JButton left = new JButton("left");
left.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
Point p = model.getPoint();
p.x--;
if (p.x > 0) {
p.x = 0;
}
model.setPoint(p);
}
});
JButton right = new JButton("right");
right.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Point p = model.getPoint();
p.x++;
if (p.x + 40 > paintPane.getWidth()) {
p.x = paintPane.getWidth() - 40;
}
model.setPoint(p);
}
});
paintPane = new PaintPane(model);
setLayout(new BorderLayout());
add(left, BorderLayout.WEST);
add(right, BorderLayout.EAST);
add(paintPane);
}
}
public class PaintPane extends JPanel {
private ShapeModel model;
public PaintPane(ShapeModel model) {
this.model = model;
this.model.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
repaint();
}
});
}
public ShapeModel getModel() {
return model;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g1 = (Graphics2D) g;
Point p = getModel().getPoint();
g1.fillOval(p.x, p.y, 40, 40);
g1.setColor(Color.WHITE);
g1.drawOval(p.x, p.y, 40, 40);
}
}