Add moving objects to JFrame - java

I'm creating a java game. In the game there are a hero and a bubble. The hero is supposed to move when I press the arrow keys and the bubble is supposed to have continuous diagonal movement. When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both I just get a very small square! I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working. Probably I have to define some type of layout to the JPanels.
What am I doing wrong?
Code:
public class Pang {
public static void main(String[] args) {
JFrame f=new JFrame();
JPanel gamePanel=new JPanel();
gamePanel.setPreferredSize(new Dimension(800, 600));
DrawHero d=new DrawHero();
DrawBubble bubble=new DrawBubble();
gamePanel.add(d);
gamePanel.add(bubble);
f.add(gamePanel);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800, 600);
}
}
public class DrawHero extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 0, y = 0, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawHero() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
imageHero = getHeroImage();
g2.drawImage(imageHero, x, y, 40, 40, null);
}
public Image getHeroImage() {
Image image = null;
image = getImage("hero.png");
return image;
}
public Image getImage(String path) {
Image tempImage = null;
try {
URL heroiURL = DrawHero.class.getResource(path);
tempImage = Toolkit.getDefaultToolkit().getImage(heroiURL);
} catch (Exception e) {
System.out.println("Error loading hero image! - "
+ e.getMessage());
}
return tempImage;
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void moveUp() {
y = y - step;
}
public void moveDown() {
y = y + step;
}
public void moveLeft() {
x = x - step;
}
public void moveRight() {
x = x + step;
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) {
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN) {
moveDown();
}
if (keyCode == KeyEvent.VK_LEFT) {
moveLeft();
}
if (keyCode == KeyEvent.VK_RIGHT) {
moveRight();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
public class DrawBubble extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 100, y = 200, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawBubble() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fill(new Ellipse2D.Double(x, y, 40, 40));
}
public void actionPerformed(ActionEvent e) {
x=x+dx;
y=y+dy;
repaint();
}
public void moveBubble() {
dy=2;
dx=2;
}
public void keyPressed(KeyEvent e) {
moveBubble();
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}

I recommend that neither the DrawHero nor DrawBubble (which should be called Hero nor Bubble respectively) should extend any JComponent. Instead each should simply know how to draw itself to a Graphics object passed to it, when requested to do so.
Then a single GameField or PlayingArea class should keep references to all the Bubble objects and the Hero and draw call the draw(Graphics) method of those objects.
Using this approach it is not necessary to worry about layouts within the GameField component (they become irrelevant).
That is the basic strategy I pursue for rendering the stationary objects in this answer to [Collision detection with complex shapes.

When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both i just get a very small square!
The default layout manager for a JFrame is a BorderLayout. When you use add(component) without a constraint the component goes to the CENTER. Only one component can be added to the CENTER, so only the last one added is displayed.
I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working.
The default layout manager for a JPanel is the FlowLayout which respects the preferred size of component. The problem is you don't override the getPreferredSize() method so the size is (0, 0) and there is nothing to paint.
Probably I have to define some type of layout to the JPanels.
Actually since you want random motion you need to use a null layout on the panel and then use the setSize() and setLocation() method of your components to position the components.
Then when you do this the custom painting should always be done at (0, 0) instead of (x, y) since the location will control where the component is painted on the panel.

Related

Java Swing Graphics and Events

I am trying to make a simple game which displays circles on a frame and when clicked the circle should disappear. I am learning how Java Swing works and managed to draw a circle (Wow such an achievement) and figured out how events work. I added an mouseListener to the circle and when clicked for now I want a to get a console log that it has been clicked but the end result is not as expected. No matter where I click I always get the "click" console log. When I try to add a listener to a JButton for example I get the end result. Are events different for graphics?
import javax.swing.*;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.*;
import java.sql.SQLOutput;
public class CirclePop {
JFrame frame;
Circle circle;
public static void main(String[] args) {
CirclePop circlePop = new CirclePop();
circlePop.drawFrame();
}
public void drawFrame() {
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
}
class Click implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
}
import java.awt.*;
import javax.swing.*;
class Circle extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(150, 140, 30, 30);
}
}
First of all, you may want to extend MouseAdapter instead of implementing MouseListener. This way you don't have "implement" all these empty methods.
Then, in your mousePressed method you just have to calculate if the click happened inside the circle. This is basically just Pythagoras:
static class ClickListener extends MouseAdapter {
private final Circle circle;
public ClickListener(Circle circle) {
this.circle = circle;
}
#Override
public void mousePressed(MouseEvent e) {
int centerX = circle.getCenterX();
int centerY = circle.getCenterY();
int radius = circle.getRadius();
int clickX = e.getX();
int clickY = e.getY();
// inside circle: (clickX - centerX)^2 + (clickY - centerY)^2 < radius^2
double xSquare = Math.pow(clickX - centerX, 2);
double ySquare = Math.pow(clickY - centerY, 2);
if (xSquare + ySquare < radius * radius) {
System.out.println("pressed");
}
}
}
I've added some fields to Circle class to get access to the properties you need for the calculation:
class Circle extends JPanel {
private final int radius = 30;
private final int centerX = 150;
private final int centerY = 140;
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(centerX, centerY, radius, radius);
}
// getter, etc.
}
You have to implement the MouseListener interface indeed, and after a mouse click, you have to check whether the mouse position is contained in the region of your circle. You could do this manually, by comparing coordinates, but this could be a bit too much work. I think it's easier to rather create a Shape object(Infact this is a good time to learn about it since you're just starting out) that you fill with the respective color, and then just check whether the circle contains the mouse position.
Also, check out the Shape class docs when you've got some spare time.
I've gone ahead and made changes to your code, it now uses an instance of Shape class to create a circle.
Also, instead of implementing the MouseListener interface, I recommend extending MouseAdapter since you're not actually providing any meaningful implementation to any method of the interface except the mousePressed() method.
Lastly, notice the shape.contains(event.getPoint()) in the mousePressed() method, that is what does the trick for checking the coordinates.
The rest of the code should be familiar.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class CirclePop {
JFrame frame;
Circle circle;
public static void main(String[] args) {
CirclePop circlePop = new CirclePop();
circlePop.drawFrame();
}
public void drawFrame() {
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
}
class Click extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (circle.shape.contains(e.getPoint())) {
System.out.println("Pressed");
}
}
}
}
class Circle extends JPanel {
Shape shape;
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
shape = new Ellipse2D.Double(150, 140, 30, 30);
g2.setColor(Color.red);
g2.fill(shape);
}
}
Okay, so, this isn't going to be short
Let's start with ....
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
Okay, seems simple enough, but, one thing you've missed is the fact that JFrame, by default, uses a BorderLayout - this means, it will make the child component (and the centre/default position) fill all the available space of the frames viewable space
You can see this if you do something like...
frame = new JFrame();
circle = new Circle();
circle.setBackground(Color.RED);
You will now see that the Circle component occupies the entire frame, so when you click on it, you're clicking the Circle component itself.
This isn't bad, but, you might want to change tact a little. Instead of adding the MouseListener independently of the Circle, have the Circle component make use of its own MouseListener, for example...
class Circle extends JPanel {
public Circle() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// More to come...
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(150, 140, 30, 30);
}
}
This means you get to control much of the logic internally to the class, makes it easier to access some of the more critical information without needing to make a bunch of, potentially, dangerous casts.
So, now we just need to add the logic in to determine if the mouse was clicked within the desirable location or not...
public void mouseClicked(MouseEvent e) {
Point point = e.getPoint();
if (point.x >= 150 && point.x <= 150 + 30 && point.y >= 140 && point.y <= 140 + 30) {
System.out.println("You clicked me :(");
}
}
Okay, that's ... basic
We can simplify it a little and make use of the available functionality within the wider API by making use of the "shapes" API, for example...
class Circle extends JPanel {
private Ellipse2D dot = new Ellipse2D.Double(150, 140, 30, 30);
public Circle() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Point point = e.getPoint();
if (dot.contains(point)) {
System.out.println("You clicked me :(");
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.red);
g2d.fill(dot);
g2d.dispose();
}
}
The benefit of this, apart from contains, is we can change the position of the shape relatively easily and our if statement contains to work 🎉
I do, highly, recommend also having a look at
Performing Custom Painting
Painting in AWT and Swing
2D Graphics Trail
Working with Geometry

