Adding JPanel after removing it from JFrame - java
I'm developing a Java Game. I'm stuck at a point,where I need to restart the whole game again after GameOver. Here is the skeleton of my program:
package projectflappy;
import java.awt.*;
public final class TheGame extends JFrame implements MouseListener{
JPanel jp;
//declaration of the varibles
int x_width = 500;
int y_height = 500;
int count = 5 ;
Ellipse2D Ball;
int x_ball;
int y_ball;
int cord_xup1,cord_xdown1;
int cord_xup2,cord_xdown2;
int cord_xup3,cord_xdown3;
int cord_xup4,cord_xdown4;
int cord_xup5,cord_xdown5;
Boolean flag = true;
RoundRectangle2D up1,down1,up2,down2,up3,down3,up4,down4;
Font font = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,40);
Font font1 = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,20);
Font font3 = new Font("Matura MT Script Capitals",Font.ROMAN_BASELINE,20);
float das[] = {10.0f};
BasicStroke color = new BasicStroke(10,BasicStroke.CAP_ROUND,BasicStroke.JOIN_BEVEL,20.0f,das,0.0f);
GradientPaint gp2 = new GradientPaint(20, 0,
Color.DARK_GRAY, 0, 10, Color.GRAY, true);
GradientPaint gp3 = new GradientPaint(30, 0,
Color.BLACK, 0, 20, Color.GREEN, true);
Toolkit kit = Toolkit.getDefaultToolkit();
//Getting the "background.jpg" image we have in the folder
Image background = kit.getImage("D:\\College\\Programs\\ProjectFLAPPY\\src\\projectflappy\\1.png");
JLabel a = new JLabel("Get Ready ! Click to Start.");
JLabel retry = new JLabel(new ImageIcon("D:\\College\\Programs\\ProjectFLAPPY\\src\\projectflappy\\unnamed.png"));
int score = 0;
//constructor
public TheGame() throws IOException
{
super("Simple Drawing");
setSize(x_width, y_height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
jp = new DrawingPanel();
add(jp);
addMouseListener(this);
}
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
update();
repaint();
}
};
Timer t = new Timer(50,action);
public void init()
{
x_ball = 30;
y_ball = 200;
cord_xup1 = 175; cord_xdown1 = 175;
cord_xup2 = 320; cord_xdown2 = 320;
cord_xup3 = 460; cord_xdown3 = 460;
cord_xup4 = 585; cord_xdown4 = 585;
cord_xup5 = 700; cord_xdown5 = 700;
retry.setVisible(false);
retry.setBounds(175,260,46,46);
a.setForeground(Color.YELLOW);
a.setFont(font1);
a.setVisible(true);
a.setBounds(105,200,300,100);
}
#Override
public void mouseClicked(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
a.setVisible(false);
if( flag == false)
{
t.stop();
}
else
{
t.start();
}
y_ball = y_ball - 40;
count--;
}
#Override
public void mousePressed(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseReleased(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseEntered(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseExited(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
// for drawing on the panel
class DrawingPanel extends JPanel{
private static final long serialVersionUID = 1L;
public DrawingPanel() {
setPreferredSize(new Dimension(300, 300));
setLayout(null);
init();
add(a);
add(retry);
// addMouseListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D d = (Graphics2D)g;
d.drawImage(background, -270,-30, this);
Ball = new Ellipse2D.Double(x_ball,y_ball,30,30);
d.setColor(Color.green);
d.setFont(font3);
up1 = new RoundRectangle2D.Double(cord_xup1,-5,30,175,20,20);
down1 = new RoundRectangle2D.Double(cord_xdown1,310,30,155,20,20);
up2 = new RoundRectangle2D.Double(cord_xup2,-5,30,200,20,20);
down2 = new RoundRectangle2D.Double(cord_xdown2,310,30,175,20,20);
up3 = new RoundRectangle2D.Double(cord_xup3,-5,30,230,20,20);
down3 = new RoundRectangle2D.Double(cord_xdown3,350,30,135,20,20);
up4 = new RoundRectangle2D.Double(cord_xup4,-5,30,115,20,20);
down4 = new RoundRectangle2D.Double(cord_xdown4,240,30,115,20,20);
d.setPaint(gp2);
d.setStroke(color);
d.fill(up1);
d.fill(down1);
d.fill(up2);
d.fill(down2);
d.fill(up3);
d.fill(down3);
d.fill(up4);
d.fill(down4);
d.setPaint(gp3);
d.setStroke(color);
d.fill(Ball);
d.setColor(Color.BLACK);
d.setFont(font1);
d.drawString(""+score ,200,50);
if( Ball.intersects(up1.getBounds()) || Ball.intersects(down1.getBounds()) || Ball.intersects(up2.getBounds()) || Ball.intersects(down2.getBounds()) || Ball.intersects(up3.getBounds()) || Ball.intersects(down3.getBounds()) || Ball.intersects(up4.getBounds()) || Ball.intersects(down4.getBounds()))
{
t.stop();
flag = false;
d.setColor(Color.red);
d.setFont(font);
d.drawString("Game Over : "+score ,100,250);
retry.setVisible(true);
}
retry.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent event) {
init(); //reset properties
}
//...
#Override
public void mousePressed(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseReleased(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseEntered(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void mouseExited(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
});
}
}
public void update()
{
cord_xdown1 -= 5;
cord_xup1 -= 5;
cord_xdown2 -= 5;
cord_xup2 -= 5;
cord_xdown3 -= 5;
cord_xup3 -= 5;
cord_xdown4 -= 5;
cord_xup4 -= 5;
cord_xdown5 -= 5;
cord_xup5 -= 5;
if( cord_xup1 <=-20)
{
cord_xup1 = 500;
cord_xdown1 = 500;
}
if( cord_xup2 <=-20)
{
cord_xup2 = 500;
cord_xdown2 = 500;
}
if( cord_xup3 <=-20)
{
cord_xup3 = 500;
cord_xdown3 = 500;
}
if( cord_xup4 <=-20)
{
cord_xup4 = 500;
cord_xdown4 = 500;
}
if( cord_xup5 <=-20)
{
cord_xup5 = 500;
cord_xdown5 = 500;
}
if(count >= 0)
{
y_ball = y_ball - 7;
count--;
if( y_ball == y_height)
{
t.stop();
}
}
else
{
y_ball = y_ball + 7;
if( y_ball == y_height-70)
{
t.stop();
}
}
if(cord_xdown1 == x_ball || cord_xdown2 == x_ball || cord_xdown3 == x_ball || cord_xdown4 == x_ball)
{
score = score+1;
}
}
public static void main(String[] args) throws IOException {
new TheGame();
}
}
here retry is a JLabel in which I'm using a MouseListener to do things.
When I run,the JPanel gets completely removed from the JFrame but the new JPanel really doesn't seem to work. But only one component i.e, a.setVisble(true) works.
This is the frame when the Players gets out.
This Frames when the Player clicks on the retry button.
The reason your new panel is not showing is due to the component hierarchy being invalid. You attempt to revalidate, but you did it before adding the panel. You need to do it AFTER you add a component to an already visible container. Check out invalidate():
This method is called automatically when any layout-related information changes (e.g. setting the bounds of the component, or adding the component to a container).
So you must validate after adding the component, not before. revalidate() invalidates then revalidates the component hierarchy.
The proper way to handle this would be to revert your game back to it's original form; just change everything back to how it was. No need to create a new panel.
You could create a method, init(), which sets your game to how it should be:
//Contains the properties that will change during gameplay
void init() {
retry.setVisible(false);
a.setForeground(Color.YELLOW);
//...
}
Which you can then call when you create the board (in the constructor) and when you press retry (in the listener):
public DrawingPanel() {
setPreferredSize(new Dimension(300, 300));
setPreferredSize(new Dimension(300, 300));
setLayout(null);
init(); //sets properties
a.setFont(font1);
a.setVisible(true);
a.setBounds(105,200,300,100);
add(a);
retry.setBounds(175,260,46,46);
retry.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent event) {
init(); //reset properties
}
//...
});
add(retry);
}
You shouldn't add a listener to a component in your update() method, since update() will be called multiple times. Add it in your constructor.
If retry is a JButton, you should use an ActionListener. I wasn't sure, so I kept it as a mouse listener.
You should avoid using null layout (absolute positioning). Layout Managers position and size components using specific calculations to ensure your resulting GUI looks the same on all platforms. There are a few uses where absolute positioning is a viable option, as mentioned in the tutorials, but it's always best to prefer a Layout Manager. IMO, null layout is bad practice, and the only reason one would use it is if they didn't understand layout managers, which is a problem in itself.
To learn more about layout managers, check out the Visual Guide to Layout Managers trail. Not only does the JDK come bundled with layouts, but you can also create your own or use a third party layout, like MigLayout
EDIT:
Post Swing code to the Event Dispatch Thread. Swing event handlers (painting, listeners) are executed on the Event Dispatch Thread. To ensure the Swing code you write is in sync with the EDT, post any Swing code that isn't already being executed on the EDT to the EDT by using invokeLater or invokeAndWait.
Do not size your frame directly. Allow your frame to size based off the contents inside of it. Your DrawingPanel (the game canvas) should determine the size of the frame.
TheGame should not extend JFrame, since it's not a frame itself, rather than something contained within a frame. Having it extend JPanel would be a little easier on you (you won't be forced to create a new class to override the paint method). Although, TheGame shouldn't extend anything, it should HAVE these things (has-a relationship, not is-a). But since you're still a beginner, I don't wanna overwhelm you with a completely new design, so I considered TheGame to be the actual game canvas (where things will be draw; TheGame will extend JPanel), so you'll no longer need DrawingBoard.
As mentioned before, you should NOT add listeners (or do any task that is only needed once) in the paint method. Keep in mind that the paint method is for painting, not initializing or setting values. You should attempt to keep logic out of that method if possible.
Stay consistent. You use a JLabel for "Click to start!", yet you use drawString for "Game Over". Pick one or the other. This choice is really up to you. For this example, I chose to use drawString, since it's consistent with the rest of your rendering methods (how you paint the background, ball and obstacles)
DO NOT CREATE NEW OBJECTS IN YOUR PAINT METHOD. You're creating a ton of new objects every 50 milliseconds. This is NOT needed, and will harm performance critically. When you use the new keyword, you create a new object. Instead of creating a new object to change it (or revert it back), just change it's state.
Take advantage of Object Orientation. It'll help keep you organized, and allow you to easily manage and scale up your application. Don't shove a bunch of variables into one class to represent a ton of different things (cordx_up1, cordx_up2... it's definitely not scalable).
Look into some of the Adapter classes like MouseAdapter and KeyAdapter; they allow you to handle events without needing to declare methods you might not use.
Use access modifiers. If you aren't familiar with them, get to know them. It makes managing code a lot easier if you know where it can be used ahead of time.
Your paths point to a specific drive with a specific name. This should not be the case, since not everyone uses that drive and/or folder name. Package your images with your project, then refer to them locally.
With that said, you have a lot of studying to do.
What I did was create a Ball class and an Obstacle class, to get a little more organized. ball_x and ball_y are now inside the Ball class, as well as the gradient for it. The objects we create from this class will now have these properties (states). I create 1 ball object for your game.
Instead of creating new variables for each pole (cordx_up1), the Obstacle class has 2 RoundRectangle2D, top and bottom, which are the poles your ball is supposed to avoid. Each obstacle also has a gradient, which is used for both top and bottom. Now I can create 1 obstacle object for 2 aligned poles. You can change the starting x position of the obstacle (although I don't recommend allowing this; x should be set dynamically based on other obstacles' positions), as well as the size for top and bottom. I create 5 obstacle objects.
To keep your game labels organized (by color, message, location, font) while using drawString instead of JLabel, I created a GameLabel class.
I separated the main method into it's own class, named Launcher, which creates a JFrame and adds your game to it; all on the Event Dispatch Thread:
public class Launcher {
public static void main(String[] args) throws IOException {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(new TheGame());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
Your Game class now extends JPanel, so you can override the paint method to render the game. I created 1 Ball and a LinkedList for your obstacles. I chose a LinkedList since inserting/removing from front/end is guaranteed constant time, meaning it'll take the same amount of time to remove/insert no matter how many obstacles are in the list. When the ball passes an obstacle, I remove it from the front of the list and add it to the back. The first obstacle in the list is always the next obstacle.
I saw how some Strings were being re-used, so I created final variables for them, which you can easily change. There's also the currentlyPlaying and isAlive booleans. currentlyPlaying is set to true when the user first clicks (to start the game), and set to false once the user has clicked to restart the game (after he lost).
readyToJump is the flag I use to forward mouse events to your update() method (technically your updatePlayerPostion() method, but it's still "centeralized" within your update() method). It's best to keep all your logic in one place. readyToJump = true would have been the only statement in your listener's method if you weren't relying on calling timer.start() in it. Since update() can't be called unless the timer has started, and mouseEvent starts the timer, we must still handle starting the game in your listener's method.
#SuppressWarnings("serial")
public final class TheGame extends JPanel implements MouseListener, ActionListener {
public static final int WIDTH = 500, HEIGHT = 500;
private final String START_SCORE = "0",
START_MESSAGE = "Get Ready ! Click to Start.",
BACKGROUND_URL = "/res/flappy.png";
private boolean currentlyPlaying, readyToJump, isAlive = true;
private int score;
private Timer timer;
private Image background;
private GameLabel messageLabel, scoreLabel;
private Collection<Obstacle> obstaclesInOrder;
private LinkedList<Obstacle> obstacles;
private Ball ball;
public TheGame() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
addMouseListener(this);
timer = new Timer(50, this);
background = loadBackgroundImage();
messageLabel = new GameLabel(START_MESSAGE, 150, 240);
scoreLabel = new GameLabel(START_SCORE, 250, 60);
obstacles = new LinkedList<>();
obstacles.removeAll(obstacles);
obstaclesInOrder = Arrays.asList(new Obstacle(175, 20, 45), new Obstacle(320), new Obstacle(460), new Obstacle(585), new Obstacle(700));
obstacles.addAll(obstaclesInOrder);
ball = new Ball(30, 100);
}
#Override
public void mouseClicked(MouseEvent e) {
if (!currentlyPlaying) {
startGame();
} else if (!isAlive) {
reset();
}
readyToJump = true;
}
private void startGame() {
currentlyPlaying = true;
messageLabel.update("");
timer.start();
}
private void endGame() {
isAlive = false;
scoreLabel.update("");
messageLabel.update("Game Over. Your score was " + Integer.toString(score));
timer.stop();
}
private void reset() {
ball.reset();
for (Obstacle obstacle : obstacles)
obstacle.reset();
messageLabel.update(START_MESSAGE, 150, 240);
scoreLabel.update(START_SCORE, 250, 60);
obstacles.removeAll(obstacles);
obstacles.addAll(obstaclesInOrder);
score = 0;
isAlive = true;
currentlyPlaying = false;
repaint();
}
#Override
public void actionPerformed(ActionEvent ae) {
update();
repaint();
}
private void update() {
if (isAlive) {
updateBallPosition();
updateObstaclePositions();
if(ballOutOfBounds() || playerCollidedWithObstacle()) {
endGame();
} else if(ballPassedObstacle()) {
addToScore();
setupNextObstacle();
}
}
}
private void updateBallPosition() {
if (readyToJump) {
readyToJump = false;
ball.jump();
} else {
ball.fall();
}
}
private void updateObstaclePositions() {
for (Obstacle obstacle : obstacles) {
if (obstacle.getX() <= -obstacle.getWidth()) {
obstacle.moveToBack();
continue;
}
obstacle.moveForward();
}
}
private void addToScore() {
scoreLabel.update(Integer.toString(++score));
}
private void setupNextObstacle() {
obstacles.addLast(obstacles.removeFirst());
}
private boolean ballOutOfBounds() {
return ball.getY() >= HEIGHT || ball.getY() <= 0;
}
private boolean ballAtObstacle() {
Obstacle currentObstacle = obstacles.getFirst();
return ball.getX() + ball.getWidth() >= currentObstacle.getX() && ball.getX() <= currentObstacle.getX() + currentObstacle.getWidth();
}
private boolean ballPassedObstacle() {
Obstacle currentObstacle = obstacles.getFirst();
return ball.getX() >= (currentObstacle.getX() + currentObstacle.getWidth());
}
private boolean playerCollidedWithObstacle() {
boolean collided = false;
if(ballAtObstacle()) {
for (Obstacle obstacle : obstacles) {
RoundRectangle2D top = obstacle.getTop();
RoundRectangle2D bottom = obstacle.getBottom();
if (ball.intersects(top.getX(), top.getY(), top.getWidth(), top.getHeight()) || ball.intersects(bottom.getX(), bottom.getY(), bottom.getWidth(), bottom.getHeight())) {
collided = true;
}
}
}
return collided;
}
private Image loadBackgroundImage() {
Image background = null;
URL backgroundPath = getClass().getResource(BACKGROUND_URL);
if(backgroundPath == null) {
background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
} else {
try {
background = ImageIO.read(backgroundPath);
} catch (IOException e) {
e.printStackTrace();
}
}
return background;
}
#Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D g = (Graphics2D) graphics;
g.drawImage(background, 0, 0, null);
ball.paint(g);
for (Obstacle obstacle : obstacles)
obstacle.paint(g);
scoreLabel.paint(g);
messageLabel.paint(g);
}
//...
}
loadBackgroundImage() loads and returns an image. If a problem occurs (image probably isn't there), it returns a black image.
(Most of) The game logic is in the update() method. Although it should all be in there, we can't due to a design flaw (from you using Timer which manages update(), and you start the timer in a listener, therefore we needed some logic in the listener). Your logic should be easy to read, and every execution step should be monitored and set by highest priority to lowest.
First, I check to make sure the ball hasn't collided with anything or gone out of bounds. If one of those things occur, I end the game.
If not, I check to see if the player has passed an obstacle. If the player passes an obstacle, I add to the score:
private void update() {
if (ballOutOfBounds() || playerCollidedWithObstacle()) {
endGame();
} else if (ballPassedObstacle()) {
addToScore();
setupNextObstacle();
}
updateBallPosition();
updateObstaclePositions();
}
I then finally update the positions.
To avoid constantly comparing the player's position and the obstacle's position (to see if the player has passed it), I created a boolean ballAtObstacle() method, which checks if the player is at an obstacle. Only then do I compare positions:
private boolean playerCollidedWithObstacle() {
boolean collided = false;
if (ballAtObstacle()) {
for (Obstacle obstacle : obstacles) {
RoundRectangle2D top = obstacle.getTop();
RoundRectangle2D bottom = obstacle.getBottom();
if (ball.intersects(top.getX(), top.getY(), top.getWidth(), top.getHeight()) || ball.intersects(bottom.getX(), bottom.getY(), bottom.getWidth(), bottom.getHeight())) {
collided = true;
}
}
}
return collided;
}
The ball.intersects method call is a bit messy. I did this so the shape didn't have to be specific, although you could declare the intersects method as boolean intersects(Shape shape).
Finally, I gave it a more flappy bird feel by increasing the fall speed as you fall. When you jump again, it goes back to normal. The longer it takes you to jump, the faster you'll fall. If you don't like this feature, and don't know how to remove it, let me know and I'll show you how.
The other classes involved:
GameLabel.java
public class GameLabel {
private String message;
private Font font;
private Color color;
private int x, y;
public GameLabel(String message, int x, int y, Color color, Font font) {
update(message, x, y, color, font);
}
public GameLabel(String message, int x, int y) {
this(message, x, y, Color.BLACK, new Font("Matura MT Script Capitals", Font.ROMAN_BASELINE, 20));
}
public GameLabel() {
this("", 0, 0);
}
public void setMessage(String message) {
this.message = message;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColor(Color color) {
this.color = color;
}
public void setFont(Font font) {
this.font = font;
}
public final void update(String message, int x, int y, Color color, Font font) {
this.message = message;
this.x = x;
this.y = y;
this.color = color;
this.font = font;
}
public void update(String message, int x, int y) {
update(message, x, y, color, font);
}
public void update(String message) {
update(message, x, y);
}
public void paint(Graphics2D g) {
g.setFont(font);
g.setColor(color);
g.drawString(message, x, y);
}
public Font getFont() {
return font;
}
public Color getColor() {
return color;
}
public String getMessage() {
return message;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Obstacle.java
public class Obstacle {
public static final int DEFAULT_TOP_HEIGHT = 175;
public static final int DEFAULT_BOTTOM_HEIGHT = 175;
public static final int DEFAULT_WIDTH = 30;
public static final int DEFAULT_ARCH_WIDTH = 20;
public static final int DEFAULT_ARCH_HEIGHT = 20;
public static final int DEFAULT_TOP_INSET = -5;
public static final int DEFAULT_BOTTOM_INSET = TheGame.HEIGHT + 5;
private RoundRectangle2D top, bottom;
private BasicStroke stroke;
private GradientPaint gradient;
private int initialX, x, width;
public Obstacle(int x, int width, int topHeight, int bottomHeight) {
this.x = initialX = x;
this.width = width;
top = new RoundRectangle2D.Double(x, DEFAULT_TOP_INSET, width, topHeight, DEFAULT_ARCH_WIDTH, DEFAULT_ARCH_HEIGHT);
bottom = new RoundRectangle2D.Double(x, DEFAULT_BOTTOM_INSET-bottomHeight, width, bottomHeight, DEFAULT_ARCH_WIDTH, DEFAULT_ARCH_HEIGHT);
stroke = new BasicStroke(10, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 20.0f, new float[] { 10.0f }, 0.0f);
gradient = new GradientPaint(20, 0, Color.DARK_GRAY, 0, 10, Color.GRAY, true);
}
public Obstacle(int x, int topHeight, int bottomHeight) {
this(x, DEFAULT_WIDTH, topHeight, bottomHeight);
}
public void reset() {
x = initialX;
top.setRoundRect(initialX, top.getY(), top.getWidth(), top.getHeight(), top.getArcWidth(), top.getArcHeight());
bottom.setRoundRect(initialX, bottom.getY(), bottom.getWidth(), bottom.getHeight(), bottom.getArcWidth(), bottom.getArcHeight());
}
public Obstacle(int x, int width) {
this(x, width, DEFAULT_TOP_HEIGHT, DEFAULT_BOTTOM_HEIGHT);
}
public Obstacle(int x) {
this(x, DEFAULT_WIDTH);
}
public void moveToBack() {
x = 600;
}
public void moveForward() {
x -= 5;
top.setRoundRect(x, top.getY(), top.getWidth(), top.getHeight(), top.getArcWidth(), top.getArcHeight());
bottom.setRoundRect(x, bottom.getY(), bottom.getWidth(), bottom.getHeight(), bottom.getArcWidth(), bottom.getArcHeight());
}
public RoundRectangle2D getTop() {
return top;
}
public RoundRectangle2D getBottom() {
return bottom;
}
public int getX() {
return x;
}
public int getWidth() {
return width;
}
public void paint(Graphics2D g) {
g.setPaint(gradient);
g.setStroke(stroke);
g.fill(top);
g.fill(bottom);
}
}
Ball.java
public class Ball {
public static final int DEFAULT_DROP_SPEED = 7;
private Ellipse2D ball;
private GradientPaint gradient;
private int initialY, x, y, width = 30, height = 30, dropSpeed = DEFAULT_DROP_SPEED;
public Ball(int x, int y) {
this.x = x;
this.y = initialY = y;
width = height = 30;
ball = new Ellipse2D.Double(x, y, width, height);
gradient = new GradientPaint(30, 0, Color.BLACK, 0, 20, Color.GREEN, true);
}
public void reset() {
y = initialY;
updateBall();
}
public void jump() {
dropSpeed = DEFAULT_DROP_SPEED;
y -= 40;
updateBall();
}
public void fall() {
y += dropSpeed++;
updateBall();
}
private void updateBall() {
ball.setFrame(x, y, width, height);
}
public boolean intersects(double x, double y, double w, double h) {
return ball.intersects(x, y, w, h);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return width;
}
public void paint(Graphics2D g) {
g.setPaint(gradient);
g.fill(ball);
}
public void moveForward() {
x += 7;
}
}
Reinitialize all the components by adding the initialization code to one method(resetAll) and call that method when u want to reinitailize
Below is an example:
import java.awt.*;
import java.awt.event.*;
public class ResetTest extends Frame{
Button b;
TextField tf;
Frame f;
Panel p;
public ResetTest(){
f=this; //intialize frame f to this to have access to our frame in event handler methods
resetAll(); //first time call to resetAll will initialize all the parameters of the frame
f.pack();
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
this.setLayout(new FlowLayout(FlowLayout.CENTER,20,20));
this.setVisible(true);
}
/**
This method will be called on click of button to
reset all the parameters of the frame so that we
get fresh new frame, everything as it was before.
**/
public void resetAll(){
b=new Button("Reset");
p=new Panel(new FlowLayout(FlowLayout.CENTER,20,20));
p.add(b);
tf = new TextField("Edit And Reset");
p.add(tf);
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
remove(p); //remove the panel that contains all our components
resetAll(); // reset all the components
f.pack(); //refreshes the view of frame when everything is reset.
}
});
add(p);
}
}
class NewClass{
public static void main(String[] args) {
ResetTest fReset = new ResetTest();
}
}
Related
Why the menubar is displayed twice, and the drawn lines only appear on a quarter of the page? [closed]
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers. This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers. Closed 2 years ago. Improve this question I am learning Java. And I have a problem, certainly basic. The menubar is displayed twice, and the drawn lines only appear on a quarter of the page. Do you think there is a relation between the Panel and the JMenuBar ? Do you think the problem is related to the repaint() method ? Here is the code : Class panel: public class Panel extends JPanel { private String colorName = "All"; private Color color = Color.RED; private String shape = "Circle"; private int size1 = 30; private int x = 0, y = 0; private boolean allow = false; #Override public void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponent(g); if (allow == true) { if (colorName == "Red") g.setColor(Color.RED); else if (colorName == "Blue") g.setColor(Color.BLUE); else if (colorName == "All") { int r = (int) ((Math.random()) * 256); int v = (int) ((Math.random()) * 256); int b = (int) ((Math.random()) * 256); color = new Color(r, v, b); g.setColor(color); } if (shape == "Circle") g.fillOval(x, y, size1, size1); if (shape == "Square") g.fillRect(x, y, size1, size1); } repaint(); } public String getColorName() { return colorName; } public void setColorName(String colorName) { this.colorName = colorName; } public boolean isAllow() { return allow; } public void setAllow(boolean allow) { this.allow = allow; } public String getShape() { return shape; } public void setForme(String forme) { this.shape = shape; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getSize1() { return size1; } public void setSize1(int size1) { this.size1 = size1; } } Class frame : public class Frame extends JFrame{ JMenuBar mb = new JMenuBar(); JMenu file = new JMenu("File"); JMenu edit = new JMenu("Edit"); Panel p = new Panel(); public Frame() { this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLocationRelativeTo(null); this.setSize(400,400); this.setTitle("Test"); this.addMouseListener(new MouseAdapter(){ #Override public void mousePressed(MouseEvent e) { int a = e.getX() - (p.getSize1()/2); int b = e.getY() - (p.getSize1()); p.setX(a); p.setY(b); p.setAllow(true); } }); this.addMouseMotionListener(new MouseMotionListener() { #Override public void mouseMoved(MouseEvent e) { } #Override public void mouseDragged(MouseEvent e) { int a = e.getX() - (p.getSize1()/2); int b = e.getY() - (p.getSize1()); p.setX(a); p.setY(b); p.setAllow(true); } }); mb.add(file); mb.add(edit); this.setJMenuBar(mb); this.setLayout(new BorderLayout()); this.add(p, BorderLayout.CENTER); this.setVisible(true); } public static void main(String[] args) { new Frame(); } } Can you help me please?
You've a very bad typographical bug in your code: #Override public void paintComponent(Graphics g) { // TODO Auto-generated method stub super.paintComponents(g); should be: #Override protected void paintComponent(Graphics g) { super.paintComponent(g); Please note the difference in the super call since paintComponents != paintComponent Also, don't compare Strings using == or !=. Use the equals(...) or the equalsIgnoreCase(...) method instead. Understand that == checks if the two object references are the same which is not what you're interested in. The methods on the other hand check if the two Strings have the same characters in the same order, and that's what matters here. And so, not: if (colorName == "Red") { // .... } but rather: if (colorName.equalsIgnoreCase("Red")) { // .... } Also, never call repaint() within a painting method. Also, don't randomize the painting or change object state within paintComponent since you do not have control over when or if the method will be called. Randomize in a listener, such as the mouse listener, and if you need animation separate from what the mouse listener provides, use a Swing Timer ALSO This: public int getX() { // ... } Overrides a key method of JPanel inherited from the Component class, that is key to placement of the JPanel, same for getY(). Rename those methods ASAP
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.
Java - How to Create a MultiThreaded Game using SwingWorker
I want to Create a [1 Player vs PC] Game with Threads. we have 10*10 two Colors Shapes in our board like this : when the Player clicks on BLUE Circles , Their color turns into Gray. at the other side PC should turn all RED Rectangles into Gray. the WINNER is who Clears all his/her own Shapes Earlier. Code for The Player works fine but, My Problem is in Implementing The PC side of the Game, as i read in this article i should use SwingWorker to Implement Threading in GUI. it's my first time using SwingWorkers and i don't know how it should be to works properly. Here is my Codes : The Main Class public class BubblePopGame { public static final Color DEFAULT_COLOR1 = Color.BLUE; public static final Color DEFAULT_COLOR2 = Color.RED; public BubblePopGame() { List<ShapeItem> shapes = new ArrayList<ShapeItem>(); int Total = 10; for (int i = 1; i <= Total; i++) { for (int j = 1; j <= Total; j++) { if ((i + j) % 2 == 0) { shapes.add(new ShapeItem(new Ellipse2D.Double(i * 25, j * 25, 20, 20), DEFAULT_COLOR1)); } else { shapes.add(new ShapeItem(new Rectangle2D.Double(i * 25, j * 25, 20, 20), DEFAULT_COLOR2)); } } } JFrame frame = new JFrame("Bubble Pop Quest!!"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ShapesPanel panel = new ShapesPanel(shapes); frame.add(panel); frame.setLocationByPlatform(true); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new BubblePopGame(); } }); } } Shape Item Class public class ShapeItem { private Shape shape; private Color color; public ShapeItem(Shape shape, Color color) { super(); this.shape = shape; this.color = color; } public Shape getShape() { return shape; } public void setShape(Shape shape) { this.shape = shape; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } } ShapesPanel Class public class ShapesPanel extends JPanel { private List<ShapeItem> shapes; private Random rand = new Random(); private SwingWorker<Boolean, Integer> worker; public ShapesPanel(List<ShapeItem> shapesList) { this.shapes = shapesList; worker = new SwingWorker<Boolean, Integer>() { #Override protected Boolean doInBackground() throws Exception { while (true) { Thread.sleep(200); int dim = rand.nextInt(300); publish(dim); return true; } } #Override protected void done() { Boolean Status; try { Status = get(); System.out.println(Status); super.done(); //To change body of generated methods, choose Tools | Templates. } catch (InterruptedException ex) { Logger.getLogger(ShapesPanel.class.getName()).log(Level.SEVERE, null, ex); } catch (ExecutionException ex) { Logger.getLogger(ShapesPanel.class.getName()).log(Level.SEVERE, null, ex); } } #Override protected void process(List<Integer> chunks) { int mostRecentValue = chunks.get(chunks.size()-1); System.out.println(mostRecentValue); Color color2 = Color.LIGHT_GRAY; ShapeItem tmpShape = shapes.get(mostRecentValue); if(tmpShape.getColor()==Color.RED){ tmpShape.setColor(color2); } repaint(); } }; worker.execute (); addMouseListener(new MouseAdapter() { #Override public void mouseClicked(MouseEvent e) { Color color1 = Color.LIGHT_GRAY; for (ShapeItem item : shapes) { if (item.getColor() == Color.BLUE) { if (item.getShape().contains(e.getPoint())) { item.setColor(color1); } } } repaint(); } }); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g.create(); for (ShapeItem item : shapes) { g2.setColor(item.getColor()); g2.fill(item.getShape()); } g2.dispose(); } #Override public Dimension getPreferredSize() { return new Dimension(300, 300); } private Color getRandomColor() { return new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()); } }
If I understood your code correctly, you are making a game where the human player has to click as fast as possible on all of his shapes while the PC is randomly clicking on shapes as well. The first one to clear all of his shapes win. If that is correct, you probably want to adjust your SwingWorker to loop until the game is finished. Currently your loop exit the first time the end of the loop is reached due to the return statement Since you are not doing anything with the boolean return value of the SwingWorker, you might as well let it return void No need to call get in the done method. The moment that method is called, the SwingWorker has finished. You only seem interested in the intermediate results In the process method, you might want to loop over all values. Note that the process method is not called each time you publish something. The values you publish are grouped and passed in bulk to the process method when the EDT (Event Dispatch Thread) is available
Switch imageIcon in java?
I have many planes(threads) that move in window, and I want switch the ImageIcon according to the direction of the plane. For example: if a plane goes to the right, the imageIcon of the plane is right and then plane goes to the left, exchange the imageIcon for the plane is left. How can I do that in method paintComponent? Sorry for my bad english.
If you're talking about swapping the ImageIcon displayed by a JLabel, then you should not switch ImageIcons in paintComponent but rather should do this in the non-paintComponent region of code, perhaps in a Swing Timer. Even if you're not talking about a JLabel, the paintComponent method should not be used for changing the state of an object. Your question however leaves too much unsaid to allow us to be able to answer it completely and well. Consider telling and showing more.
If you are looking for a logic thingy, then a small example is here, though you might have to modify it for your needs. import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.util.Random; import javax.swing.*; public class FlyingAeroplane { private Animation animation; private Timer timer; private ActionListener timerAction = new ActionListener() { #Override public void actionPerformed(ActionEvent ae) { animation.setValues(); } }; private void displayGUI() { JFrame frame = new JFrame("Aeroplane Flying"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); animation = new Animation(); frame.setContentPane(animation); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); timer = new Timer(100, timerAction); timer.start(); } public static void main(String... args) { SwingUtilities.invokeLater(new Runnable() { #Override public void run() { new FlyingAeroplane().displayGUI(); } }); } } class Animation extends JPanel { private final int HEIGHT = 150; private final int WIDTH = 200; private int x; private int y; private ImageIcon image; private boolean flag; private Random random; public Animation() { x = 0; y = 0; image = new ImageIcon(getClass().getResource("/image/aeroplaneright.jpeg")); flag = true; random = new Random(); } public void setValues() { x = getXOfImage(); y = random.nextInt(70); repaint(); } private int getXOfImage() { if (flag) { if ((x + image.getIconWidth()) == WIDTH) { flag = false; x--; return x; } x++; image = new ImageIcon(getClass().getResource("/image/aeroplaneright.jpeg")); } else if (!flag) { if (x == 0) { flag = true; x++; return x; } x--; image = new ImageIcon(getClass().getResource("/image/aeroplaneleft.jpeg")); } return x; } #Override public Dimension getPreferredSize() { return (new Dimension(WIDTH, HEIGHT)); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image.getImage(), x, y, this); } } IMAGES USED :
On setting the direction you should set the image icon too, or have a getImageIcon(direction). In the paintComponent no heavy logic should happen; it should be as fast as possible. You have no (total) control when and how often paintComponent is called.
Remembering where a mouse clicked? ArrayLists? HashCodes?
Sorry guys, I deleted my APPLES and CATS example :) Here's the updated version of my question! I'm losing my sanity here. I need someone who can enlighten me. I've tried a couple of times explaining my problem here. Hopefully, this time, my question will be easier to understand. Basically I have this frame, and there's an image displayed. There is a JList on the right, and there is another panel for JLabels at the bottom. Here's a screencap of my frame. When I click on the image, a JOptionPane pops out, like so. And I enter my input. My JList is an ArrayList, so everything I input is added to the JList and the JPanel at the bottom. Now, when I hover on the the part where I clicked, you noticed that the square disappeared). It only appears when I click the image, and when I hover the label at the bottom. My labels, as of now are LOLZ NOSE and INPUT HERE. What I want to do is when I hover on the label, for example INPUT HERE, it shows the square again, featuring the part where I clicked. My problem now is when I click on NOSE, which is supposed to be showing a square on the nose part and a the name NOSE with black bg, IT IS NOT SHOWING. Also, only the last label's square is shown, disregarding the other labels' position clicked. How do I get a label to remember the position of the click I make? People said I should use ArrayLists or HashCodes however I have no idea how to implement them. Thank you to anyone who can help. Edit: I've already done the rectangle, btw. It's showing only for the last label inputted. Here are some of the code snippets requested! How I'm setting the text on JLabel and updating the JList: public void updateLabel(){ StringBuilder text = new StringBuilder(); //creates empty builder, capacity 16 for(Object s: tagModel.toArray()) //returns an array containing the elements of the tagModel text.append(" " + s); repaint(); hoverLabel.setText(text.toString()); //returns a String hoverLabel.addMouseMotionListener(this); hoverPanel.add(hoverLabel); } My mouseListener upon click: #Override public void mouseClicked(MouseEvent event) { // TODO Auto-generated method stub x = event.getX(); y = event.getY(); isRectPresent = true; repaint(); input = JOptionPane.showInputDialog("Enter tag name:"); if((input != null) && !input.isEmpty()){ tagModel.addElement(input); } } My mouseMotionListener upon hovering: #Override public void mouseMoved(MouseEvent e) { // TODO Auto-generated method stub xpos = e.getX(); //gets where the mouse moved ypos = e.getY(); //checks if the mouse is inside the bounds of the rectangle if (xpos > x && xpos < x + 100 && ypos > y && ypos < y + 100) isRectPresent = false; if(e.getSource() == hoverLabel){ isRectPresent = true; repaint(); } repaint(); } How I'm painting: public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D) g; g2.drawImage(image, 0, 0, null); if(image != null && isRectPresent){ Stroke stroke = g2.getStroke(); g2.setStroke(new BasicStroke(4)); g2.setColor(Color.WHITE); g2.drawRect(x-50, y-50, 100, 100); g2.setStroke(stroke); }else{ if(xpos > x && xpos < x + 100 && ypos > y && ypos < y + 100){ g.setColor(Color.BLACK); g.fillRect(x-50, y-50, 100, 25); g.setColor(Color.WHITE); g.setFont(new Font("Tahoma", Font.BOLD, 12)); g.drawString(input, x-30, y-30); } } } If you want me to add some more snippets, just tell me! :)
You should create a HashMap, say something like: Map linkSet = new HashMap(); And whenever you click on the drawing and create a label, add the JLabel and the point on the image to the set using the put method with the JLabel as the key and the Point as the value. Then in the JLabel's MouseMotionListener, use your label as a key and obtain the corresponding point from the set using the map's get(...) method. edit: Corrected as per alicedimarco's comment. Again, thanks! edit 2 I think you want again to use a Map. If you have a Map, you can have it retrieve the Point of interest from the JLabel's or the JList's String, and then pass this Point to the class that's drawing the image and let it use the Point to draw a rectangle. For instance you could give the image drawing class a Point field called displayPoint, and a method called setDisplayPoint(Point p). It can be as simple as this: public void setDisplayPoint(Point p) { this.displayPoint = p; repaint(); } and assuming that the object of interest is centered at that point, use the displayPoint in the paintComponent method: protected void paintComponent(Graphics g) { super.paintComponent(g); // draw image if (img != null) { g.drawImage(img, X_SHIFT, Y_SHIFT, null); } // if displayPoint not null, draw the surrounding rectangle if (displayPoint != null) { g.setColor(RECT_COLOR); int x = displayPoint.x - RECT_WIDTH / 2; int y = displayPoint.y - RECT_WIDTH / 2; int width = RECT_WIDTH; int height = RECT_WIDTH; g.drawRect(x, y, width, height); } } edit 3: To get mouse clicks, it's quite easy, simply add a MouseListener to the component that holds the image: // !! added imgRect.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { imgMousePressed(e); } }); And in your code that is called from this mouse listener, use a JOptionPane to get the user's choice of tag name, and add the resulting String to both the listDataModel so that it is seen in the JList and also in the stringPointMap together with the Point obtained from the MouseEvent so that you can map the String to the Point and be able to retrieve it: // !! added private void imgMousePressed(MouseEvent e) { String result = JOptionPane.showInputDialog(this, "Please enter name for this point on image:"); if (result != null) { stringPointMap.put(result, e.getPoint()); listDataModel.addElement(result); } } That's it. Then putting it all together: import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.*; #SuppressWarnings("serial") public class ImageRectMain extends JPanel { private ImageRect imgRect; private DefaultListModel listDataModel = new DefaultListModel(); private JList list = new JList(listDataModel); private Map<String, Point> stringPointMap = new HashMap<String, Point>(); public ImageRectMain() { String nose = "Nose"; String ear = "Ear"; String rightEye = "Right Eye"; String leftEye = "Left Eye"; listDataModel.addElement(ear); listDataModel.addElement(nose); listDataModel.addElement(rightEye); listDataModel.addElement(leftEye); stringPointMap.put(nose, new Point(480, 500)); stringPointMap.put(ear, new Point(270, 230)); stringPointMap.put(rightEye, new Point(380, 390)); stringPointMap.put(leftEye, new Point(662, 440)); MouseAdapter listMouseAdapter = new MouseAdapter() { #Override public void mouseMoved(MouseEvent e) { listMouseMoved(e); } #Override public void mouseExited(MouseEvent e) { listMouseExited(e); } }; list.addMouseMotionListener(listMouseAdapter); list.addMouseListener(listMouseAdapter); try { imgRect = new ImageRect(); // !! added imgRect.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { imgMousePressed(e); } }); JPanel eastPanel = new JPanel(); eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.PAGE_AXIS)); eastPanel.add(new JLabel("You have tagged the following:")); eastPanel.add(new JScrollPane(list)); eastPanel.add(Box.createVerticalGlue()); eastPanel.add(Box.createVerticalGlue()); eastPanel.add(Box.createVerticalGlue()); eastPanel.add(Box.createVerticalGlue()); setLayout(new BorderLayout()); add(imgRect, BorderLayout.CENTER); add(eastPanel, BorderLayout.EAST); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // !! added private void imgMousePressed(MouseEvent e) { String result = JOptionPane.showInputDialog(this, "Please enter name for this point on image:"); if (result != null) { stringPointMap.put(result, e.getPoint()); listDataModel.addElement(result); } } private void listMouseExited(MouseEvent e) { imgRect.setDisplayPoint(null); } private void listMouseMoved(MouseEvent e) { int index = list.locationToIndex(e.getPoint()); Object value = listDataModel.get(index); if (value != null) { Point point = stringPointMap.get(value.toString()); if (point != null) { imgRect.setDisplayPoint(point); } } } private static void createAndShowGui() { JFrame frame = new JFrame("ImageRectMain"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new ImageRectMain()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } #SuppressWarnings("serial") class ImageRect extends JPanel { public static final String IMAGE_PATH = "http://i.stack.imgur.com/7oNzg.jpg"; private static final int DEFAULT_W = 687; private static final int DEFAULT_H = 636; private static final int X_SHIFT = -6; private static final int Y_SHIFT = -26; private static final Color RECT_COLOR = Color.pink; private static final int RECT_WIDTH = 40; private BufferedImage img; private Point displayPoint = null; public ImageRect() throws MalformedURLException, IOException { img = ImageIO.read(new URL(IMAGE_PATH)); } public void setDisplayPoint(Point p) { this.displayPoint = p; repaint(); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (img != null) { g.drawImage(img, X_SHIFT, Y_SHIFT, null); } if (displayPoint != null) { g.setColor(RECT_COLOR); int x = displayPoint.x - RECT_WIDTH / 2; int y = displayPoint.y - RECT_WIDTH / 2; int width = RECT_WIDTH; int height = RECT_WIDTH; g.drawRect(x, y, width, height); } } #Override public Dimension getPreferredSize() { return new Dimension(DEFAULT_W, DEFAULT_H); } }
One nice feature of a JList is that you can story any object in it. You're not limited to strings. When objects are stored in JLists, swing will call the object's toString() method, and display it in the list. Knowing this, you can now write your own class that stores the name of your selection label and the coordinates of the box. This object's toString() method will return the name of the label, which will make the right thing appear in the JList. Then, in the selection event handler for the JList, you can get your custom object out, and retrieve the box coordinates stored in it, and draw them on the screen. No need to fuss with other containers (although knowing how to use them is a good thing to). Ok, Create a class like this... public class MyLabel { private int x; private int y; private String text; public MyLabel (String text, int x, int y) { this.text = text; // assign x and y too... } #Override public String toString() { return label; } public int getX() { return x; } // similar function to getY() } The class above overrides toString(), so when you put it in a JList, it will use the call to toString() to determine what to display, and since this toString implementation returns the label name you'll see that in the list. And add those into your JList instead of Strings. Then at some point in your code you'll do something like this... // this is pseudocode, method names may not be correct... MyLabel ml = (MyLabel)jList.getSelectedItem(); int x = ml.getX(); int y = ml.getY(); // draw the box... Hope that helps.