I have Form on which i am drawing oval on mouseClick Event. This works Fine for me. The Circles are painted. But When i Minimize the Form and Again Maximize it the Panel gets Refreshed and Circles are removed (i.e Panel is left Blank).
Code is :
I have a JFrame on which there is a Jpanel named jPanel1, on this panel circles are drawn.
private void jPanel1MouseClicked(java.awt.event.MouseEvent evt) {
count += 1;
if (count <= clients) {
drawCircle(evt.getX() - (radius / 2), evt.getY() - (radius / 2));
}
}
public void drawCircle(int x, int y) {
Graphics g = jPanel1.getGraphics();
g.drawOval(x - radius, y - radius, 2 * radius, 2 * radius);
g.setColor(Color.BLACK);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
In this case, not only is it important to override your paintComponent method for the JPanel, but you also need to store the information about the circles you're going to draw. You use that stored information to paint all the circles on the screen during the paintComponent call.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class TempProject extends JPanel{
/** Stores info about circles */
public ArrayList<CircleInfo> circles = new ArrayList<CircleInfo>();
/** fields that were in example code */
public int count = 0;
public final int radius = 20;
public final int clients = 20;
public TempProject(){
addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent evt) {
count += 1;
if (count <= clients) {
// Store info about the circle to draw
circles.add(new CircleInfo(evt.getX() - (radius / 2), evt.getY() - (radius / 2), radius));
// Tell swing to repaint asap
repaint();
}
}});
}
#Override
public void paintComponent(Graphics g ) {
super.paintComponent(g);
//Iterates through saved circles and paints them
for(CircleInfo circle : circles){
g.drawOval(circle.x - circle.radius, circle.y - circle.radius, 2 * circle.radius, 2 * circle.radius);
g.setColor(Color.BLACK);
g.fillOval(circle.x - circle.radius, circle.y - circle.radius, 2 * circle.radius, 2 * circle.radius);
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane(new TempProject());
frame.setPreferredSize(new Dimension(400, 300));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
/** Simple class for storing Circle info */
public static class CircleInfo{
int x = 0;
int y = 0;
int radius = 0;
public CircleInfo(int x, int y, int radius){
this.x = x; this.y = y; this.radius = radius;
}
}
}
You don't have to call explicitely draw functions outside the paintComponent method of your JPanel.
You should instead extends JPanel and put the drawCircle code inside paintComponent method:
public class DrawCircleClass extends JPanel
{
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x - radius, y - radius, 2 * radius, 2 * radius);
g.setColor(Color.BLACK);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
}
Swing will automatically calls paintComponent method when the Component should be redrawn (es. after maximize the minimized window).
All the drawings must be done in paint method of panel. So you have to override this method in the panel and put the drawing code there
Related
Can't get the program to print more than one square.
My code right now
import java.awt.*;
import javax.swing.*;
public class MyApplication extends JFrame {
private static final Dimension WindowSize = new Dimension(600, 600);
private int xCord=9, yCord=32, width=80, height=80;
public MyApplication() {
//Create and set up the window
this.setTitle("Squares");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window centered on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - WindowSize.width / 2;
int y = screensize.height / 2 - WindowSize.height / 2;
setBounds(x, y, WindowSize.width, WindowSize.height);
setVisible(true);
}
public static void main(String args[]) {
MyApplication window = new MyApplication();
}
public void paint(Graphics g) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
g.setColor(Color.getHSBColor(red, green, blue));
g.fillRect(xCord, yCord, width, height);
while((yCord+height)<600){
if((xCord+width)>600){
xCord=9;
yCord+=80;
}
else xCord+=80;
repaint();
}
}
}
I'm trying to fill a 600x600 window with squares of different colours that go to a new line once a row is full.
First of all, don't.
Don't override paint of top level containers, like JFrame.
JFrame is a composite component, meaning that they're a number of layers between its surface and the user and because of the way the paint system works, these can be painted independent of the frame, which can produce weird results.
Top level containers are not double buffered, meaning your updates will flash.
DO call a paint methods super method, unless you are absolutely sure you know what you're doing.
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting works in Swing and how you should work with it.
This...
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - WindowSize.width / 2;
int y = screensize.height / 2 - WindowSize.height / 2;
setBounds(x, y, WindowSize.width, WindowSize.height);
is a bad idea on a number of levels.
Toolkit#getScreenSize does not take into consideration the size of other UI elements which will reduce the available viewable area available on the screen, things like the taskbar/dock or menu bar on some OS
Using setBounds(x, y, WindowSize.width, WindowSize.height); on a window based class is also a bad idea, as the avaliable viewable area is the window size MINUS the window's decorations, meaning the actually viewable area is smaller then you have specified and because you're painting directly to the frame, you run the risk of painting under the frame decorations.
You can have a look at How can I set in the midst? for more details
One thing you should now about painting, painting is destructive, that is, each time a paint cycle occurs, you are expected to completely repaint the current state of the component.
Currently, this...
public void paint(Graphics g) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
g.setColor(Color.getHSBColor(red, green, blue));
g.fillRect(xCord, yCord, width, height);
while ((yCord + height) < 600) {
if ((xCord + width) > 600) {
xCord = 9;
yCord += 80;
} else {
xCord += 80;
}
repaint();
}
}
will only paint a single rectangle, base on the last value of xCord and yCord most likely AFTER the paint method has exited.
Swing uses a passive rendering engine, meaning that the system will make determinations about what to paint and when, you don't control it. You can make a "request" to the system through the use repaint, but it's up to the system to decide when and what will get painted, this means that multiple requests can be optimised down to a single paint pass.
Also, painting should do nothing more than paint the current state. It should avoid changing the state, directly or indirectly, especially if that change triggers a new paint pass, as this can suddenly reduce the performance of your program to 0, crippling it.
So, what's the answer?
Well, change everything...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MyApplication {
public static void main(String[] args) {
new MyApplication();
}
public MyApplication() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
private static final Dimension DESIRED_SIZE = new Dimension(600, 600);
private int width = 80, height = 80;
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return DESIRED_SIZE;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int xCord = 0, yCord = 0;
while ((yCord) < getHeight()) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
g2d.setColor(Color.getHSBColor(red, green, blue));
g2d.fillRect(xCord, yCord, width, height);
if ((xCord + width) > getWidth()) {
xCord = 0;
yCord += 80;
} else {
xCord += 80;
}
}
g2d.dispose();
}
}
}
Break down...
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
This creates an instance of Jframe, you don't really want extend from JFrame, you're not adding any new functionality to the class
frame.pack() is packing the window around the content, this ensures that the frame is always larger (by the amount of the frame decorations) then the desired content size
frame.setLocationRelativeTo(null); will centre the window in a system independent manner.
Next...
private static final Dimension DESIRED_SIZE = new Dimension(600, 600);
private int width = 80, height = 80;
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return DESIRED_SIZE;
}
I've used DESIRED_SIZE to provide a sizing hint to the parent containers layout manager.
Finally...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int xCord = 0, yCord = 0;
while ((yCord) < getHeight()) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
g2d.setColor(Color.getHSBColor(red, green, blue));
g2d.fillRect(xCord, yCord, width, height);
if ((xCord + width) > getWidth()) {
xCord = 0;
yCord += 80;
} else {
xCord += 80;
}
}
g2d.dispose();
}
Note here, I've changed the xCord and yCord positions to zero, I no longer need to "guess" at the frame decorations. As well as making the local variables, so that when ever the method is called again, the values are reset to zero.
You don't "have" to cast the Graphics reference to Graphics2D, but Graphics2D is a more powerful API. I also like to copy it's state, but that's me, your code is simple enough so it's unlikely to have adverse effects on anything else that might be painted after your component.
Notice also, I've use getWidth and getHeight instead of "magic numbers", meaning you can resize the window and the painting will adapt.
You could try placing the whole paint mechanism inside your loop to get it done with in a one call. Therefore you wont need to call repaint inside the paint method itself:
public void paint(Graphics g) {
while((yCord+height)<600){
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
g.setColor(Color.getHSBColor(red, green, blue));
g.fillRect(xCord, yCord, width, height);
if((xCord+width)>600){
xCord=9;
yCord+=80;
}
else xCord+=80;
}
}
I was reading Core Java and encountered this code snippet:
package draw;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class DrawTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new DrawFrame();
frame.setTitle("DrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class DrawFrame extends JFrame
{
public DrawFrame()
{
add(new DrawComponent());
pack();
}
}
class DrawComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
public void paintCompent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// draw a rectangle
double leftX = 100;
double topY = 100;
double width = 200;
double height = 150;
Rectangle2D rect = new Rectangle2D.Double(leftX, topY, width, height);
g2.draw(rect);
// draw the enclosed ellipse
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect);
g2.draw(ellipse);
// draw a diagonal line
g2.draw(new Line2D.Double(leftX, topY, leftX + width, topY + height));
// draw a circle with the same center
double centerX = rect.getCenterX();
double centerY = rect.getCenterY();
double radius = 150;
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(centerX, centerY, centerX + radius, centerY + radius);
g2.draw(circle);
}
public Dimension getPreferredSize()
{
return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
I tried this code on Eclipse, it did run but, instead of rectangles, ellipse, diagonal lines and circle, there appeared nothing in the frame. I double-checked the code against the book, there was no typo. What is wrong?
There's a spelling mistake...
public void paintCompent(Graphics g) {
should be
public void paintComponent(Graphics g) {
This is why you should use the #Override annotation, as it will give you a compile time error when you try to override a method that doesn't exist within the parent hierarcy.
You should also be calling super.paintComponent(g); before performing any custom painting
In my code I am trying to paint by JFrame but it is not painting correctly. I tell the frame to paint in the beginning where I create it however it is the normal grey color once it is created. I think it may have to do with the fact that I am repainting it, if so how can I make sure it is repainted yellow? Could someone try to figure out why my code is not painting the JFrame Yellow? Thank you!
public class EvolutionColor {
public static void main(String args[]) {
JFrame frame = new JFrame("Bouncing Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BallPanel bp = new BallPanel();
frame.add(bp);
frame.setSize(600, 600); // set frame size
frame.setVisible(true); // display frame
frame.setBackground(Color.YELLOW);
}
class BallPanel extends JPanel implements ActionListener {
private int delay = 10;
protected Timer timer;
private int x = 0; // x position
private int y = 0; // y position
private int radius = 15; // ball radius
private int dx = 2; // increment amount (x coord)
private int dy = 2; // increment amount (y coord)
public BallPanel() {
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // call superclass's paintComponent
g.setColor(Color.red);
// check for boundaries
if (x < radius) {
dx = Math.abs(dx);
}
if (x > getWidth() - radius) {
dx = -Math.abs(dx);
}
if (y < radius) {
dy = Math.abs(dy);
}
if (y > getHeight() - radius) {
dy = -Math.abs(dy);
}
// adjust ball position
x += dx;
y += dy;
g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
}
Don't set the JFrame to yellow, set the BallPanel object to yellow.
Make BallPanel transparent...
bp.setOpaque(false);
Don't make decisions about the state of your component within paintComponent method, these decisions should be made within you actionPerformed method
Painting is for painting and paintComponent may be called for any number of reasons, many which you don't control
For a problem I have to draw a circle on the screen with center at coordinates (280,300) with a radius of 50. The hint says: A circle is an oval with the same width and height. The center of this circle is 50 pixels below and 50 pixels to the right of the NW corner of this oval.
There is the TryoutPanel class:
import java.awt.*;
import javax.swing.*;
public class TryoutPanel extends JPanel{
private Color myColor;
public TryoutPanel(Color c){
myColor = c;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
setForeground(myColor);
g.drawString("top",10,50);
g.drawLine(10,60, 200,60);
g.drawString("middle",10,80);
g.drawLine(10,90, 200,90);
g.drawString("bottom",10,110);
g.drawLine(10,120, 200,120);
g.drawRect(200,300,100,50);
g.drawOval(200,300,100,50);
for(int j = 0; j < 9; j++)
g.drawOval(50,200, 10 + 20*j, 210 - 20*j);
}
}
I have to fill in the code in the following:
public void paintComponent(Graphics g){
super.paintComponent(g);
setForeground(myColor);
//INSERT CODE HERE
I tried:
g.drawOval(280,300,50,50);
But it says I used incorrect parameters. What am I doing wrong.
The x/y parameter of drawOval is the top/left corner from where the oval will be drawn
In order to be able to draw the circle around the center point if 230x300, you will need to subtract the radius from each point and then generate a width and height (diameter) of double that...
g.drawOval(230 - radius, 300 - radius, radius * 2, radius * 2);
So, this example basic draws a rectangle around the point of 230x300 with a width/height of 200 (radius = 100) and draws lines through this point to illustrate the center point the oval then drawn about...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TryoutOval {
public static void main(String[] args) {
new TryoutOval();
}
public TryoutOval() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TryoutPanel(Color.RED));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TryoutPanel extends JPanel {
private Color myColor;
public TryoutPanel(Color c) {
myColor = c;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int centerX = 280;
int centerY = 300;
int radius = 50;
int diameter = radius * 2;
int x = centerX - radius;
int y = centerY - radius;
g.setColor(Color.BLACK);
g.drawRect(x, y, diameter, diameter);
g.drawLine(x, y, x + diameter, y + diameter);
g.drawLine(x + diameter, y, x, y + diameter);
g.setColor(myColor);
g.drawOval(x, y, diameter, diameter);
g.fillOval(centerX - 5, centerY - 5, 10, 10);
}
}
}
Oh, and setForeground(myColor); is a horribly bad idea within any paint method as it will cause a paint event to be added to the event queue each time the method is called, which will cause a never ending repaint request which will eventually consume your CPU
This must be rather trivial and straight forward, but I cannot figure it out.
This is what my JPanel looks like, it is added to a JFrame:
private class RadarPanel extends JPanel {
public RadarPanel() {
super();
this.repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//painting logic here
//repaint in 500 ms
this.repaint(500);
}
}
Now, when I resize the JFrame this JPanel starts getting redrawn all the time. However, when I do not resize the JFrame the JPanel's paintComponent method does not seem to get called, even though I call repaint in the constructor.
Any advice? Thanks.
UPDATE:
more complete code (everything except drawing logic):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PlayerRadar extends JFrame {
private static final long serialVersionUID = 230324190;
//settings
private static final int windowWidth = 300;
private static final int windowHeight = 300;
private static final int maxDistance = 250;
//components
private PlayerRadar radarWindow;
private JPanel radarPanel;
public PlayerRadar(String title) {
super(title);
//set reference
radarWindow = this;
//create radar window
Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
this.setAlwaysOnTop(true);
this.setBackground(new Color(0xFFFFFF));
this.setBounds(screenSize.width - windowWidth, 0, windowWidth, windowHeight);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
radarWindow.setVisible(false);
}
});
this.setVisible(true);
//create a JPanel for drawing
radarPanel = new RadarPanel();
radarPanel.setBounds(0, 0, windowWidth, windowHeight);
radarPanel.setBackground(new Color(0xFFFFFF));
//add to frame
this.getContentPane().add(radarPanel);
}
private class RadarPanel extends JPanel {
private static final long serialVersionUID = 230324191;
private static final int repaintInterval = 500;
public RadarPanel() {
super();
}
#Override
public void paint(Graphics g) {
super.paint(g);
//draw player oval (center of the frame)
g.setColor(Color.BLUE); //blue
int ovalWidth = (int) Math.round(this.getWidth() / 30);
int ovalHeight = (int) Math.round(this.getHeight() / 30);
int playerLocalX = (int) Math.round(this.getWidth() / 2);
int playerLocalY = (int) Math.round(this.getHeight() / 2);
int ovalX = playerLocalX - ovalWidth / 2;
int ovalY = playerLocalY - ovalHeight / 2;
g.fillOval(ovalX, ovalY, ovalWidth, ovalHeight);
g.setColor(Color.BLACK); //black
g.drawOval(ovalX, ovalY, ovalWidth, ovalHeight);
//get info of the player itself
PlayerInfo thisPlayer = GameUtil.getPlayerInfo();
float playerPosZ = thisPlayer.position[0];
float playerPosX = thisPlayer.position[2];
//float playerRotRad = thisPlayer.rotation;
//set rectangle specs
int rectWidth = this.getWidth() / 40;
int rectHeight = this.getWidth() / 40;
//only continue if we have information about our player
if (thisPlayer != null) {
//get nearby players
ArrayList<PlayerInfo> playersInfo = GameUtil.getNearbyPlayers();
//for each other player, draw a rectangle
for (PlayerInfo playerInfo : playersInfo) {
//get data
float posZ = playerInfo.position[0];
float posX = playerInfo.position[2];
//float rotRad = playerInfo.rotation;
//calculate relative x and y
int rectX = playerLocalX + Math.round((posX - playerPosX) / maxDistance * this.getWidth() / 2) - rectWidth / 2;
int rectY = playerLocalY + ovalHeight / 2 + Math.round((playerPosZ - posZ) / maxDistance * this.getHeight() / 2) - rectHeight / 2;
//draw rectangle
g.setColor(Color.RED);
g.fillRect(rectX, rectY, rectWidth, rectHeight);
g.setColor(Color.BLACK);
g.drawRect(rectX, rectY, rectWidth, rectHeight);
}
}
//repaint soon
this.repaint(repaintInterval);
}
}
}
You where correct the first time. Custom painting is done in the paintComponent() method, NOT the paint() method.
You should NEVER invoke repaint() from within the paintComponent() method, since that will result in an infinite loop.
If you want to animate the painting, then you should be using a Swing Timer to schedule the animation.
You should not be using use setSize(). That is the job of the layout manager. Instead you can override the getPreferredSize() method of the panel (or use setPreferredSize()) and then you can pack() the frame, instead of setting its size.
The panel should be added to the frame BEFORE the frame is made visible otherwise it has a size of (0, 0) which means there is nothing to paint.
It won't repaint until your form is shown and graphics is initialized. I don't think calling repaint in constructor is a good idea. It will repaint once the component is visible.