Painting JFrame Difficulty - java

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

Related

How do I animate my java Swing graphics2d component

I'm making a gravity simulator and I need it animate live so the user can watch it. I've been able to make it trace out the path the object would take.
But as you can see it just traces it out and then displays the window. I think my problem is because all of this in the section of code that builds the JPanel but I don't know how to change it properly.
Here's what I'm doing for my window:
import java.awt.*;
import javax.swing.*;
import java.lang.Math;
public class Universe {
public static void main(String[] args) throws InterruptedException {
new Universe();
}
public Universe() {
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("Gravity Simulator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
int paneWidth = 500;
int paneHeight = 500;
#Override
public Dimension getPreferredSize() {
return new Dimension(paneWidth, paneHeight);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int size = Math.min(getWidth()-4, getHeight()-4) / 10;
int width = getWidth() - (size * 2);
int height = getHeight() - (size * 2);
int x0=paneWidth/2; int y0=paneHeight/2; int radius0=20;
int y = (getHeight() - (size * 10)) / 2;
for (int horz = 0; horz < 2; horz++) {
int x = (getWidth() - (size * 10)) / 2;
for (int vert = 0; vert < 10; vert++) {
g.drawRect(x, y, size, size);
drawCircle(g, x+25, y+25, 5);//A massive object would go here this just proof of concept
x += size;
}
y += size;
}
double[] velocity={5,-2};
MassiveObject planet = new MassiveObject(g, 20, 50, velocity, 250, 150);
planet.draw(g);
MassiveObject rock = new MassiveObject(g, 2, 25, velocity, 275, 300);
rock.draw(g);
double sGravity = fGrav(planet, rock);
//double dis = massDis(planet, rock);
System.out.println("Distance: "+massDis(planet, rock));
System.out.println("Gravity: "+sGravity+" Newtons of force(gravity is multiplied by "+1000000+")");
double[] traj = objectTrajetory(planet, rock, rock.getMass());
int t = 0;
try {
while(true) {
//double k = sGravity/dis;
//x and y components of motion
double xm = traj[0];
double ym = traj[1];
double[] nVelocity= {xm,ym};
//////////////////////////////
//set new position of object
rock.setX(rock.getX()+(xm));
rock.setY(rock.getY()+(ym));
rock.setVelocity(nVelocity);
rock.draw(g);
t++;
System.out.println("position changed: "+rock.getCoords());
traj = objectTrajetory(planet, rock, 1);
Thread.sleep(100);
if (t> 15){break;}
}
}
catch(Exception e) {
}
//System.out.println("Distance: "+massDis(planet, rock));
//System.out.println("Gravity: "+fGrav(planet, rock)+" Newtons of force(gravity is multiplied by "+1000000+")");
g2d.dispose();
}
And here is the code for the draw function of my MassiveObject:
public void draw(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Ellipse2D.Double circle = new Ellipse2D.Double(this.x0-(this.radius/2), this.y0-(this.radius/2), this.radius, this.radius);
g2d.setColor(Color.GRAY);
g2d.fill(circle);
}
So basically what I'm asking is how can I make it run that algorithm to paste the MassiveObject at its new location after the window is already pulled up so the user can watch it happening instead of it just building the window with it already on it?
The logic of your animation shouldn't be in the paintComponent() method. The paintComponent() method should just paint the current frame of animation. The code inside paintComponent() is run inside a special thread dedicated to handling all UI paints, responding to clicks etc. So for as long as paintComponent() is running, nothing else can happen in the UI, hence your application "grinds to a halt".
The logic to periodically update the state and then order a repaint should be in a separate thread (or the main thread). When it has updated the state and needs the next frame to be drawn, it then calls the panel's repaint() method. Because you're doing this in another thread, you would surround it in SwingUtilities.invokeLater(). This orders Swing to to call back into the paintComponent():
while (true) {
// Update state used by the paintComponent() method
updateObjectPositions();
// Now draw the new animation frame
SwingUtilities.invokeLater(() -> {
universePanel.repaint(0, 0, universeWidth, universeHeight);
});
Thread.sleep(...);
}
Because the drawing and updating are happening in different threads, you need to make sure that the data is shared between the threads in a thread-safe way. If you're just starting out and the calculations are very quick, then you could put the updateObjectPositions() method inside the invokeLater() so that the update to the data and the redraw happen in the UI thread. But remember that the code inside the invokeLater() will be blocking the UI for as long as it runs, so it should be as brief as possible and just handle a single frame. Crucially, your while loop and sleep should not go inside the invokeLater() or inside any UI code such as paintComponent().
Thanks a lot for the help, I was able to get the program animating the way I wanted it to and it was exactly as you all suggested. I removed my logic from the paintComponent() and put it inside the JPanel pane, ran a timer to continuously update the position, and then ran the repaint() function at the end of each loop in timer.
public class TestPane extends JPanel {
int paneWidth = 1200;
int paneHeight = 1200;
double[] velocity={4,4};
MassiveObject planet = new MassiveObject( 50, 50, velocity, paneWidth/2,paneHeight/2);
MassiveObject rock = new MassiveObject( 2, 25, velocity, 150, 200);
double[] traj = objectTrajetory(planet, rock, rock.getMass());
double xm=0.00;
double ym=0.00;
public TestPane() {
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xm = traj[0];
ym = traj[1];
double[] nVelocity= {xm,ym};
//////////////////////////////
//set new position of object
rock.setX(rock.getX()+(xm));
rock.setY(rock.getY()+(ym));
rock.setVelocity(nVelocity);
System.out.println("position changed: "+rock.getCoords());
repaint();
traj = objectTrajetory(planet, rock, 1);
rock.setX(rock.getX()+(xm));
rock.setY(rock.getY()+(ym));
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(paneWidth, paneHeight);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int size = Math.min(getWidth()-4, getHeight()-4) / 10;
int width = getWidth() - (size * 2);
int height = getHeight() - (size * 2);
int x0=paneWidth/2; int y0=paneHeight/2; int radius0=20;
rock.draw(g);
planet.draw(g);
g2d.dispose();
}
The program now animates pretty smoothly instead of just spitting out a plot of the path it would take.
Snap of Animated Orbit

Java: drawing multiple sprites in different threads at a time

I need to create a JPanel, where upon a mouse click a new Sprite must appear in a new thread. This is what I have:
public class BouncingSprites {
private JFrame frame;
private SpritePanel panel = new SpritePanel();
private Sprite ball;
public BouncingSprites() {
frame = new JFrame("Bouncing Sprite");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setVisible(true);
}
public void start(){
panel.animate(); // never returns due to infinite loop in animate method
}
public static void main(String[] args) {
new BouncingSprites().start();
}
}
Class SpritePanel:
public class SpritePanel extends JPanel
{
Sprite sprite;
public SpritePanel()
{
addMouseListener(new Mouse());
}
//method for creating a new ball
private void newSprite (MouseEvent event)
{
sprite = new Sprite(this);
}
public void animate()
{
}
private class Mouse extends MouseAdapter
{
#Override
public void mousePressed( final MouseEvent event )
{
newSprite(event);
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (sprite != null)
{
sprite.draw(g);
}
}
}
Class Sprite :
public class Sprite implements Runnable
{
public final static Random random = new Random();
final static int SIZE = 10;
final static int MAX_SPEED = 5;
SpritePanel panel;
private int x;
private int y;
private int dx;
private int dy;
private Color color = Color.BLUE;
private Thread animation;
public Sprite (SpritePanel panel)
{
this.panel = panel;
x = random.nextInt(panel.getWidth());
y = random.nextInt(panel.getHeight());
dx = random.nextInt(2*MAX_SPEED) - MAX_SPEED;
dy = random.nextInt(2*MAX_SPEED) - MAX_SPEED;
animation = new Thread(this);
animation.start();
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, SIZE, SIZE);
}
public void move()
{
// check for bounce and make the ball bounce if necessary
//
if (x < 0 && dx < 0){
//bounce off the left wall
x = 0;
dx = -dx;
}
if (y < 0 && dy < 0){
//bounce off the top wall
y = 0;
dy = -dy;
}
if (x > panel.getWidth() - SIZE && dx > 0){
//bounce off the right wall
x = panel.getWidth() - SIZE;
dx = - dx;
}
if (y > panel.getHeight() - SIZE && dy > 0){
//bounce off the bottom wall
y = panel.getHeight() - SIZE;
dy = -dy;
}
//make the ball move
x += dx;
y += dy;
}
#Override
public void run()
{
while (Thread.currentThread() == animation)
{
move();
panel.repaint();
try
{
Thread.sleep(40);
}
catch ( InterruptedException exception )
{
exception.printStackTrace();
}
}
}
}
This code creates a new moving sprite. However, the previous sprite gets removed from the panel. What I need to do is to add a new sprite with a mouse click, so that the previous sprite is not removed. If I click three times, three sprites should be painted.
How do I change my code to implement that?
Many thanks!
You could replace Sprite sprite; in the SpritePanel class with List<Sprite> sprites = new ArrayList<>();. Then, when you make a new Sprite, add it to the list. Then in your paintComponent method, you could iterate through the list, drawing all of the sprites.

Transparent Panels

I am making a game where a user has to draw lines so as to make a ball bounce into a target. I'm having trouble getting both the ball and the line to show up concurrently, and I can get only one or the other to appear. It seems to me that the panels block each other out, even though I made them transparent. I would like for them both to appear on the same frame. As of this post, the line panel covers the ball panel.
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Game
{
public static void main(String args[]) throws Exception
{
JFrame f = new JFrame("Let's Play");
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize(1280, 720);
f.setLocation(300, 300);
f.setResizable(false);
//this part draws a ball that bounces around the screen
BallPanel ballPanel = new BallPanel()
{
// draw rectangles and arcs
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);
}
};
ballPanel.setOpaque(false);
f.add(ballPanel);
//this part allows you to draw lines on the frame with your mouse
JPanel lineP = new JPanel()
{
Point pointStart = null;
Point pointEnd = null;
{
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
pointStart = me.getPoint();
}
public void mouseReleased(MouseEvent me)
{
pointStart = null;
}
});
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent me)
{
pointEnd = me.getPoint();
}
public void mouseDragged(MouseEvent me)
{
pointEnd = me.getPoint();
repaint();
}
});
}
public void paint(Graphics dline)
{
super.paint(dline);
if (pointStart != null)
{
dline.setColor(Color.RED);
dline.drawLine(pointStart.x, pointStart.y, pointEnd.x, pointEnd.y);
}
}
};
lineP.setOpaque(false); //attempted to enable to see ball panel here
f.add(lineP);
f.setVisible(true);
}
}
class BallPanel extends JPanel implements ActionListener
{
private int delay = 10;
protected Timer timer;
public int x = 30; // x position
public int y = 30; // y position
public int radius = 15; // ball radius
public int dx = 10; // increment amount (x coord)
public int dy = 10; // increment amount (y coord)
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e)
// will run when the timer fires
{
repaint();
}
}
You've got several issues, but the main one is that you're over-using GUI components. You should have just one single component JPanel that does the drawing, a DrawingPanel, and not a ball panel and a line panel. Rather Ball and Line should be logical classes, not GUI classes, and their display should be in the same single DrawingPanel.
Other issues include:
A main method that has way too much code. Most of that code should be off-loaded into the OOP world where it belongs.
GUI component classes that also implement listener interfaces. This is giving the class too much responsibility making debugging and upgrading difficult. Separate these concerns.
One of your classes overrides the paint method, and this should be avoided. Override paintComponent.
The other class that overrides paintComponent has program logic within paintComponent, and this should be avoided since you have limited control over when or if this method gets called. Get the logic out of that class and into either the mouse listener code or the game loop code (Swing Timer).

