Drawing to a Panel from separate classes - java

I have been trying to learn how to draw to a Jpanel for a game. I want to draw to it from different classes (like a class that manages maps and a class that manages player models).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main
{
public static void main (String[] args)
{
JFrame frame = new JFrame ("Java Game");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setSize (1000, 600);
JPanel panel = new JPanel();
panel.setBackground (Color.WHITE);
Dimension size = new Dimension(1000,500);
panel.add (new Player()); // Class with paintComponent method.
panel.setPreferredSize(size);
panel.setBackground(Color.BLUE);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
}
next class
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
#SuppressWarnings ("serial")
public class Player extends JComponent
{
int x = 50;
int y = 450;
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.setColor (Color.BLACK);
g.fillRect (x, y, 50, 50);
}
}

You probably want to extend JPanel. It's already opaque, and it can handle the background color for you. Also give your panel a size like they do here, then you can do the drawing relative to the size.
Player p = new Player();
p.setBackground(Color.cyan);
frame.add(p);
frame.pack();
frame.setVisible(true);
…
public class Player extends JPanel {
private static final int X = 320;
private static final int Y = 240;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(getWidth() / 2 - 25, getHeight() / 2 - 25, 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(X, Y);
}
}

Related

Custom JComponent (A Line) Doesn't Show Up On JPanel

It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel. I've tried setting the layout manager of jpanel to null but no result. I want to use JComponents for drawing lines because I want them clickable.
Main.java file:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
class Line extends JComponent {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked");
}
});
}
}
It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel
Swing components are responsible for determining their own preferred size.
When you add a component to a panel, the layout manager will then set the size/location of the component based on the rules of the layout manager.
When you add a component to the frame you really add it to the content pane of the frame which is a Jpanel which uses a BorderLayout by default. So the component is sized to fill the space available in the frame.
panel.setLayout(null);
You then added the component to a panel with a null layout. Now you are responsible for setting the size/location of the component. If you don't the size is (0, 0) so there is nothing to paint.
You should override the getPreferredSize() method of your class to return the preferred size of the component. Then layout managers can do their job.
If you really need a null layout, then the size of the component should be set in the application code, not it the Line class itself.
But now my line has a big container that listens for any clicks,
If you want hit detection then you override the contains(...) method.
Here is a basic example implementing the above suggestions:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Line extends JComponent
{
private Line2D.Double line;
public Line( Point2D start, Point2D end)
{
line = new Line2D.Double(start, end);
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
System.out.println("mouse clicked");
}
});
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor( Color.BLUE );
g2.setStroke( new BasicStroke(2.0F) );
g2.draw( line );
}
#Override
public Dimension getPreferredSize()
{
Rectangle bounds = line.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
return new Dimension(width, height);
}
#Override
public boolean contains(int x, int y)
{
double distance = line.ptSegDist( new Point2D.Double(x, y) );
return distance < 2;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
Add custom size in Line constructor.
public Line( Point2D start, Point2D end){ ...
this.setSize(200, 200); }
Updated to fit also with painted Graph
Advice to change from JComponent to JPanel in order to see background
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setSize(300,300);
frame.add(panel);
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
frame.setVisible(true);
}
}
class Line extends JPanel {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.RED);
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
Rectangle r = g2.getClipBounds();
System.out.println(r.x+":"+r.y);
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked at "+e.getX()+":"+e.getY());
}
});
int max_x = (int) Math.max(start.getX(), end.getX());
int max_y = (int) Math.max(start.getY(), end.getY());
System.out.println("max x="+max_y+",y="+max_y);
setSize(max_x,max_y);
setVisible(true);
setBackground(Color.GREEN);
}
}
Note: Only inside_green clicks allowed !

Repaint() does not work after adding more components to JPanel in Swing, Java