Paint method not painting Java

My paint method doesnt seem to paint my 20x20 cells. I have a boolean array for the cells to control their state and that if true, call the cells paint method, a cell is painted however I have two problems;
Only one is painted at a time which is odd because i should have a 40x40 array of booleans meaning i have 40x40 cells
They dont actually paint exactly where I click. I do not know how this is the case as when I get the co-ordinates of my click I immediately place those co-ordinates as my x, and y values in my paint method.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells = new Cells();
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
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, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
else(gameState[xArrayElement][yArrayElement]) = true;
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
this.repaint();
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
System.out.println(xPosition);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cell Class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
You are doing a number of things wrong. My first suggestion would be to forget about offscreen graphics and ensure you are doing what you want. You can always create an image latter. Here are some basic guidelines:
Don't extend JFrame. Use an instance.
Extend JPanel or create a class that extends JPanel and add to frame instance
Then override paintComponent(g) and use that graphics context to draw.
Here is an earlier answer that may help Can't add Graphics into JPanel in Java
More information may be found in the Java Tutorials on painting.
Updated. It took me a few minutes to find this.
public void setPosition(int xi, int xj){
x = xi;
y = xi; // <--- should be xj
}
Regarding (1) above. You must repaint every cell each time you enter paintComponent. This means you will need to iterate across the list and paint them in the correct spot. Right now you are only painting one upon each entry.
A couple more suggestions. Instead of messing with the thread and calling repaint every 20ms in a loop, why not just invoke repaint in the mouseClicked() method.
If you do eventually need to paint every 20ms. I suggest using a swing Timer as follows: (check JavaDoc to ensure I got the syntax correct!!)
Timer timer = new Timer(0, (ev)-> frame.repaint());
timer.setDelay(20);
timer.start();
And you can create your own mouseListener class and extending MouseAdapter. The purpose of these adapter classes is to keep the clutter down so you don't have to have empty methods to satisfy the interface requirements. Put the class inside your main class so it has access to the appropriate data structures. Then just add an instance of it to the mouse listener of the target Component.

