Java Button Triggers Multiple Events - java

I'm learning some basic Java working on a task using inner classes to create a two button gui. One button changes the colour of a drawn circle and one changes the text of a label. The problem I'm having is that when I click the change label button the circle colour changes as well (on the first click, nothing happens thereafter). If I click the change colour button it operates as expected changing only the circle colour on each click. Basically I would like to try and understand why this happens.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TwoButtons {
JFrame frame;
JLabel label;
public static void main (String[] args) {
TwoButtons gui = new TwoButtons ();
gui.go();
}
public void go () {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton labelButton = new JButton("Change Label");
labelButton.addActionListener(new LabelListener());
JButton colorButton = new JButton("Change Colour");
colorButton.addActionListener(new ColorListener());
label = new JLabel("I'm a label");
MyDrawPanel drawPanel = new MyDrawPanel();
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.fillRect(0,0,this.getWidth(), this.getHeight());
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color randomColor = new Color(red, green, blue);
g.setColor(randomColor);
g.fillOval(140,140,300,300);
}
}
frame.getContentPane().add(BorderLayout.SOUTH, colorButton);
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
frame.getContentPane().add(BorderLayout.EAST, labelButton);
frame.getContentPane().add(BorderLayout.WEST, label);
frame.setSize(600, 600);
frame.setVisible(true);
}
class LabelListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
label.setText("Boom!");
}
}
class ColorListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
}

I guess whenever a Component of a JFrame is changed, the Frame repaints automatically. Because after you click the label button for the first time the text doesn't change anymore, repaint isn't automatically called after clicking the label button.

In you paintComponent method you get new value of color component each time.
Problem is that this method can be invoked for lots of reason not only when you invoke repaint.
I suggest you get field Color randomColor in your TwoButtons class and move code
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
randomColor = new Color(red, green, blue);
before frame.repaint(); in ColorListener

Related

JAVA swing gui window gets jarred after repaint method is run

