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
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;
}
}
}
I am trying to make a simple game using JPanel.
I am using Graphics Draw to display all information, including text, but I need to add user input.
I was thinking about using JTextField with absolute positioning to make it work with what is being drawn, but I have heard that absolute positioning is not a good way set up a JPanel.
Is there a better way to use both graphics paint and JComponents in the same panel?
Solution: use layout managers
Why not simply have the drawing JPanel held by another JPanel, one that uses BorderLayout and held in the BorderLayout.CENTER position. You can then place JTextFields or other control components in the outer JPanel in other positions.
You can also add a layout manager to the drawing JPanel and then add components onto this using the layout. Just remember that if you add any JPanels on top of the drawing JPanel, the added JPanels should be transparent, that is myPanel.setOpaque(false) should be called on them so that the drawing underneath shows through.
For example -- run this program to see what I mean:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
private JSlider hue1Slider = new JSlider(0, 100, 0);
private JSlider hue2Slider = new JSlider(0, 100, 0);
public GradientPaintEg() {
Color color = drawingPanel.getColor1();
float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
int value = (int) (hsb[0] * 100);
hue1Slider.setValue(value);
color = drawingPanel.getColor2();
hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
value = (int) (hsb[0] * 100);
hue2Slider.setValue(value);
hue1Slider.setMajorTickSpacing(20);
hue1Slider.setMinorTickSpacing(5);
hue1Slider.setPaintTicks(true);
hue1Slider.setPaintLabels(true);
hue1Slider.setPaintTrack(true);
hue1Slider.addChangeListener(new SliderListener(hue1Slider, drawingPanel, true));
hue1Slider.setOpaque(false);
hue2Slider.setMajorTickSpacing(20);
hue2Slider.setMinorTickSpacing(5);
hue2Slider.setPaintTicks(true);
hue2Slider.setPaintLabels(true);
hue2Slider.setPaintTrack(true);
hue2Slider.addChangeListener(new SliderListener(hue2Slider, drawingPanel, false));
hue2Slider.setOpaque(false);
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
setLayout(new BorderLayout());
// if you want to add the slider panel to the main JPanel:
// add(sliderPanel, BorderLayout.PAGE_START);
add(drawingPanel);
// if you want to add the sliderPanel to the drawing JPanel
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
}
private class SliderListener implements ChangeListener {
private JSlider slider;
private DrawingPanel drawingPanel;
private boolean color1Listener;
public SliderListener(JSlider slider, DrawingPanel drawingPanel, boolean color1Listener) {
this.slider = slider;
this.drawingPanel = drawingPanel;
this.color1Listener = color1Listener;
}
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
float hue = value / 100f;
Color c = Color.getHSBColor(hue, 1f, 1f);
if (color1Listener) {
drawingPanel.setColor1(c);
} else {
drawingPanel.setColor2(c);
}
}
}
private static void createAndShowGui() {
GradientPaintEg mainPanel = new GradientPaintEg();
JFrame frame = new JFrame("GradientPaintEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final float X2 = 20;
private static final float Y2 = X2;
private Color color1 = Color.RED;
private Color color2 = Color.BLUE;
public Color getColor1() {
return color1;
}
public void setColor1(Color color1) {
this.color1 = color1;
repaint();
}
public Color getColor2() {
return color2;
}
public void setColor2(Color color2) {
this.color2 = color2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(new GradientPaint(0, 0, color1, X2, Y2, color2, true));
g2.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
In this code example, I have a JPanel that draws, called class DrawingPanel and use within another main JPanel, the GradientPaintEg class:
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
If I want to add components to the DrawingPanel, I first give it a layout, and then add the component(s). For instance, there is a JPanel that holds JSliders called sliderPanel that I add to the DrawingPanel instance using BorderLayout:
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
This adds the sliderPanel to the top of the drawingPanel.
But also note that I have to make sliderPanel non-opaque first:
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
I've also made the JSliders themselves non-opaque so that the underlying drawing shows through:
// ......
hue1Slider.setOpaque(false);
// ......
hue2Slider.setOpaque(false);
Here is a basic mcve of combining user input in JTextfield and painting, on a panel using a layout manager:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
class DrawingPanel extends JPanel {
private final JButton update;
private final JTextField input;
private final static int W = 300, H = 350, RADIUS = 100, GAP = 50;
private String text;
public DrawingPanel() {
setPreferredSize(new Dimension(W, H));
setOpaque(false);
setLayout(new BorderLayout());
update = new JButton("Update");
update.addActionListener(e->update());
add(update, BorderLayout.PAGE_START);
input = new JTextField();
add(input, BorderLayout.PAGE_END);
text = "Enter text and press button";
}
private void update() {
text = input.getText();
input.setText("");
repaint();
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponents(g);
final int width = getWidth();
final int height = getHeight();
g.setColor(Color.RED);
g.fillOval(width/2 - RADIUS, height/2 - RADIUS, RADIUS*2, RADIUS*2);
g.setColor(Color.BLUE);
g.drawString(text, height/2 - RADIUS - GAP, GAP);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(()->makeGui());
}
private static void makeGui() {
JFrame frame = new JFrame();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new DrawingPanel());
frame.pack();
frame.setVisible(true);
}
}
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);
}
}
I am trying to make the effect of gravity but it just looks like there are growing streaks of circles instead of individual circles moving down. I do not know how to remove the circles I have already drawn. There are no errors in the code btw.
import javax.swing.Timer;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
public class Tester {
static JFrame frame;
static JPanel panel;
static JButton button;
static ArrayList<Ellipse2D.Double> circles = new ArrayList<Ellipse2D.Double>();
static void init(){
frame = new JFrame();
panel = new JPanel(new BorderLayout());
button = new JButton("South");
panel.add(button, BorderLayout.SOUTH);
frame.add(panel);
frame.setVisible(true);
panel.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setBackground(Color.LIGHT_GRAY);
}
public static void main(String[] args) {
init();
class MeteorMover extends JPanel{
Ellipse2D.Double m;
int x = 40,y=40;
boolean isSettingGravity=true;
public MeteorMover(){
m = new Ellipse2D.Double(x,y,30,30);
}
void createNewMeteor(int n){
repaint();
}
void setGravity(){
isSettingGravity = true;
for (int i=0;i<circles.size();i++){
Ellipse2D.Double m = circles.get(i);
m= new Ellipse2D.Double(m.getX(),m.getY()+1,30,30);
circles.set(i, m);
}
repaint();
}
protected void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
if (isSettingGravity){
for (Ellipse2D.Double c:circles){
g2.draw(c);
}
isSettingGravity = false;
}
else{
m = new Ellipse2D.Double(x,y,30,30);
circles.add(m);
g2.fill(m);
g2.draw(m);
Random r = new Random();
x = r.nextInt(500);
y=r.nextInt(100);
}
}
}
final MeteorMover m = new MeteorMover();
panel.add(m);
panel.repaint();
class TimerListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
m.createNewMeteor(1);
}
}
TimerListener cListener = new TimerListener();
Timer timer = new Timer(1000,cListener);
timer.start();
class TimerListener2 implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
m.setGravity();
}
}
TimerListener2 gListener = new TimerListener2();
Timer gTimer = new Timer(100,gListener);
gTimer.start();
}
}
call super.paintComponent(g);
protected void paintComponent(Graphics g) {
super.paintComponent(g);
read more about super.paintComponent .
There's no direct way to erase with graphics, you have two options:
If you always know which ellipse you need to erase, AND the ellipses never intersect, then you could keep in memory which is the next ellipse to erase and call g.setColor(bgColor); g.fill(erasedEllipse);
This is option is more reliable, You could keep an ArrayList of ellipses to draw. and you could clear all the pane and repaint all
the ellipses in the ArrayList, and if you want to erase one, you
just call ArrayList.remove(erasedElipseIndex)
For a school project I have to create a simple canvas on which I have to draw random circles. For this I am using a for loop where I kept spawning circles. To keep my PC from crashing, I used the Thread.sleep(20) method. It worked well but had one flaw: it is unable to close itself using the close button.
public class CanvasDraw extends Canvas {
private Random rand = new Random();
public void paint(Graphics graph){
setBackground(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
for(int i = 0; i<9999; i++){
graph.setColor(new Color(rand.nextInt(255),
rand.nextInt(255), rand.nextInt(255)));
graph.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
CanvasDraw drawing = new CanvasDraw();
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.getContentPane().add(drawing);
}
}
How can I fix this?
In your code you paint 10000 ovals each time paint is invoked (which can be quite often). This will clog the event dispatch thread. Paint your ovals on a BufferedImage in your main, and paint that instead. Also extend JComponent instead of Canvas and overwrite paintComponent instead of paint.
Here you go:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CircleDraw {
public static void main(String[] args) throws InterruptedException {
Random rand = new Random();
BufferedImage bufferedImage = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferedImage.getGraphics();
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
for (int i = 0; i < 9999; i++) {
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
JComponent component = new JComponent() {
#Override
protected void paintComponent(Graphics g) {
g.drawImage(bufferedImage, 0, 0, null);
}
};
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(component);
frame.setSize(bufferedImage.getWidth(), bufferedImage.getHeight());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Try running your spawning circles loop to run on separate thread.
Don't run it on MAIN thread!!
I created a simple circle animation using Java Swing. Here's what the GUI looks like.
The circle repaints every second with a different color and a different circle radius. The drawing area is 420 x 420 pixels. The color varies randomly. The circle radius varies randomly from 42 pixels to 140 pixels.
The JFrame is created in the run method of the CircleAnimation class.
The drawing panel Is created in the DrawingPanel class. All of the custom painting happens in the paintComponent method of the DrawingPanel class.
The animation is set up as a java.swing.Timer after the JFrame is created. The Animation class holds the ActionListener for the Timer. The actionPerformed method creates a new random color and circle radius.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
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.SwingUtilities;
import javax.swing.Timer;
public class CircleAnimation implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleAnimation());
}
#Override
public void run() {
JFrame frame = new JFrame("Circle Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer timer = new Timer(1000, new Animation(drawingPanel));
timer.setInitialDelay(1000);
timer.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int radius;
private Color color;
private Random random;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(420, 420));
this.random = new Random();
setRandomColorAndRadius();
}
public void setRandomColorAndRadius() {
this.color = new Color(getRandom(0, 255),
getRandom(0, 255), getRandom(0, 255));
int start = getPreferredSize().width / 10;
int limit = getPreferredSize().width / 3;
this.radius = getRandom(start, limit);
}
private int getRandom(int start, int limit) {
return random.nextInt(limit - start) + start;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() / 2 - radius;
int y = getHeight() / 2 - radius;
int width = radius + radius;
int height = width;
g.setColor(color);
g.fillOval(x, y, width, height);
}
}
public class Animation implements ActionListener {
private DrawingPanel drawingPanel;
public Animation(DrawingPanel drawingPanel) {
this.drawingPanel = drawingPanel;
}
#Override
public void actionPerformed(ActionEvent event) {
drawingPanel.setRandomColorAndRadius();
drawingPanel.repaint();
}
}
}