Paint- Repaint-mechanism in Swing

I have a problem with Paint- Repaint-Mechanism in Swing java.:
i want to create GraphicEditor, which can create a rectangle and shape with the Mouse.
The application should look like this
My Code Look like this :
public class MiniGrafikEditor extends JFrame implements ActionListener {
private Vector rectList;
private Rectangle currentRect;
private Color color = Color.green;
private static int v = 0 ;
JPanel bp;
public static void main(String[] args) {
MiniGrafikEditor wnd = new MiniGrafikEditor();
}
public MiniGrafikEditor() {
super("Rechtecke zeichnen");
rectList = new Vector();
currentRect = new Rectangle(0, 0, 0, 0);
setLayout(new BorderLayout());
addWindowListener(new MyWindowListener());
addMouseListener(new MyMouseListener());
addMouseMotionListener(new MyMouseMotionListener());
bp = new JPanel();
bp.setBackground(Color.gray);
add("North", bp);
JRadioButton b = null;
bp.add(b = new JRadioButton("Rechteck"));
b.addActionListener(this);
bp.add(b = new JRadioButton("kreis"));
b.addActionListener(this);
bp.add(b = new JRadioButton("Standard"));
b.addActionListener(this);
setLocation(200, 200);
setSize(400, 300);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
String label = ((JRadioButton) e.getSource()).getLabel();
bp.repaint();
if (label.equals("Rechteck")) {
v=1;
bp.repaint();
}
if (label.equals("Blue")) {
color = Color.blue;
bp.repaint();
}
Graphics g = getGraphics();
drawRects(g);
}
public void drawRects(Graphics g) {
Rectangle r;
Enumeration e;
g.clearRect(0, 0, getSize().width, getSize().height);
g.setColor(color);
for (e = rectList.elements(); e.hasMoreElements();) {
r = (Rectangle) e.nextElement();
g.drawRect(r.x, r.y, r.width, r.height);
}
if (currentRect != null && (currentRect.x > 0 || currentRect.y > 0)) {
g.drawRect(currentRect.x, currentRect.y, currentRect.width,
currentRect.height);
}
bp.repaint();
}
class MyMouseListener extends MouseAdapter {
public void mousePressed(MouseEvent event) {
bp.repaint();
if(v==1){
currentRect = new Rectangle(event.getX(), event.getY(), 0, 0);
}
}
public void mouseReleased(MouseEvent event) {
if(v==1){
if (currentRect.width > 0 || currentRect.height > 0) {
rectList.addElement(currentRect);
currentRect = null;
}
Graphics g = getGraphics();
drawRects(g);
}
bp.repaint();
}
}
class MyMouseMotionListener extends MouseMotionAdapter {
public void mouseDragged(MouseEvent event) {
if(v==1){
int x = event.getX();
int y = event.getY();
if (x > currentRect.x && y > currentRect.y) {
currentRect.width = x - currentRect.x;
currentRect.height = y - currentRect.y;
}
Graphics g = getGraphics();
drawRects(g);
}
}
}
class MyWindowListener extends WindowAdapter {
public void windowClosing(WindowEvent event) {
setVisible(false);
dispose();
System.exit(0);
}
}
}
when i run the application its look like this:
When i try to draw a rectangle , i can see when the Jpanel repaint it self.
how can i repaint the Jpanel and i cant see it, when it repaint it self.
thx a lot for your Help.
You have to write an extension of JPanel that overrides paint(Graphics g) or even better paintComponent(Graphics g). Under the overridden version of the method you will have to call your drawRects method.
See Custom Painting Approaches for examples of how to do custom painting using:
A List of objects to paint
a BufferedImage to paint the objects.
The examples will paint rectangles of different colors. So you will need to add code to paint different shapes. But the idea is to understand the basic painting concepts first.

