Changing a shape and a colour after a click (GUI, Java) - java

I'm just learning how GUI works and I wanted to write a code where following happens:
firstly we see red rectangle
after a click it changes into a circle in gradient (I picked orange and pink) + the background is black.
The problem is, I don't know how to notify the change when I use repaint(), I tried creating the first picture with another method - fail, maybe I lack some knowledge. Currently we get just the second output that doesn't change after the click.
This is the code at the moment I got stuck:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGUI implements ActionListener {
JButton button;
JFrame frame;
public void work() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("Color change");
button.addActionListener(this);
mojpanel panel = new mojpanel();
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300,300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import javax.swing.*;
import java.awt.Graphics2D;
public class mojpanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
GradientPaint gradient = new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D) g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
SimpleGUI aplGUI = new SimpleGUI();
JFrame frame = new JFrame();
mojpanel panel = new mojpanel();
frame.add(panel);
aplGUI.work();
}
}
import java.awt.*;
public class Painting extends SimpleGUI {
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawRect(100, 100, 100, 100);
g.fillRect(100, 100, 100, 100);
}
}

When you create new classes, you short invest more thought into them. You have two classes which are supposed to be interchangeable, one painting a red rectangle, the other painting a circle with a color gradient, with the ability to switch from the former to the latter.
So when you have one class extending JPanel and the other extending SimpleGUI, there is no way to exchange one for the other. Further, the names mojpanel and Painting do not reflect their purposes.
Besides that, you have it backward. Don’t implement an action that calls repaint(), followed by an attempt to recognize that repaint() has been called, to modify the GUI afterwards. Instead, change the GUI’s state and after the GUI has changed in a way that the visual appearance needs to be updated, call repaint. Note that most properties of the Swing components do already trigger an according repaint automatically when you change them.
You may create two classes extending JComponent having a custom paintComponent method and replace one with the other when the action has been triggered. But there’s a less intrusive way. Let the classes implement Icon and set the icon property of a component, e.g. a JLabel. This is one of the properties that will trigger the painting automatically:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SimpleGUI {
static class GradientOval implements Icon {
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
#Override
public int getIconWidth() {
return 200;
}
#Override
public int getIconHeight() {
return 200;
}
}
static class RedRectangle implements Icon {
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
#Override
public int getIconWidth() {
return 200;
}
#Override
public int getIconHeight() {
return 200;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel content = new JLabel(new RedRectangle());
JButton button = new JButton("Change To Circle");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
I don’t know which level of Java knowledge you have. The code
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
creates an instance of an anonymous inner class implementing ActionListener. You can simplify this code using a lambda expression:
button.addActionListener(e -> content.setIcon(new GradientOval()));
To demonstrate the interaction between component properties and repaints, here an approach using a custom component:
import java.awt.*;
import javax.swing.*;
public class SimpleGUI {
static class DualAppearance extends JComponent {
private boolean first = true;
public boolean isFirst() {
return first;
}
public void setFirst(boolean shouldBeFirst) {
if(shouldBeFirst != first) {
first = shouldBeFirst;
repaint();
}
}
public void next() {
if(first) {
first = false;
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
if(first) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
else {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(200, 200);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DualAppearance content = new DualAppearance();
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
This DualAppearance component follows the usual pattern. When being requested to paint itself, it will always paint itself according to the current state. This may happen multiple times without a state change, due to other reasons, e.g. being requested by the system. When its own state changes and requires a repaint, which only this component can know, it will invoke repaint.
You can easily modify this example code to toggle between both appearances by replacing
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
with
JButton button = new JButton("Toggle");
button.addActionListener(e -> content.setFirst(!content.isFirst()));

Related

paint() doesn't work after panel and frame was added

I am making kind of my paint that creates shapes, it worked fine until I added layers(panel and frame), now the shapes aren't visible on button press I assume it is under the layers?i tried using paintComponent and revalidate etc and still couldn't get it to appear
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
public boolean SquareCheck;
public boolean CircleCheck;
JButton buttonSquare;
JButton buttonCircle;
JFrame frame;
JPanel panel;
public void paint(Graphics g) {
if (SquareCheck) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
} else if (CircleCheck) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public void start() {
frame = new JFrame();
panel = new JPanel();
//setLayout(new BorderLayout());
buttonSquare = new JButton("■");
buttonCircle = new JButton("●");
buttonSquare.setPreferredSize(new Dimension(200, 20));
buttonCircle.setPreferredSize(new Dimension(200, 20));
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
//add(buttonCircle, BorderLayout.NORTH);
//add(buttonSquare, BorderLayout.SOUTH);
JToggleButton red = new JToggleButton();
panel.add(buttonCircle);
panel.add(buttonSquare);
frame.add(panel);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonSquare) {
SquareCheck = true;
} else if (e.getSource() == buttonCircle) {
CircleCheck = true;
}
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
Main2 x = new Main2();
x.start();
}
}
Basiclly the buttons changes the boolean then the repaint is called and based on the boolean it draws either a cirlce or a square,the code worked before adding frame and panel
Your paint method is a method of the Main2 class, but you never add a Main2 instance to the JFrame or to any component that goes into the JFrame, and so the Main2 instance will never be displayed, and the Swing painting manager will never call its paint method.
For starters, get rid of this variable, panel = new JPanel(); and every place you use panel, substitute this. This way you'll be working with a correct Main2 instance and adding it to the GUI.
Other issues:
You need to call the super's equivalent painting method in your override on its first line
Override paintComponent, not paint, and yes call super.paintComponenet(g); in this override
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
For safety's sake, add the #Override annotation above any method that you think may be overriding a parent method (such as paint), to make sure that you are doing it correctly.
For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
#SuppressWarnings("serial")
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
private JToggleButton buttonSquare;
private JToggleButton buttonCircle;
JFrame frame;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buttonSquare.isSelected()) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
}
if (buttonCircle.isSelected()) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public Main2() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buttonSquare = new JToggleButton("■");
buttonCircle = new JToggleButton("●");
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
this.add(buttonCircle);
this.add(buttonSquare);
frame.add(this);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
new Main2();
}
}
class MyShape {
public int x, y, width, length;
public MyShape(int x, int y, int width, int length) {
this.x = x;
this.y = y;
this.width = width;
this.length = length;
}
}

How do I draw on a JPanel from multiple outside classes?

I am currently making a game with a main menu and a world where you actually play.
I have a class called Game, which inherits from JPanel and implements the Runnable, MouseListener, KeyListener and ActionListener interfaces
(only important parts included)
I also have two classes InWorldHandler and OutWorldHandler for handling the mechanics in the world and outside of it respectively.
The Game class:
public class Game extends JPanel implements Runnable, KeyListener, MouseListener, ActionListener
{
protected JFrame frame;
private Timer timer = new Timer(25, this);
private World world;
private Player player = new Player();
private boolean draw;
Game(World world)
{
frame = new JFrame("Minecraft 2D");
this.world = world;
player.enterWorld(world, this);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
if(draw)
{
g2d.setColor(new Color(255, 255, 255));
g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());
draw = false;
//Here, the in-game mechanics should be handled
if(player.inWorld())
{
Chunk chunk = player.getLoadedChunk();
for(int x = 0; x < 16; x++)
{
for(int y = 0; y < 16; y++)
{
Block block = chunk.getBlockAt(x, y);
BufferedImage texture = block.getTexture();
g2d.drawImage(block.getTexture(), x*32, y*32, texture.getWidth()*2, texture.getHeight()*2, null);
}
}
}
//Here, the out-game mechanics should be handled
else
{
}
}
}
#Override
public void actionPerformed(ActionEvent e)
{
this.repaint();
draw = true;
}
#Override
public void run()
{
draw = true;
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this);
frame.setMinimumSize(new Dimension(518, 540));
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.addKeyListener(this);
frame.addMouseListener(this);
frame.setFocusable(true);
frame.setVisible(true);
frame.pack();
timer.start();
}
}
Both the other classes have empty bodies currently. I just have no idea how to do that.
I want the InWorldHandler class to draw on the panel when the player is in game, and the OutWorldHandler class when the player is in the main menu, both called in the Game class. How do I do that?
Instead of having a single JPanel, why don't you try with a CardLayout and switch whether to show the InnerWorld or the OuterWorld according to a flag that determines where in the program you're at.
As you're implementing KeyListener, I think that's for you to be able to move your character, please take a look at the accepted answer on this question: Keylistener not working for JPanel and use KeyBindings instead.
Also avoid the use of setMimimum/Maximum/PreferredSize() and override those methods instead: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Take this code as an example:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Game {
private JFrame frame;
private JButton button;
private boolean status;
private JPanel[] cards;
private CardLayout cl;
private JPanel gamePane;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Game().createAndShowGUI());
}
private void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
button = new JButton("Switch Worlds");
status = true; //True = Inner / False = Outer
cl = new CardLayout();
gamePane = new JPanel(cl);
cards = new JPanel[2];
cards[0] = new InnerWorld();
cards[1] = new OuterWorld();
gamePane.add(cards[0], "innerWorld");
gamePane.add(cards[1], "outerWorld");
button.addActionListener(e -> {
status = !status;
if (status) {
cl.show(gamePane, "innerWorld");
} else {
cl.show(gamePane, "outerWorld");
}
});
frame.add(gamePane);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
#SuppressWarnings("serial")
class InnerWorld extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawString("Inner World", 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
#SuppressWarnings("serial")
class OuterWorld extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawString("Outer World", 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}

Gui graphic does not appear in Panel

I am trying to make a drawOval moving by using the two buttons that I set to be North and East so the ball will move between the JButtons, at the center.
Why does not appear at the panel?
Also I am thinking using a function that make this x=x+; and y=y+1 when I pressed left or right.
I do not figure out what can I do.
So this is the code I made:
public class Main extends JFrame implements ActionListener {
JButton left;
JButton right;
JPanel p;
Main(){
JButton left = new JButton("left");
left.addActionListener(this);
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
//The first way I think is better to make it move.
}
});
JButton right = new JButton("right");
right.addActionListener(this);
Panel p = new Panel();
p.setLayout(new BorderLayout());
p.add("West",left);// to the left
p.add("East",right);//to the right
Container c = getContentPane();
c.add(p);
}
public static void main(String[] args) {
Main f=new Main();
f.setTitle("Heracles");
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true); //this is the window
}
public void paintComponent (Graphics g) {
super.paintComponents(g);
Graphics2D g1=(Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
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);
}
}
Why does not appear at the panel?
To display graphic you created, use follow these steps,
Remove paintComponent method and replace it with below code..
public JComponent createOvel() {
return new JComponent() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g1 = (Graphics2D) g;
g.drawOval(3, 5, 45, 46); // The ball
g.fillOval(20, 30, 40, 40);
}
};
}
Then call it in Main() constructor,
p.add("Center", createOvel());
This will display the graphic you created.

