this may be a silly question but How do i call the paintComponent? Its not displaying the object at all. its within the, public class Ball extends JPanel implements Runnable.
public class Balls {
public static void main(String[] args) {
new Balls();
}
public Balls() {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Balls!");
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.add(new ballAdder());
frame.setSize(1000, 1000);
frame.setVisible(true);
}
});
}
public class ballAdder extends JPanel {
public ballAdder() {
add(new Ball(5, 5));
}
}
public class Ball extends JPanel implements Runnable {
public int x, y;
public int speedx, speedy;
public int width = 40, height = 40;
public Ball(int x, int y) {
this.x = x;
this.y = y;
new Thread(this).start();
}
public void move() {
x += speedx;
y += speedy;
if (0 > x || x > 950) {
speedx = -speedx;
}
if (0 > y || y > 950) {
speedy = -speedy;
}
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(x, y, width, height);
}
public void run() {
while (true) {
move();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
You should never call paintComponent
(or paint
) yourself. This is done by the RepaintManager
.
The problem you're actually having is that fact speedx
and speedy
or 0
, meaning you balls never move...
The other issue is that the ballAdder
class is using a FlowLayout
and you Ball
class is not providing any details about it's preferred size, meaning that the Ball
panel has a preferred size of 0x0
.
Review
There is a significant issue with scalability with your design. Apart from the fact that you're going to find it difficult to add more then one ball to the UI because of layout issues...
Each Ball
has it's own thread. This means, the more balls you add, the more threads that are going to be running. This is going to have a steady drain on resources and effect the performance of your application.
It would be better to provide a concept of a Drawable
object which knew where it should be displayed within the concept it's container and could be painted
from within the paintComponent
. By utilising a single javax.swing.Timer
it should be more capable of supporting a growing number of random balls.
First Fix
To fix you're first issue, you could do something like this...
public class ballAdder extends JPanel {
public ballAdder() {
setLayout(new BorderLayout());
add(new Ball(5, 5));
}
}
The problem with this fix is that you're only ever going to be able to have a single Ball
on the container, as it will want to occupy the maximum available space.
You might want to have a read through Using Layout Managers for more details
A (possible) better solution
A (possible) better solution would be to use a single JPanel
as a "ball pit" which maintained a reference to list of balls.
You would then use the BallPitPane
's paintComponent
method to draw all the balls (in the balls list).
Through the use of a single javax.swing.Timer
, you could iterate through the balls list and update there positions (within the context of the BallPitPane
IMHO, this easier then trying to fight with the layout managers or writing your own...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Bounce {
public static void main(String[] args) {
new Bounce();
}
public Bounce() {
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 BallPitPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallPitPane extends JPanel {
private List<Ball> balls;
private Random rand;
public BallPitPane() {
rand = new Random(System.currentTimeMillis());
balls = new ArrayList<>(25);
Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (balls.isEmpty()) {
balls.add(new Ball(BallPitPane.this));
}
if (rand.nextBoolean()) {
balls.add(new Ball(BallPitPane.this));
}
for (Ball ball : balls) {
ball.move();
}
repaint();
}
});
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
}
protected static int random(int min, int max) {
return (int)Math.round(Math.random() * (max - min)) + min;
}
public static class Ball {
public static final int WIDTH = 10;
public static final int HEIGHT = 10;
private int x;
private int y;
private int deltaX;
private int deltaY;
private Color color;
private BallPitPane parent;
public Ball(BallPitPane parent) {
this.parent = parent;
x = parent.getWidth() / 2;
y = parent.getHeight() / 2;
deltaX = random(-4, 4);
deltaY = random(-4, 4);
color = new Color(random(0, 255), random(0, 255), random(0, 255));
}
public void move() {
x += deltaX;
y += deltaY;
if (x + WIDTH > parent.getWidth()) {
x = parent.getWidth() - WIDTH;
deltaX *= -1;
} else if (x < 0) {
x = 0;
deltaX *= -1;
}
if (y + HEIGHT > parent.getHeight()) {
y = parent.getHeight() - HEIGHT;
deltaY *= -1;
} else if (y < 0) {
y = 0;
deltaY *= -1;
}
}
public Color getColor() {
return color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.fillOval(x, y, WIDTH, HEIGHT);
g2d.setColor(Color.BLACK);
g2d.drawOval(x, y, WIDTH, HEIGHT);
}
}
}