I'm beginning to learn Java Swing. I was trying to create a GUI in which there are 2 buttons, changeColor on the bottom and changeLabel on right. It has a label on right and at the center a JPanel which shows a gradient colored oval.
When I click on changeLabel, it works fine and changes the label on left. But when I click on changeColor, a new oval appears and the whole layout breaks, with some new panels superimposing. I am following a book in which the same thing is given, but it uses random color generation in the paintComponent method, which I learned from here, isn't a good thing to do. That method works fine, but I tried to avoid that and make a separate method. This is not working though.
GUI class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TwoButtons {
public JFrame frame;
private JLabel label;
private MyDrawPanel panel;
private boolean clicked = false;
public static void main(String[] args) {
TwoButtons gui = new TwoButtons();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton labelButton = new JButton("Change Label");
labelButton.addActionListener(new LabelListener());
JButton colorButton = new JButton("Change color");
colorButton.addActionListener(new ColorListener());
label = new JLabel("I'm a label");
panel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.SOUTH, colorButton);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.getContentPane().add(BorderLayout.EAST, labelButton);
frame.getContentPane().add(BorderLayout.WEST, label);
frame.setSize(300, 300);
frame.setVisible(true);
}
class LabelListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (!clicked) {
label.setText("Ouch!! (Click again to revert)");
clicked = true;
} else {
clicked = false;
label.setText("Change Label");
}
}
}
class ColorListener implements ActionListener {
#Override//
public void actionPerformed(ActionEvent e) {
//frame.repaint();
panel.changeColors();
}
}
}
Coloring class:
import javax.swing.*;
import java.awt.*;
public class MyDrawPanel extends JPanel{
private Color startColor,endColor;
public MyDrawPanel(){
this.changeColors();
}
public void paintComponent(Graphics g){
Graphics2D g2D=(Graphics2D)g;
GradientPaint gradient=new GradientPaint(70,70,startColor,150,150,endColor);
g2D.setPaint(gradient);
g2D.fillOval(70,70,100,100);
}
public void changeColors(){
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
startColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
endColor = new Color(red, green, blue);
this.repaint();
}
}
Before clicking change color
After clicking on change color
To avoid rendering artifacts:
public void paintComponent(Graphics g){ ..
Should be:
public void paintComponent(Graphics g){
super.paintComponent(g); ..
By calling the super method, it will automatically repaint the background and borders etc., thus erasing the earlier drawing.

Java swing - Change color of a label containing icon using buttons

I am trying to change the color of an icon using 3 different JButtons, each for a different color.
The icon is a circle that starts out as red default. When the user clicks the button called "blue" the circle changes color to blue, same idea for "green".
To update this icon color, i am supposed to use the repaint() method.
Here is my method to create the red circle icon.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class ColorIcon implements Icon
{
private int width;
private Color color;
private ChangeColor c;
public ColorIcon(int aWidth, ChangeColor c)
{
this.c = c;
width = aWidth;
color = Color.RED;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return width / 2;
}
public void setColor(Color c)
{
color = c;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
color = c.color;
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double ellipse = new Ellipse2D.Double(x,y, width, width);
g2.setColor( color );
g2.fill( ellipse );
}
}
And here is the class i use to test it. This class is the one with the event handler that creates the buttons that when pressed should change the color of the icon.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestColorIcon{
public java.awt.Color color = java.awt.Color.RED;
public Component createComponents()
{
JButton buttonRed = new JButton("Red");
JButton buttonBlue = new JButton("Blue");
JButton buttonGreen = new JButton("Green");
final ColorIcon icon = new ColorIcon( 20);
final JLabel label = new JLabel( icon );
JPanel panel = new JPanel();
panel.setLayout( new GridLayout(0, 3) );
panel.add( buttonRed );
panel.add( buttonBlue );
panel.add( buttonGreen );
panel.add( label );
buttonRed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
color = Color.RED;
label.repaint();
}
});
buttonBlue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
color = Color.BLUE;
label.repaint();
}
});
buttonGreen.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
color = Color.GREEN;
label.repaint();
}
});
return panel;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
ChangeColor changeColor = new ChangeColor();
Component content = changeColor.createComponents();
frame.getContentPane().add( content );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.show();
}
}
When I run this, i get an error saying compiler cannot resolve class ColorIcon.
I am not sure how to fix this error and get the desired output.
The code has gotten a bit long so i am unable to pinpoint the exact reason for the error, so any help is appreciated.
You never seem to change the color of the ColorIcon itself
buttonRed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
color = Color.RED;
label.repaint();
}
});
Instead, make sure you are applying the color to the icon as well.
buttonRed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
color = Color.RED;
icon.setColor(color);
label.repaint();
}
});
Icon should not be relying on any other information other then what it's provide for itself, so instead of trying to get the color from yet another source, simply use the support that the ColorIcon already has
When I run this, i get an error saying compiler cannot resolve class
ColorIcon.
Your constructor of the ColorIcon class is
public ColorIcon(int aWidth, ChangeColor c)
It expects one int and one ChnageColor arguments.
And you are calling it with wrong parameters.
final ColorIcon icon = new ColorIcon(20);

but why swing does not paint my component?