Painting inside Swing Timer not working

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.

custom JPanel not updating

the code:
import java.awt.BorderLayout;
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;
public class PaintWindow {
private JFrame frame;
private aJPanel panel;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
PaintWindow window = new PaintWindow();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public PaintWindow() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new aJPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
panel.stam();
}
});
frame.getContentPane().add(btnNewButton, BorderLayout.NORTH);
frame.setBounds(100, 100, 450, 300);
frame.setVisible(true);
}
public class aJPanel extends JPanel {
private static final long serialVersionUID = 8874943072526915834L;
private Graphics g;
public aJPanel() {
super();
System.out.println("Constructor");
}
public void paint(Graphics g) {
super.paint(g);
System.out.println("paint");
g.fillRect(10, 10, 10, 10);
this.g = g;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paintComponent");
g.fillRect(20, 20, 20, 20);
this.g = g;
}
public void stam() {
System.out.println("stam");
this.g.fillRect(40, 40, 40, 40);
this.repaint();
}
}
}
what I want to do is paint custom shapes (lines, rectangles etc) after the user clicks a button or triggers a mouse event. but calling aJPanel.stam() does not show the rectangle its supposed to. I am fairly new to JPanel. any suggestions?
First of all:
class names should start with a capital letter
class names should be descriptive.
"aJPanel" does not follow either of the above.
but calling aJPanel.stam() does not show the rectangle its supposed to
See Custom Painting Apporoaches for the two common ways to do dynamic painting:
Add objects to a List and iterate the List to paint all the objects
Paint directly to a BufferedImage and then paint the BufferedImage
The examples add the Rectangle by dragging the mouse, but you can easily add Rectangles by just invoking the addRectangle(...) method when you click a button.

Categories