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);
}
}
}
Related
I am creating a Java program using Swing and want to open a JFrame, run a for-loop that stores items in a list of Strings, display graphics while that is happening, and only after that is done call another method. The issue is that even though I call frame.setVisible(true); before running the loop, it only displays after the loop is done. I would use a SwingWorker but I need to stop the main thread from running the next method until after the loop is finished. If someone knows a way to use SwingWorker or knows a fix to this, that would be great. Here is the code I am referring to:
//The JPanel in charge of displaying graphics while the loop is running
FrameRenderer renderer = new FrameRenderer(videoFile, this.getWidth(), this.getHeight());
this.add(renderer);
this.setVisible(true);
//Call the method with the for-loop after this.setVisible is called
List<String> frames = renderer.renderFrames();
//I need this to run after the loop is finished
DisplayFrames display = new DisplayFrames(frames, this.getWidth(), this.getHeight(), this);
this.add(display);
SwingWorker
Worker Threads and SwingWorker
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private JLabel label = new JLabel("...");
public MainPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 64, 32, 64));
add(label);
SwingWorker<Void, String> worker = new SwingWorker<>() {
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
publish(Integer.toString(index));
// It's important, if you want to allow the UI to
// update on a single view, you need to allow time
// for this thread to sleep, otherwise, you could
// end up in a siutatio where the only update you
// get is the last one (with a list of all the
// the values you "published"
Thread.sleep(10);
}
return null;
}
#Override
protected void process(List<String> chunks) {
label.setText(chunks.get(chunks.size() - 1));
}
};
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
label.setText("All done here");
}
}
});
worker.execute();
}
}
}
Swing Timer
How to Use Swing Timers
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
private JLabel label = new JLabel("...");
public MainPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(32, 64, 32, 64));
add(label);
Timer timer = new Timer(10, new ActionListener() {
private int value = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (value >= 1000) {
((Timer)(e.getSource())).stop();
label.setText("All done here");
}
label.setText(Integer.toString(value));
value++;
}
});
timer.start();
}
}
}
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 have added a keylistener to try and get a shape to move right when I press the right arrow key. But it isn't working. I don't really know how to use keylistner that well. Can someone help me.
This is the code:
package walkingman;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class WalkingMan extends JPanel implements KeyListener{
int x = 0;
int y = 0;
#Override
public void paint(Graphics g){
super.paint(g);
g.fillOval(x, y, 150, 150);
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame ("Walking Man");
frame.setSize(1080,720);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
WalkingMan game = new WalkingMan();
frame.add(game);
while (true){
game.repaint();
game.keyPressed(e);
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
x++;
}
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
There are a few issues with your code
You never add the KeyListener to the panel.
A KeyListener for a JPanel would only work if it is focusable & also focused.
Override paintComponent instead of paint.
Call setVisible at the end of the method.
Get rid of the whole while-loop, it'll only cause problems.
Use KeyBindings instead of KeyListeners.
Fixed code without key bindings:
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class WalkingMan extends JPanel implements KeyListener {
int x = 0;
int y = 0;
#Override
public void paintComponent(Graphics g) { // Overide paintComponent, not paint
super.paintComponent(g);
g.fillOval(x, y, 150, 150);
}
public WalkingMan() { // Class Constructor
setFocusable(true); // KeyListeners only work if the component is focusable
addKeyListener(this); // Add the KeyListener implemented by this class to the instance
}
public void createAndShowGUI() {
JFrame frame = new JFrame("Walking Man");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
WalkingMan game = new WalkingMan();
frame.add(game);
frame.setSize(1080, 720);
frame.setVisible(true); // Call setVisible after adding the components
game.requestFocusInWindow(); // Request focus for the panel
}
public static void main(String[] args) throws InterruptedException {
new WalkingMan().createAndShowGUI();
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
x++;
}
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
Set frame well before it is displayed And remove the while loop which is still running.
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame ("Walking Man");
frame.setSize(1080,720);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
WalkingMan game = new WalkingMan();
frame.add(game);
frame.setVisible(true);//Call visible method here
}
Why not restructure your code in a much clear way like this
import javax.swing.*;
import java.awt.*;
import javax.swing.border.LineBorder;
import java.awt.Color;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class WalkingMan extends JFrame{
EmilsKeyClass keyBoard = new EmilsKeyClass();
public WalkingMan (){
add(keyBoard,BorderLayout.CENTER);
keyBoard.addKeyListener(new KeyAdapter(){
#Override
public void keyPressed(KeyEvent e){
if(e.getKeyCode()== KeyEvent.VK_ENTER){
x++;
repaint();
}
}
});
keyBoard.setFocusable(true);
}
public static void main(String [] args){
WalkingMan frame = new WalkingMan ();
frame.setTitle("Walking Man");
frame.setSize(1080,720);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class EmilsKeyClass extends JPanel{
int x = 0;
int y = 0;
public EmilsKeyClass(){
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
super.paint(g);
g.fillOval(x, y, 150, 150);
//your code
}
}
}
I have a class mypanel extends from jpanel where i use the graphics and make a ball. Second class is Main where i make a JFrame and add panel to frame. There is another class MKeyListener in Main which extends from KeyAdapter class where i handel the keyboard event. I have made a object of Jpanel class in Main class and register the MkeyListener class with the jpanel class. now i want to move down the ball on jpanel with down keyboard key butt ball is not moving down with down key that is code of my programe.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class mypanel extends JPanel{
int n=0;
int m=0;
int i=170;
int j=340;
int a=60;
int b=20;
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D g2= (Graphics2D)g;
g2.setColor(Color.green);
g2.fillOval(n,m,10,10);
}
}
public class Main {
JFrame frame;
mypanel p;
int x,y;
public Main (){
x=0;
y=0;
frame=new JFrame();
Container c = frame.getContentPane();
c.setLayout(new BorderLayout());
p = new mypanel();
c.add(p,BorderLayout.CENTER);
frame.setSize(400,400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MKeyListener k=new MKeyListener();
p.addKeyListener(k);
}
public static void main(String args []) {
Main a= new Main();
}
class MKeyListener extends KeyAdapter {
public void keyPressed(KeyEvent event) {
if (event.getKeyCode()==KeyEvent.VK_DOWN ) {
x =x+4;
y=y+4;
p.n+=x;
p.m+=y;
p.repaint();
System.out.println("success");
}
}
}
}
KeyListener is is picky, the component it is registered to must have focus AND be focuable before it will trigger key events. It can also be overridden by any other focusable component, which can be a good and bad thing.
It's generally recommended to use the key bindings API instead, which gives you control over the focus level required to trigger events. It's also generally far more flexible in it's configuration and re-usability
See How to Use Key Bindings for more details
import java.awt.Color;
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.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String args[]) {
Main a = 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 MyPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MyPanel extends JPanel {
private int n = 0;
private int m = 0;
private int i = 170;
private int j = 340;
private int a = 60;
private int b = 20;
public MyPanel() {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Action.down");
am.put("Action.down", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
n += 4;
m += 4;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.green);
g2.fillOval(n, m, 10, 10);
}
}
}
As a general piece of advice, it's generally a bad idea to expose fields of you object as public or package-private, you lose control over there management, meaning that they could be modified from any where with out your knowledge or control.
Better to self contain the management of these values (either internally or through the use of getters) or via a model-controller paradigm