how to draw multiple rectangles

so I am trying to make a simple program where you click on the screen and it creates a block that falls and collides with a larger block beneath and sticks to it. Kind of like a simple collision program. The problem is when I create one block it deletes the block previously. I made an array, but it still does this. Do any of you know what Im doing wrong? Im sure its a simple fix.
public class Screen extends JPanel implements Runnable {
public static JLabel statusbar; //displays a status bar showing what mouse movements are taking place
private Image cat; //image of the cat
public int xCoord ; //get the coordinates of the mouse pressed
public int yCoord ;
public int xCreate;
public int yCreate;
public Rectangle Ground;
public Rectangle Block;
public boolean isClicked = false;
public int clickCount = 0;
Rectangle blocks[] = new Rectangle[10];
int blocknum = 0;
public Screen(Frame frame) {
loadPic(); //calls the loadPic method above
Handlerclass handler = new Handlerclass(); //creates a new class to use the mouse motion listener
System.out.println("mouse works!");
addMouseListener(handler);
addMouseMotionListener(handler);
statusbar = new JLabel("default");
add(statusbar);
}
public void run(){ //this is the game run loop
System.out.println("this is running");
try{
} catch(Exception e) {} //exception handling
}
public void loadPic(){ //loads the picture from the other project but its the same pic
cat = new ImageIcon("C:\\Users\\Camtronius\\Documents\\NetBeansProjects\\Moving Block Proj\\src\\MovingBlock\\catIcon1.png").getImage(); //gets the image
System.out.println("Image Loaded!");
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g); //paints the component, the picture, on top
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(cat, xCoord, yCoord, null);
g2d.setColor(Color.BLUE);
Ground = new Rectangle(0,450,550,50);
g2d.fillRect(0,450, 550, 50);
for(Rectangle blocknum : blocks){
if (blocks != null) {
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
}
}
//move();
}
public void move(){
if(yCreate<400){
yCreate+=1;
}else{
}
if(Ground.intersects(blocks[blocknum])){
yCreate=400;
System.out.println("contains!");
}
}
private class Handlerclass implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent event){
}
public void mousePressed(MouseEvent event){
}
public void mouseReleased(MouseEvent event){
if(blocknum<blocks.length){
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
repaint();
}
blocknum=blocknum+1;
}
public void mouseEntered(MouseEvent event){
}
public void mouseExited(MouseEvent event){
}
public void mouseDragged(MouseEvent event){
}
public void mouseMoved(MouseEvent event){
statusbar.setText(String.format("Coordinates are: %d, %d", event.getX(),event.getY()));
xCoord=event.getX();
yCoord=event.getY();
}
}
}
Painting is a destructive process. That is, when a new paint cycle runs, the previous contents of the Graphics context should be cleared...
So, in you paintComponent method you are only painting the last block...
if(isClicked = true){
blocks[blocknum] = new Rectangle(50,50, xCreate, yCreate);
g2d.setColor(Color.RED);
g2d.fillRect(xCreate,yCreate,50,50);
System.out.println(blocknum);
repaint(); // THIS IS A BAD IDEA
}
DO NOT call any method that might cause repaint to be called. This will put you in a potential cycle of death that will consume your CPU.
Instead, you should loop through the blocks array and paint each one...
for (Rectangle block : blocks) {
if (block != null) {
g2d.setColor(Color.RED);
g2d.fill(block);
}
}
And in you mouseReleased method, you should be adding the new rectangles...
public void mouseReleased(MouseEvent event){
blocknum=blocknum+1;
if (blocknum < blocks.length) {
xCreate=event.getX();
yCreate=event.getY();
blocks[blocknum] = new Rectangle(xCreate, yCreate, 50, 50);
}
}
I'd suggest you take a look at Custom Painting, Painting in AWT and Swing and Concurrency in Swing for more details

