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.
Related
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.
I'm having a problem getting particular objects to appear in my frame/panels. In the code below I have objects of type "Drawing" such as light, red, yellow, and green. The program is supposed to create a traffic light but the other drawing don't appear and I'm not sure why. light is separate from the rest so that it won't be affected if the background draws over it, but this isn't the issue. The circles/lights don't draw and I don't see what I'm missing or what I'm doing wrong.
What the frame should look like
package lab8;
import oracle.jvm.hotspot.jfr.JFR;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
enum shape{
circle,square;
}
public class TrafficLight2 extends JFrame {
JPanel panel = new JPanel();
JFrame frame = new JFrame();
TrafficLight2(){
frame.setPreferredSize(new Dimension(500, 500));
frame.setLayout(new FlowLayout());
panel.setLayout(new FlowLayout());
TrafficLight t = new TrafficLight();
t.setPreferredSize(new Dimension(500,500));
panel.setPreferredSize(new Dimension(500,500));
panel.add(t,BorderLayout.CENTER);
Drawing light = new Drawing();
light.colour=Color.RED;
light.s=shape.circle;
repaint();
panel.add(light);
frame.add(light);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new TrafficLight2();
}
class ButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
ButtonListener(){
}
}
class Drawing extends JPanel{
int width=50;
int height=50;
shape s;
Color colour;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int verticalCenter = getParent().getHeight()/2;
int horizontalCenter = getParent().getWidth()/2;
int topLeftSquareCornerY = verticalCenter - (height/2);
int topLeftSquareCornerX = horizontalCenter - (width/2);
g.setColor(colour);
if (s==shape.square) {
g.fillRect(topLeftSquareCornerX, topLeftSquareCornerY, width, height);
}
else{
g.fillOval(topLeftSquareCornerX,topLeftSquareCornerY,width,height);
}
}
}
class TrafficLight extends Drawing{
Drawing red = new Drawing();
Drawing yellow = new Drawing();
Drawing green = new Drawing();
Drawing background = new Drawing();
TrafficLight(){
this.red.s=shape.circle;
this.red.colour=Color.RED;
this.yellow.s=shape.circle;
this.yellow.colour=Color.YELLOW;
this.green.s=shape.circle;
this.green.colour=Color.GREEN;
this.background.s=shape.square;
this.background.colour=Color.BLACK;
this.background.width=100;
this.background.height=300;
this.s=shape.square;
this.colour=Color.BLACK;
this.width=100;
this.height=300;
background.add(red,BorderLayout.NORTH);
background.add(yellow,BorderLayout.CENTER);
background.add(green,BorderLayout.SOUTH);
this.add(background)
repaint();
}
}
}
You forgot to add background to TrafficLight.
TrafficLight(){
.... other stuff
this.add(background);
}
Also, I find it odd that TrafficLight2 extends JFrame, but is not actually used...
I am writing what should be a simple piece of code that creates a JFrame object and then paints the background black and draws a blue square. However, the repaint() method is not calling the paintComponent() method.
This is the code:
import java.util.Vector;
import javax.swing.JPanel;
import javax.swing.JFrame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Container;
public class Snake extends JPanel{
private Vector xCoords = new Vector();
private Vector yCoords = new Vector();
public Snake(){
xCoords.add(150);
yCoords.add(150);
}
public void startJFrame(){
JFrame window = new JFrame();
window.setSize(300, 300);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = window.getContentPane();
c.setBackground(Color.black);
}
public void paintRequest(){
System.out.println("Request to paint received.");
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println("paintComponent was called");
g.setColor(Color.blue);
int x = (int)xCoords.get(0);
int y = (int)yCoords.get(0);
g.fillRect(x, y, 10, 10);
}
public static void main(String[] args){
Snake mkFrame = new Snake();
mkFrame.startJFrame();
mkFrame.paintRequest();
}
}
I know that paintRequest() is being called properly because it prints "Request to paint received", but "paintComponent was called" is never printed. On the gui side, the JFrame window is created and it has a black background, but there is no blue square. Thank you in advance for the help.
There is no Snake ever added to the frame!
Change:
JFrame window = new JFrame();
To:
JFrame window = new JFrame();
window.add(new Snake());
I want to draw a disc that changes colors twice a second. The disk is drawn on a DrawPanel which extends a JPanel and in the main method the DrawPanel is added to a frame.
For the colorchanging I use a timer which works when I'm trying to change the background of the DrawPanel in the main method (what i commented out).
Can someone tell me why it doesn't work for the Graphics g object or any other suggestions?
I just copied the code from the main method and added it into the paintComponent() method, but here it doesn't work.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class DrawPanel extends JPanel{
public GridBagLayout gbl;
//position and dimension
int x = 0, y = 0, width = 200, height = 200;
public DrawPanel(){
repaint();
}
public DrawPanel(GridBagLayout gridBagLayout) {
this.gbl = gridBagLayout;
}
public void paintComponent(Graphics g){
//Overwriting of old picture
super.paintComponent(g);
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Random gen = new Random();
Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
//Draw color disk
g.setColor(color);
g.fillArc(x, y, width, height, 0, 360);
}
};
Timer t = new Timer(500, action);
t.setRepeats(true);
t.setInitialDelay(0);
t.start();
//Draw boundary of circle
g.setColor(Color.BLACK);
g.drawArc(x, y, width, height, 0, 360);
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setSize(300, 300);
final DrawPanel panel = new DrawPanel();
panel.setOpaque(true);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// ActionListener action = new ActionListener() {
// #Override
// public void actionPerformed(ActionEvent e) {
// Random gen = new Random();
// Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
// panel.setBackground(color);
// }
// };
//
// Timer t = new Timer(500, action);
// t.setRepeats(true);
// t.setInitialDelay(0);
// t.start();
}
}
The Graphics object is transient, so you should not cache it even if the compiler allows that. Instead establish the timer in the constructor of the class, set the BG of the panel, then call for a repaint. E.G.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class DrawPanel extends JPanel {
Random gen = new Random();
//position and dimension
int x = 0, y = 0, width = 200, height = 200;
Color drawColor = Color.BLACK;
public DrawPanel() {
repaint();
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
//Draw color disk
drawColor = color;
DrawPanel.this.repaint();
}
};
Timer t = new Timer(500, action);
t.setRepeats(true);
t.setInitialDelay(0);
t.start();
}
#Override
public void paintComponent(Graphics g) {
//Overwriting of old picture
super.paintComponent(g);
//Draw boundary of circle
g.setColor(drawColor);
g.drawArc(x, y, width, height, 0, 360);
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setSize(300, 300);
final DrawPanel panel = new DrawPanel();
panel.setOpaque(true);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Graphics objects are only valid for that one draw
It would be better to instead to tell the JPanel to change it's current color (with a variable) and then tell it to repaint
Add variable discColor to your JPanel
Change your drawing code to not use timers and instead make it simple, just draw the disc based off of discColor
With a timer, update the discColor variable and then call the panel's repaint() method
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