I have a working version of the simplest java gui with a button and a circle which works fine:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
//a gui element shares its events only with classes that implement Actionlistener interface
public class SimpleGui1 implements ActionListener {
JButton button;
JFrame frame;
ppanel mypanel;
public static void main (String[] args) {
SimpleGui1 mywindow = new SimpleGui1();
mywindow.renderWindow();
}
public void renderWindow(){
frame = new JFrame();
button = new JButton("click me");
mypanel = new ppanel();
//register my interest to catch button events
button.addActionListener(this);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, mypanel);
frame.setSize(300,300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
//button will call this method when clicked (its the callback)
public void actionPerformed(ActionEvent event)
{
frame.repaint();
button.setText("Clicked!!");
}
}
//i need this to override paintComponent
public class ppanel extends JPanel {
//draw something silly
public void paintComponent(Graphics g) {
//super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color startColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
Color endColor = new Color(red, green, blue);
GradientPaint gradient = new GradientPaint(70,70,startColor, 150,150, endColor);
g2d.setPaint(gradient);
g2d.fillOval(70,70,100,100);
}
}
And then, just for fun, i tried to aggregate the two classes to one like this:
public class SimpleGui1 extends JPanel implements ActionListener {
private JButton button;
private JFrame frame;
private JPanel mypanel;
public static void main (String[] args) {
SimpleGui1 mywindow = new SimpleGui1();
mywindow.renderWindow();
}
public void renderWindow(){
frame = new JFrame();
button = new JButton("click me");
mypanel = new JPanel();
frame.setSize(300,300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// register my interest to catch button events
button.addActionListener(this);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, mypanel);
//without this i see only the button
//frame.add(this);
}
//button will call this method when clicked (its the callback)
public void actionPerformed(ActionEvent event)
{
frame.repaint();
button.setText("Clicked!!");
}
public void paintComponent(Graphics g) {
//super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color startColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
Color endColor = new Color(red, green, blue);
GradientPaint gradient = new GradientPaint(70,70,startColor, 150,150, endColor);
g2d.setPaint(gradient);
g2d.fillOval(70,70,100,100);
}
}
The new class compiles successfully but at run time it draws only the button. For some reason paintComponent is not called and the only way to work correctly is to add the following in renderwindow():
frame.add(this);
my question is why this behaviour ... why do i have to add explicitly
the object to my frame for this version to work properly?
Tried repaint() and validate() almost everywhere. Does not change much
Also i know that i shouldnt be drawing things off EDT and a version with inner classes also alleviates the problem
Indeed, you have to explicitly add the this object to the content pane. I compiled and tested the following:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
//a gui element shares its events only with classes that implement Actionlistener interface
public class SimpleGui1 extends JPanel implements ActionListener {
JButton button;
JFrame frame;
ppanel mypanel;
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color startColor = new Color(red, green, blue);
red = (int) (Math.random() * 255);
green = (int) (Math.random() * 255);
blue = (int) (Math.random() * 255);
Color endColor = new Color(red, green, blue);
GradientPaint gradient = new GradientPaint(70,70,startColor, 150,150, endColor);
g2d.setPaint(gradient);
g2d.fillOval(70,70,100,100);
}
public static void main (String[] args) {
SimpleGui1 mywindow = new SimpleGui1();
mywindow.renderWindow();
}
public void renderWindow(){
frame = new JFrame();
button = new JButton("click me");
//register my interest to catch button events
button.addActionListener(this);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, this);
frame.setSize(300,300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
//button will call this method when clicked (its the callback)
public void actionPerformed(ActionEvent event)
{
frame.repaint();
button.setText("Clicked!!");
}
}
You need to explicitly add the components in order for this to be painted, it doesn't matter that the component is the object itself!
It's likely that this is your problem:
public void renderWindow(){
//where everything is the same except ofcourse:
mypanel = new JPanel();
}
Because the mypanel is of type JPanel you are adding a generic panel to the frame and not the object with your overrides.
Try:
public void renderWindow(){
//where everything is the same except ofcourse:
mypanel = new SimpleGui1(); // or possibly 'this'
}
This way your SimpleGui1 class will be added to the frame and can participate in the Swing message queue.
To answer your question about 'why', it is because when you attach a panel to the frame the panel has to have the code to handle messages and overrides. When you include a JPanel object you don't get any of the custom behavior. To tell the frame that it should send messages to your class you have to tell the frame about, essentially registering it with the frame.

Paint component method called multiple times whilst maximising window

So I have created a GUI in which a user clicks a JButton to change the color of a circle... I have used the paintComponent method which I am aware will be called when the GUI is displayed and when the GUI window is minimised and then re-opened.
However when I maximise my window on mac the paintComponent method is called several times and the circle cycles through many different colours, why does this occur, as in why is the paintComponent method called multiple times.
Source Code:
GUI Class
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class Gui extends JFrame {
JPanel row1 = new JPanel();
JPanel drawingSpace = new MyDrawPanel();
JButton colourChange = new JButton("Click here to change colors");
public Gui(){
setTitle("Circle Colors");
setSize(400,650);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BorderLayout layoutMaster = new BorderLayout();
colourChange.addActionListener(new EventHandler(this));
setLayout(layoutMaster);
setLayout(layoutMaster);
row1.add(colourChange);
add(drawingSpace, BorderLayout.CENTER);
add(row1, BorderLayout.SOUTH);
setVisible(true);
}
public static void main(String[] args){
Gui createPage = new Gui();
}
}
EVENT HANDLING Class
import java.awt.event.*;
import java.awt.*;
public class EventHandler implements ActionListener {
Gui refRemote;
public EventHandler(Gui obj){
refRemote = obj;
}
public void actionPerformed(ActionEvent e1){
String buttonTitle = e1.getActionCommand();
if(buttonTitle.equals("Click here to change colors"))
{
refRemote.repaint();
}
}
}
DRAWING PANEL Class
import javax.swing.*;
import java.awt.*;
public class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g1){
Graphics2D g2D = (Graphics2D) g1;
int red = (int) (Math.random()*256);
int green = (int) (Math.random()*256);
int blue = (int) (Math.random()*256);
Color initialColor = new Color(red, green, blue);
red = (int) (Math.random()*256);
green = (int) (Math.random()*256);
blue = (int) (Math.random()*256);
Color finalColor = new Color(red, green, blue);
///GradientPaint gradient = new GradientPaint(50, 50, initialColor, 100, 100, finalColor);
g2D.setPaint(initialColor);
g2D.fillOval(100, 150, 200, 200);
}
}
Instead of changing the colour on repaint, have a specific method to change the colour that will be called from the ActionListener. When paintComponent is called it should then just use whatever the current colour is.