I am starting with Java and want to make a simple pong game to get into the ways of displaying stuff in java. I have created 2 classes that both extend JPanel and call the repaint() function every 16.6 milliseconds. I added both to a JPanel which I added to the frame, but only the component I added first displays.
I've tried to revalidate() after repaint() in both classes and made sure that the Timer actually works and that it's actionPerformed() method is actually called.
This is the main method:
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame("Pong");
//...
JPanel mainPanel = new JPanel();
mainPanel.add(new Player());
mainPanel.add(new Ball(frameWidth/2, frameHeight/2 -40));
frame.add(mainPanel);
}
}
This is the important code for the classes (It's basically the same for both of them):
public class Player extends JPanel {
public Player(){
setPreferredSize(new Dimension(Main.frameWidth, Main.frameHeight));
setBackground(Color.BLACK);
new Timer(1000/60, new ActionListener(){
public void actionPerformed(ActionEvent e){
update();
repaint();
}
}).start();
}
//...
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(x, y, width, height);
}
}
(Of coure I left things like #Override or unneseccary functions out to shorten the code)
This code only paints the Player, altough I want it to display all the components of mainPanel.
This is an example that you can (hopefully) run. I had to split it up into 3 files, since I suck at anonymus classes:
Main.java
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args){
JFrame frame = new JFrame("Pong");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 800);
JPanel mainPanel = new JPanel();
mainPanel.add(new Player());
mainPanel.add(new Ball());
frame.add(mainPanel);
frame.setVisible(true);
}
}
///////
Player.java
//////
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Player extends JPanel{
private int x = 20, y = 300, width = 20, height = 100;
public Player(){
setPreferredSize(new Dimension(800, 800));
setBackground(Color.BLACK);
new Timer(1000/60, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
repaint();
}
}).start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(x, y, width, height);
}
}
//////
Ball.java
//////
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Ball extends JPanel{
private int x = 20, y = 300, width = 20, height = 100;
public Ball(){
setPreferredSize(new Dimension(800, 800));
setBackground(Color.BLACK);
new Timer(1000/60, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
repaint();
}
}).start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(x, y, width, height);
}
}
In method paintComponent() of class Player, you paint the exact same Rectangle each time. In order to achieve animation, each time you paint the rectangle you need to change either its size or location or both. What do you expect the Player to do? Should it move up and down along the left edge of the window? Have you seen the lesson entitled How to Use Swing Timers which is part of Oracle's Java Tutorial?
EDIT
I see now that Player hides Ball, because of the default layout manager of JPanel. The below code is essentially the code you posted but I set GridLayout as the layout manager for mainPanel. Also, a java source code file may contain more than one class but exactly one class must be public. Hence in the below code only class Main is public.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("Pong");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel(new GridLayout(0, 2));
mainPanel.add(new Player());
mainPanel.add(new Ball());
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
class Player extends JPanel {
public Player() {
setBorder(BorderFactory.createLineBorder(Color.RED, 2, false));
setPreferredSize(new Dimension(800, 800));
setBackground(Color.BLACK);
new Timer(1000 / 60, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}).start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(20, 100, 20, 100);
}
}
class Ball extends JPanel {
public Ball() {
setBorder(BorderFactory.createLineBorder(Color.CYAN, 2, false));
setPreferredSize(new Dimension(800, 800));
setBackground(Color.BLACK);
new Timer(1000 / 60, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}).start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(300, 300, 10, 10);
}
}
And here is a screen capture of the GUI...
I just realized that if I extend my window manually, the second JPanel shows up under the one responsible for the Player! This means that I'll need to set the Panels position somehow, right?
#Abra

JComponents with Graphics.draw

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);
}
}

How can I close a window which is using a for loop and thread.sleep

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();
}
}
}

about drawing a Polygon in java

hi there i'm trying to improve myself about java2D and first of all i'm dealing with drawing polygons. However, i can not see the polygon on frame. I read some tutorials and examples but as i said i face with problems. here is the sample code of drawing a polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
public class jRisk extends JFrame {
private JFrame mainMap;
private Polygon poly;
public jRisk(){
initComponents();
}
private void initComponents(){
mainMap = new JFrame();
mainMap.setSize(800, 600);
mainMap.setResizable(false);
mainMap.setVisible(true);
mainMap.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int xPoly[] = {150,250,325,375,450,275,100};
int yPoly[] = {150,100,125,225,250,375,300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
}
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
/**
* #param args
*/
public static void main(String[] args) {
new jRisk();
}
}
JFrame does not have a paintComponent(Graphics g) method. Add the #Override annotation and you will get a compile time error.
1) Use JPanel and override paintComponent (you would than add JPanel to the JFrame viad JFrame#add(..))
2) Override getPreferredSize() to return correct Dimensions which fit your drawing on Graphics object or else they wont be seen as JPanel size without components is 0,0
3) dont call setSize on JFrame... rather use a correct LayoutManager and/or override getPrefferedSize() and call pack() on JFrame after adding all components but before setting it visible
4) Have a read on Concurrency in Swing specifically about Event Dispatch Thread
5) watch class naming scheme should begin with a capital letter and every first letter of a new word thereafter should be capitalized
6) Also you extend JFrame and have a variable JFrame? Take away the extend JFrame and keep the JFrame variable as we dont want 2 JFrames and its not good practice to extend JFrame unless adding functionality
Here is your code with above fixes (excuse picture quality but had to resize or it was going to 800x600):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
As per your comment:
i am preparing a map which includes lots of polygons and yesterday i
used a JPanel over a JFrame and i tried to check if mouse was inside
of the polygon with MouseListener. later i saw that mouseListener gave
false responds (like mouse is not inside of the polygon but it acts
like it was inside the polygon). so i deleted the JPanel and then it
worked
Here is updated code with MouseAdapter and overridden mouseClicked to check if click was within polygon.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
if (poly.contains(me.getPoint())) {
System.out.println("Clicked polygon");
}
}
};
p.addMouseListener(ma);//add listener to panel
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
JFrame does not extend JComponent so does not override paintComponent. You can check this by adding the #Override annotation.
To get this functionality extract paintComponent to a new class which extends JComponent. Don't forget to call super.paintComponent(g) rather than super.paintComponents(g).
Replace
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
With
protected void paint(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}

Categories