JPanel won't paint correctly

I am quite new to Java and Swing, and this is also my first post so sorry if it doesn't make too much sense.
What I am trying to do is when I click on a JPanel, I want it to add a circle where I click. At the moment, all that seems to happen is when I click, a small grey square appears inside the JPanel I want to add to, but I can't seem to find any way of making it draw as a circle.
I have a class that extends JPanel called "Ball" which is what is being added when I click.
At the moment, I am not too worried about it being in the correct location, just for it to draw the ball correctly.
Below is the code for my "Ball" class:
package paintsliders;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
class Ball extends JPanel{
private int x,y,w,h;
//I will use this constructor to put the ball in the correct location later.
Ball(){
/*this.w = 100;
this.h = 100;
this.x = 200;
this.y = 200;*/
}
//draw the ball
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(200,200,10,10);
g.setColor(Color.RED);
}
}
I can kind of guess that it is something to do with the paintComponent method, but everywhere I have looked doesn't seem to have a solution for me.
Any help would be great, thanks!
The Graphcis context has already been translated to meet the x/y location that the component should appear within it's parent container, this means that the top, left corner of the Graphics context within the paintComponent method is actually 0x0.
You need to define some size for the ball, you're painting at 10x10, which would suggest that your ball component should return a preferredSize of 10x10
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
You will become responsible for providing appropriate layout details to the ball when it's added to the parent container...
public void mouseClicked(MouseEvent evt) {
Point p = evt.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
ball.setBounds(new Rectangle(p, size));
add(ball);
}
This, of course, assumes you have a null layout set for the parent container
UPDATED
Something like...
public class PaintBalls {
public static void main(String[] args) {
new PaintBalls();
}
public PaintBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Board());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Board extends JPanel {
public Board() {
setLayout(null);
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
p.x -= size.width / 2;
p.y -= size.height / 2;
ball.setBounds(new Rectangle(p, size));
add(ball);
repaint();
}
});
}
}
public class Ball extends JPanel {
public Ball() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(0, 0, 10, 10);
g2d.dispose();
}
}
}
You probably have a main JPanel where you click.
I would rather design the main panel to handle the mouse click and the Ball class to be a simple Object that defines a drawBall(Graphics g, int x, int y) method that knows how to paint a Ball. This would be called by the paintComponent() method in the main panel. In the main panel, you handle the mouse click, create an object of type Ball and call repaint(). Inside the paintComponent() you call ball.drawBall().

Categories

Resources