Buttons and Icons Within a Label - SquareIcon

I am trying to create a program with three buttons and a label with a CompositeIcon that at the start is empty.
When you click one of the buttons there will be added a square at the screen (with the described color), when another button is pressed there is gonna be added another square and so on. That means, when i press a button five times, there will be created five squres at the given colors.
If any one will read my code i will be thankful.
/*
This program creates a window with three buttons - red, green and blue - and a label with an icon.
This icon is a CompositeIcon that at the start is empty. When we press one of the three buttons, there will
be added a square at the specified color.
*/
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ResponsiveFrame extends JFrame {
static SquareIcon icon;
static int number; // this
static Color awtColor; // this
public static void main(String[] args) {
final JFrame frame = new JFrame();
final JLabel label = new JLabel();
JButton redButton = new JButton("RED");
JButton greenButton = new JButton("GREEN");
JButton blueButton = new JButton("BLUE");
/*
this is the part that i am not sure about!
not sure about the parameters.
*/
icon = new SquareIcon(number, awtColor);
redButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
icon.addIcon(new SquareIcon(20, Color.RED));
label.setIcon(icon);
frame.repaint();
frame.pack();
}
});
greenButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
icon.addIcon(new SquareIcon(20, Color.GREEN));
label.setIcon(icon);
frame.repaint();
frame.pack();
}
});
blueButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
icon.addIcon(new SquareIcon(20, Color.BLUE));
label.setIcon(icon);
frame.repaint();
frame.pack();
}
});
frame.setLayout(new FlowLayout());
frame.add(redButton);
frame.add(greenButton);
frame.add(blueButton);
frame.add(label);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
And here is the part with the SquareIcon.
import java.util.*;
import java.awt.*;
import javax.swing.*;
public class SquareIcon implements Icon {
private ArrayList<Icon> icons;
private int width;
private int height;
public SquareIcon(int number, Color awtColor) {
icons = new ArrayList<Icon>();
number = number;
awtColor = awtColor;
}
public void addIcon(Icon icon) {
icons.add(icon);
width += icon.getIconWidth();
int iconHeight = icon.getIconHeight();
if (height < iconHeight)
height = iconHeight;
}
public int getIconHeight() {
return height;
}
public int getIconWidth() {
return width;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
for (Icon icon : icons) {
icon.paintIcon(c, g, x, y);
x += icon.getIconWidth();
}
}
}
The program does compile, but when i press the buttons, nothing happens!
You create a JLabel but add it to nothing.
I suggest that you create a new JLabel each time you need one, and be sure to add it to the GUI after creating it. You will want to add it to a JPanel that is displayed in the JFrame/GUI, and will want to call revalidate() and repaint() on the JPanel after adding the label so that the panel knows to re-layout all its components, including the new one, and to re-draw itself and its children.

Categories