ContentPane background color changes when adding object

I have the following code:
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class Ball extends JPanel implements Runnable {
List<Ball> balls = new ArrayList<Ball>();
Color color;
int diameter;
long delay;
private int x;
private int y;
private int vx;
private int vy;
public Ball(String ballcolor, int xvelocity, int yvelocity) {
color = Color.PINK;
diameter = 30;
delay = 40;
x = 1;
y = 1;
vx = xvelocity;
vy = yvelocity;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,50,50); //adds color to circle
g.setColor(Color.pink);
g2.drawOval(x,y,50,50); //draws circle
}
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if(x + vx < 0 || x + diameter + vx > getWidth()) {
vx *= -1;
}
if(y + vy < 0 || y + diameter + vy > getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
Ball ball1 = new Ball("pink",6,6);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setBackground(Color.BLACK);
f.getContentPane().add(ball1);
f.setSize(1000,500);
f.setLocation(200,200);
new Thread(ball1).start();
f.setVisible(true);
}
}
I would like to make the background black instead of the default grey one, but this only works if I get the ball out of the code, basically deleting f.getContentPane().add(ball1);. What I want is a pink ball bouncing on a black background, but it keeps going back to grey when adding the ball.
What is the problem?
Ball which extends from JPanel is opaque (not see through), so it is filling it's own background with it's default color...
You could make Ball transparent by using setOpaque(false) or set the Ball's background color to BLACK using setBackground(Color.BLACK) for example...
ps- I should add, because JFrame uses a BorderLayout, your Ball pane will occupy the entire available space within the center of the frame (CENTER being the default position and the frame not containing any other components)

JPanel Design issue

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

Categories

Resources