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.
Related
I am trying to make a simple Java program with GUI using Java Swing.
I have painting panel (gPanel) in the center of the screen, panel with buttons (buttonSet) in the west and panel with labels (labelPanel) in the east. To paint over gPanel I use paintComponent method and since I have two buttons, which are supposed to draw different things (and change label on the right of the screen), I decided to put switch case in paintComponent method for it to choose the correct actual painting method.
When I run the program everything looks fine - program uses the first method to paint and there is a sampletext.png image shown in the middle of the screen with yellow background, as it should be. Button number 1 also uses this method to draw over gPanel, so pressing it draws the same thing.
Now Button number 2 uses the second painting method and this is where things go wrong. It draws sampleimage.png over the gPanel, but also parts of left and right panels (i.e. buttons from left buttonSet panel and orange colour that is background colour of side panels) are drawn, though it shouldn't happen. Also the whole gPanel becomes gray (I think it happens because of label on the right that becomes very long after pressing Button number 2, because when the label was shorter gPanel didn't turn gray and left the previously drawn things instead).
Pressing Button number 1 paints things from first method properly, so pressing it after pressing Button number 2 "reverts" the changes.
What do I have to do to make my second painting method work properly?
Also why adding border to buttonSet and labelPanel works but adding it to gPanel doesn't?
package com.inferjus.drawingproject;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.border.*;
/**
*
* #author inferjus
*/
public class DrawingProject
{
private JFrame frame;
private graphicPanel gPanel;
private JPanel buttonSet;
private JPanel labelPanel;
private JLabel label;
private int painter=0;
public static void main(String[] args)
{
DrawingProject program=new DrawingProject();
program.prepareGUI();
}
public int getPainter()
{
return painter;
}
public void setPainter(int x)
{
painter=x;
}
public void prepareGUI()
{
//setting JFrame and icon
frame=new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
try { frame.setIconImage(ImageIO.read(getClass().getResource("/resources/sampleicon.png")));}
catch (IOException e) { e.printStackTrace(); }
//border for components
Border bigBlackBorder=new LineBorder(Color.black, 3);
//setting JPanel (graphicPanel) for drawing images
gPanel=new graphicPanel();
gPanel.setBorder(bigBlackBorder); // <--- why it does not work?
//setting JPanel for buttons on the left of the screen
buttonSet=new JPanel();
buttonSet.setLayout(new BoxLayout(buttonSet, BoxLayout.Y_AXIS));
buttonSet.setBorder(bigBlackBorder);
//setting JButtons
JButton buttonOne=new JButton("Button number 1");
buttonOne.addActionListener(new buttonOneListener());
buttonSet.add(buttonOne);
buttonSet.setBackground(Color.orange);
JButton buttonTwo=new JButton("Button number 2");
buttonTwo.addActionListener(new buttonTwoListener());
buttonSet.add(buttonTwo);
//setting JLabels on the right of the screen
label=new JLabel("Default label");
label.setFont(new Font("Consolas", Font.PLAIN, 20));
labelPanel=new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
labelPanel.setBackground(Color.orange);
labelPanel.setBorder(bigBlackBorder);
JLabel popeLabelTitle=new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font("Consolas", Font.BOLD, 24));
//adding JLabels to labelPanel
labelPanel.add(BorderLayout.NORTH, popeLabelTitle);
labelPanel.add(BorderLayout.CENTER, label);
//adding components to JFrame
frame.getContentPane().add(BorderLayout.CENTER, gPanel);
frame.getContentPane().add(BorderLayout.EAST, labelPanel);
frame.getContentPane().add(BorderLayout.WEST, buttonSet);
frame.setVisible(true);
}
class graphicPanel extends JPanel
{
private BufferedImage sampletext=null;
private BufferedImage sampleimage=null;
#Override
public void paintComponent(Graphics g)
{
//for Button One paint sampletext.png, for Button Two paint sampleimage.png
switch (painter)
{
case 0:
paintSampletext(g);
break;
case 1:
paintSampleimage(g);
break;
}
}
//paint yellow background and put sampletext.png in the middle
private void paintSampletext(Graphics g)
{
if (sampletext==null)
{
gPanel.setSampletextPNG();
}
g.setColor(Color.yellow);
g.fillRect(0,0, gPanel.getWidth(), gPanel.getHeight());
g.drawImage(sampletext, gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, this);
g.setColor(Color.black);
g.drawRect(gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, sampletext.getWidth(), sampletext.getHeight());
g.dispose();
}
//paint sampleimage.png over what is already displayed
private void paintSampleimage(Graphics g)
{
if (sampleimage==null)
{
gPanel.setSampleimagePNG();
}
int x=(int)((Math.random()*gPanel.getWidth())-sampleimage.getWidth());
int y=(int)((Math.random()*gPanel.getHeight())-sampleimage.getHeight());
g.drawImage(sampleimage, x, y, gPanel);
g.dispose();
}
public void setSampletextPNG()
{
try { sampletext=ImageIO.read(getClass().getResource("/resources/sampletext.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
public void setSampleimagePNG()
{
try { sampleimage=ImageIO.read(getClass().getResource("/resources/sampleimage.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
}
class buttonOneListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 1: change of label.");
setPainter(0);
gPanel.repaint();
}
}
class buttonTwoListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 2: change of label + drawing images over gPanel.");
setPainter(1);
gPanel.repaint();
}
}
}
Tree of my project:
DrawingProject
-JRE System Library
-src
--com.inferjus.drawingproject
---DrawingProject.java
--resources
---sampleicon.png
---sampleimage.png
---sampletext.png
what shows after running the program by default or after pressing Button One
what shows after pressing Button Two one time
what shows after pressing Button Two a few times
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
I went ahead and created the following GUI. I created two BufferedImages for the text image and the plain image so I wouldn't have to read any external files.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
Model
I created a model class to hold the button flag and the two BufferedImages. This is the class where you would read the resources.
You can add the JFrame icon back to this class.
Model classes are plain Java getter/setter classes.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
Class names are written in camel case and start with an upper case character. Method names are written in camel case and start with a lower case character. Field names follow the same rules as method names.
I separated the creation of the JFrame from the creation of the JPanels. This helps me to separate my concerns and makes it much easier to visually verify whether or not the code is correct. Aim to write short methods that do one thing and do it well.
You have to manually draw a border on a graphic JPanel. I added the code to your paintComponent method to paint a partial border.
Your paintComponent method should paint. Period. Nothing else. It must also start with a call to the super.paintComponent method to maintain the Swing paint chain.
I changed your JLabel in the right JPanel to a JTextArea. A JTextArea allows for longer messages to word wrap on multiple lines and not make your JFrame change size.
Controller
Your JButton controller classes were fine, except for the class names.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code in one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint border
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
switch (model.getPainter()) {
case 0:
paintSampleText(g);
break;
case 1:
paintSampleImage(g);
break;
}
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void paintSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) ((Math.random() * getWidth()) - image.getWidth());
int y = (int) ((Math.random() * getHeight()) - image.getHeight());
g.drawImage(image, x, y, this);
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
}
}
Update
In order to paint multiple images, you have to save the origin of the images in a List. I've modified the application model to hold a List of origin Point instances. I also corrected the code to create a random point.
Here's the GUI with multiple images.
Here's the modified code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintMyBorder(g);
if (model.getPainter() == 1) {
createSampleImage(g);
}
paintSampleText(g);
BufferedImage image = model.getSampleImage();
List<Point> origin = model.getImageOrigin();
for (Point point : origin) {
g.drawImage(image, point.x, point.y, this);
}
}
private void paintMyBorder(Graphics g) {
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void createSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) (Math.random() * (getWidth() - image.getWidth()));
int y = (int) (Math.random() * (getHeight() - image.getHeight()));
model.addNewImageOrigin(new Point(x, y));
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
private final List<Point> imageOrigin;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
this.imageOrigin = new ArrayList<>();
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public void addNewImageOrigin(Point point) {
this.imageOrigin.add(point);
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
public List<Point> getImageOrigin() {
return imageOrigin;
}
}
}
It's my first post on StackOverflow. I'm a beginner in Java and I'm reading Head First Java recently. I have searched google for many times but I still can't find an answer to fix my doubt.
On chapter 12, I copy the code to Eclipse. My codes are executable, but after I click the button to change color of the circle, there's no any circle showed on the window. And another class "SimpleAnimation" has the same problem too. There is no any circle on the window. It has bothered me for two days. Please help this poor kid(TAT). Thanks!
Run TwoButtons
Here are the codes.
This is class TwoButtons:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
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 circle");
colorButton.addActionListener(new ColorListener());
label = new JLabel("I'm a label");
MyDrawPanel drawPanel = new MyDrawPanel();
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(500, 500);
frame.setVisible(true);
}
class LabelListener implements ActionListener{
public void actionPerformed(ActionEvent event) {
label.setText("Ouch!");
}
}
class ColorListener implements ActionListener{
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
}
This is class MydrawPanel:
import javax.swing.JPanel;
import java.awt.*;
public class MyDrawPanel extends JPanel {
public void paintConponent(Graphics 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);
}
}
Run SimpleAnimation
This is class SimpleAnimation:
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) {
}
}
}
class MyDrawPanel extends JPanel{
public void paintConponent(Graphics g) {
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
}
}
}
Thanks again!
For starters:
public void paintConponent(Graphics g) {
Should be:
#Override
public void paintComponent(Graphics g) {
Always use #Override notation when changing methods, to ensure the method name is spelled correctly and uses the correct method arguments. Or to put that another way, use the compiler flag to check that code is actually overriding a parent method, rather than defining a new one!
Other tips
Any custom painted component should:
Call the super method before custom painting, to ensure that older paints are erased & the BG color (etc.) of the component is painted.
Override the getPreferredSize() method to provide a hint to the layout manager.
You have not overridden the paintComponent method. There is a typo in the name. Please note the M in paintComponent. Your method, in each case, is named paintConponent.
The compiler will warn you if you try to override a method from a superclass but get the method signature wrong (the name or the number and types of parameters) if you use the #Override annotation on your method:
#Override
public void paintComponent(Graphics g) {
// . . .
}
You're missing the annulation override
import javax.swing.JPanel;
import java.awt.*;
public class MyDrawPanel extends JPanel {
#Override
protected 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);
this.setVisible(true);
}
}
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.
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.
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