My program creates randomly colored and sized worms that expand on a JFrame/JPanel. As time progresses the worms are constantly expanding in random directions. When new worm is clicked a new worm is born somewhere on the screen.
Where my issue arises:
I having trouble understanding how I would then kill worms. When I click the kill worm button I want the worm to disappear (OR stop growing) on screen and it to be removed from the arraylist. I have no idea how to even begin doing this. I personally think removing an instance of the arraylist would be the best way, but how would I go about actually removing the worm from the screen.
Below is my code:
Main Class:
package Main;
import java.awt.Dimension;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
ThreadFrame myFrame = new ThreadFrame();
myFrame.setSize(new Dimension(640, 480));
myFrame.setLocationRelativeTo(null);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setTitle("Worms! - Jonathan Perron");
myFrame.setVisible(true);
myFrame.setResizable(false);
}
}
ThreadFrame Class:
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ThreadFrame extends JFrame implements ActionListener {
int index = 0;
JButton btnNewWorm, btnKillWorm;
JPanel myPanel2 = new JPanel();
ArrayList<DrawThread> worms = new ArrayList<DrawThread>();
public JPanel getMyPanel2(){
return this.myPanel2;
}
public ThreadFrame() {
JPanel myPanel = new JPanel();
btnNewWorm = new JButton("New Worm");
btnKillWorm = new JButton("Kill Worm");
myPanel.setBounds(0, 400, 640, 80);
myPanel.setLayout(new FlowLayout());
myPanel2.setSize(new Dimension(640, 400));
myPanel2.setLayout(null);
myPanel2.setBackground(Color.WHITE);
btnNewWorm.setBounds(100, 410, 200, 30);
btnKillWorm.setBounds(340, 410, 200, 30);
add(btnNewWorm);
add(btnKillWorm);
add(myPanel2);
add(myPanel);
btnNewWorm.addActionListener(this);
btnKillWorm.addActionListener(this);
}
public void actionPerformed(ActionEvent AE) {
if(AE.getSource() == btnNewWorm){
DrawThread dw = new DrawThread(myPanel2);
worms.add(dw);
System.out.println("New worm!");
}
if(AE.getSource() == btnKillWorm){
//stop worms from growing or complete disappear from JFrame
System.out.println("Kill worm!");
}
}
}
DrawThread Class:
package Main;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.Random;
import javax.swing.JPanel;
public class DrawThread extends Thread implements Runnable{
Random rand = new Random();
JPanel panel2 = new JPanel();
Graphics2D g, graph;
private int sleepTime, wormDiameter, hue, saturation, brightness, randomWidth, randomHeight;
public DrawThread(int sleepTime, int wormDiameter, int hue, int saturation, int brightness, int randomWidth, int randomHeight, JPanel myPanel2) {
this.sleepTime = sleepTime;
this.wormDiameter = wormDiameter;
this.brightness = brightness;
this.hue = hue;
this.saturation = saturation;
this.randomWidth = randomWidth;
this.randomHeight = randomHeight;
g = (Graphics2D) myPanel2.getGraphics();
}
public void setColor(int hue){
this.hue = hue;
}
public int getSleepTime(){
return sleepTime;
}
public void setSleepTime(int sleepTime){
this.sleepTime = sleepTime;
}
public DrawThread(JPanel myPanel2){
//get panel dimensions
int panelWidth = myPanel2.getWidth();
int panelHeight = myPanel2.getHeight();
//worm location
randomWidth = rand.nextInt(panelWidth);
randomHeight = rand.nextInt(panelHeight);
//worm size
wormDiameter = rand.nextInt(7)+3;
//worm color
hue = rand.nextInt(255);
saturation = rand.nextInt(255);
brightness = rand.nextInt(255);
Color color = new Color(hue, saturation, brightness);
//sleep
sleepTime = rand.nextInt(80) + 20;
//Graphics
g = (Graphics2D) myPanel2.getGraphics();
g.setColor(color);
Ellipse2D.Double ellipse2D = new Ellipse2D.Double(randomWidth, randomHeight, wormDiameter, wormDiameter);
g.fill(ellipse2D);
Thread thread1 = new Thread(new DrawThread(sleepTime, wormDiameter, hue, saturation, brightness, randomWidth, randomHeight, myPanel2));
thread1.start();
}
public void run(){
try {
while(true) {
Thread.sleep(sleepTime);
Color color = new Color(hue, saturation, brightness);
g.setColor(color);
int addedHeight = 0, addedWidth = 0;
int random = rand.nextInt(8);
//determining the worms next move location
if(random == 0){ addedWidth = 0; addedHeight = 1; } //North
if(random == 1){ addedWidth = 1; addedHeight = 1; } //North-East
if(random == 2){ addedWidth = 1; addedHeight = 0; } //East
if(random == 3){ addedWidth = 1; addedHeight = -1; } //South-East
if(random == 4){ addedWidth = 0; addedHeight = -1; } //South
if(random == 5){ addedWidth = -1; addedHeight = -1; } //South-West
if(random == 6){ addedWidth = -1; addedHeight = 0; } //West
if(random == 7){ addedWidth = -1; addedHeight = 1; } //North-West
//Prevent worms from getting off the screen
if(randomHeight >= 480){ addedHeight = -1; }
if(randomHeight <= 0){ addedHeight = 1; }
if(randomWidth >= 640){ addedWidth = -1; }
if(randomWidth <= 0){ addedWidth = 1; }
randomWidth += addedWidth;
randomHeight += addedHeight;
Ellipse2D.Double e = new Ellipse2D.Double(randomWidth, randomHeight, wormDiameter, wormDiameter);
g.fill(e);
}
}
catch (InterruptedException e) {
System.out.println("ERROR!");
}
}
public String toString() {
String result = "SleepTime: " + sleepTime + "\nWorm Diameter: " + wormDiameter
+ "\nHue: " + hue + "\nSaturation: " + saturation + "\nBrightness: "
+ brightness + "\nWidth: " + randomWidth + "\nHeight: " + randomHeight;
return result;
}
}
Any help is greatly appreciated! :)
EDIT: This is the assignment that my teacher has given to write this program.
===========================================================================
In this assignment, we’re going to write a program that draws images of “worms” in a window. The
worms will grow with time, moving in random directions. Each worm will be a different color and will
grow at a different rate. A separate Thread object will manage the drawing of each worm. Here’s an
example of what the window will look like after two worms have grown for a while.
Write a class called ThreadFrame that extends JFrame. This class should include a main method that
creates one instance of this class. This should produce a GUI with an appearance similar to what you see
above. Make the window 640 by 480 pixels, and do not allow the user to resize it. Add two JPanels to
the content pane, a white one in the center region on which the threads will be drawn, and a gray one in
the south region to hold the JButtons marked “New Worm” and “Kill Worm”.
Add an action listener to the “New Worm” button, so that when you click on it, it creates a new instance
of a class called DrawThread (to be described shortly), adds it to an ArrayList, and starts it. Add an action
listener to the “Kill Worm” button, so that when you click on it, it removes the first DrawThread from
the ArrayList and interrupts it.
The class DrawThread extends Thread, and does most of the work of the program. This class will draw
on the upper panel of the ThreadFrame, so a reference to this panel must be passed to the constructor
of DrawThread, when this constructor is called from ThreadFrame. The constructor should perform the
following tasks:
Assign the JPanel reference (received as an argument) to an instance variable of this object.
Get the graphics context for the JPanel (use the getGraphics method), cast it to type Graphics2D, and
assign it to an instance variable.
Determine the width and height of this JPanel, and save these values.
Create a Color object, with randomly-chosen values for the three arguments to set the red, green, and
blue intensities, and assign this object to an instance variable.
Randomly choose a sleep time for this thread, between 20 and 100 milliseconds. This will determine
how rapidly the image grows.
Regarding your instructions
Write a class called ThreadFrame that extends JFrame. This class should include a main method that creates one instance of this class. This should produce a GUI with an appearance similar to what you see above. Make the window 640 by 480 pixels, and do not allow the user to resize it. Add two JPanels to the content pane, a white one in the center region on which the threads will be drawn, and a gray one in the south region to hold the JButtons marked “New Worm” and “Kill Worm”.
OK, you've got this.
Add an action listener to the “New Worm” button, so that when you click on it, it creates a new instance of a class called DrawThread (to be described shortly), adds it to an ArrayList, and starts it.
Again, you've done this.
Add an action listener to the “Kill Worm” button, so that when you click on it, it removes the first DrawThread from the ArrayList and interrupts it.
Break it down:
Get the most recent worm from the ArrayList -- you would use two ArrayList methods, size() and get(...) to achieve this. Give it a try.
Then interrupt the thread. Once you get the object from the array list, you will need to call a Thread method on it, and I'm guessing that you'll be able figure out which method, right (read the instructions for this, i.e, "and interrupts it")? ;)
Edit
Note, that his recommendations are not good, and I would not hire your instructor or course director as a Swing programmer if I needed one.
Edit
Just for grins, here is another way to code this sort of thing. Note that it does not satisfy your assignment requirements (which is one reason I don't hesitate to post it), but it shows what I believe are better Swing behaviors:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class MyWormMain {
private static void createAndShowGui() {
MyWormDrawPanel drawPanel = new MyWormDrawPanel();
MyWormButtonPanel btnPanel = new MyWormButtonPanel(drawPanel);
JFrame frame = new JFrame("Worms");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawPanel, BorderLayout.CENTER);
frame.getContentPane().add(btnPanel.getMainPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class MyWormDrawPanel extends JPanel {
private static final int PREF_W = 640;
private static final int PREF_H = 480;
private static final Color BACKGROUND = Color.WHITE;
private static final int TIMER_DELAY = 50;
private List<MyWorm> wormList = new ArrayList<>();
private Timer wormTimer;
private Random random = new Random();
public MyWormDrawPanel() {
setBackground(BACKGROUND);
wormTimer = new Timer(TIMER_DELAY, new WormTimerListener());
wormTimer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (MyWorm worm : wormList) {
worm.draw(g2);
}
}
public void addWorm() {
int r = random.nextInt(128) + 128;
int g = random.nextInt(128) + 128;
int b = random.nextInt(128) + 128;
int rand = random.nextInt(3);
switch (rand) {
case 0:
r /= 3;
break;
case 1:
g /= 3;
break;
case 2:
b /= 3;
default:
break;
}
Color color = new Color(r, g, b);
int x = random.nextInt(PREF_W);
int y = random.nextInt(PREF_H);
Point head = new Point(x, y);
wormList.add(new MyWorm(color, head, PREF_W, PREF_H));
}
public void killWorm() {
if (wormList.size() > 0) {
wormList.remove(wormList.size() - 1);
}
}
private class WormTimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
for (MyWorm worm : wormList) {
worm.grow();
}
repaint();
};
}
}
#SuppressWarnings("serial")
class MyWormButtonPanel {
private static final int GAP = 15;
private JPanel mainPanel = new JPanel();
private MyWormDrawPanel drawPanel;
public MyWormButtonPanel(MyWormDrawPanel drawPanel) {
this.drawPanel = drawPanel;
mainPanel.setLayout(new GridLayout(1, 0, GAP, GAP));
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(new JButton(new AddWormAction("Add Worm", KeyEvent.VK_A)));
mainPanel.add(new JButton(new KillWormAction("Kill Worm", KeyEvent.VK_K)));
}
public JComponent getMainPanel() {
return mainPanel;
}
private class AddWormAction extends AbstractAction {
public AddWormAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.addWorm();
}
}
private class KillWormAction extends AbstractAction {
public KillWormAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
drawPanel.killWorm();
}
}
}
class MyWorm {
private static final int MAX_DIR = 360;
private static final int SEG_WIDTH = 20;
private static final int MAX_RAND_DIR = 60;
private Color color;
private List<Point> body = new ArrayList<>();
private Random random = new Random();
private int direction;
private int maxX;
private int maxY;
public MyWorm(Color color, Point head, int maxX, int maxY) {
this.color = color;
body.add(head);
direction = random.nextInt(MAX_DIR);
this.maxX = maxX;
this.maxY = maxY;
}
public void grow() {
Point lastPt = body.get(body.size() - 1);
int x = lastPt.x
+ (int) (SEG_WIDTH * 3 * Math.cos(Math.toRadians(direction)) / 4.0);
int y = lastPt.y
+ (int) (SEG_WIDTH * 3 * Math.sin(Math.toRadians(direction)) / 4.0);
if (x < 0) {
x = maxX - 1;
}
if (x > maxX) {
x = 0;
}
if (y < 0) {
y = maxY - 1;
}
if (y > maxY) {
y = 0;
}
Point nextPoint = new Point(x, y);
direction += random.nextInt(MAX_RAND_DIR) - MAX_RAND_DIR / 2;
body.add(nextPoint);
}
public void draw(Graphics2D g2) {
Graphics2D g2b = (Graphics2D) g2.create();
g2b.setColor(color);
for (Point p : body) {
int x = p.x - SEG_WIDTH / 2;
int y = p.y - SEG_WIDTH / 2;
int width = SEG_WIDTH;
int height = SEG_WIDTH;
g2b.fillOval(x, y, width, height);
}
g2b.dispose();
}
}
Related
I have a very basic question: my MainDriver class wishes to run the Line class when a button Line is pressed; however, I know we cannot run a whole class, so if there's another way to implement this, please let me know.
The following is the MainDriver class; I've highlighted the relevant bit in astericks.
package javafx.scene;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
//import java.awt.*;
class MainDriver extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
// create a frame
static JFrame frame;
// main function
public static void main(String args[])
{
// create a frame
frame = new JFrame("DrawShapes");
try {
// set look and feel
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (Exception e) {
System.err.println(e.getMessage());
}
// create a object of class
Line line = new Line();
// create number buttons and some operators
JButton b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bs, bd, bm, be, beq;
// create number buttons
b0 = new JButton("line");
b1 = new JButton("circle");
b2 = new JButton("triangle");
b3 = new JButton("rectangle");
b4 = new JButton("quadrilateral");
b5 = new JButton("move");
b6 = new JButton("copy");
b7 = new JButton("delete");
b8 = new JButton("random");
b9 = new JButton("import");
ba = new JButton("export");
bs = new JButton("");
bd = new JButton("/");
bm = new JButton("*");
beq = new JButton("C");
// create . button
be = new JButton(".");
// create a panel
JPanel p = new JPanel();
// add action listeners
b0.addActionListener(line);
//add more later
// add elements to panel
p.add(b0);
p.add(ba);
p.add(b1);
p.add(b2);
p.add(b3);
p.add(bs);
p.add(b4);
p.add(b5);
p.add(b6);
p.add(bm);
p.add(b7);
p.add(b8);
p.add(b9);
p.add(bd);
p.add(be);
p.add(b0);
p.add(beq);
// set Background of panel
p.setBackground(Color.blue);
// add panel to frame
frame.add(p);
frame.setSize(450, 400);
}
public void actionPerformed(ActionEvent e)
{
String s = e.getActionCommand();
*************************
if (s.equals("line")) {
//Im not sure how to implement the object here
}
***************************
}
}
And here's the Line class
package javafx.scene;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class Line extends JPanel implements MouseListener,ActionListener{
private int x,y,x2,y2,a=1;
public Line(){
super();
addMouseListener(this);
}
public void paint(Graphics g){
int w = x2 - x ;
int h = y2 - y ;
int dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0 ;
if (w<0) dx1 = -1 ; else if (w>0) dx1 = 1 ;
if (h<0) dy1 = -1 ; else if (h>0) dy1 = 1 ;
if (w<0) dx2 = -1 ; else if (w>0) dx2 = 1 ;
int longest = Math.abs(w) ;
int shortest = Math.abs(h) ;
if (!(longest>shortest)) {
longest = Math.abs(h) ;
shortest = Math.abs(w) ;
if (h<0) dy2 = -1 ; else if (h>0) dy2 = 1 ;
dx2 = 0 ;
}
int numerator = longest >> 1 ;
for (int i=0;i<=longest;i++) {
g.fillRect(x,y,1,1);
numerator += shortest ;
if (!(numerator<longest)) {
numerator -= longest ;
x += dx1 ;
y += dy1 ;
} else {
x += dx2 ;
y += dy2 ;
}
}
}
public void mouseClicked(MouseEvent mouse) {
if (a == 1) {
a = 0;
x = x2 = mouse.getX();
y = y2 = mouse.getY();
} else {
a = 1;
x = x2;
y = y2;
x2 = mouse.getX();
y2 = mouse.getY();
repaint();
}
}
public void mouseEntered(MouseEvent mouse){ }
public void mouseExited(MouseEvent mouse){ }
public void mousePressed(MouseEvent mouse){ }
public void mouseReleased(MouseEvent mouse){ }
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
Here is an example on how to go about it:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;
public class Main {
private static void drawPoint(final Graphics g, final Point p) {
g.drawLine(p.x - 10, p.y, p.x + 10, p.y);
g.drawLine(p.x, p.y - 10, p.x, p.y + 10);
}
private static void drawLine(final Graphics g, final Point p1, final Point p2) {
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
public static interface ShapeByPoints {
int getPointCount();
void draw(final Graphics g, final Point... points);
}
public static class DrawPanel extends JPanel {
private final ArrayList<Point> points;
private ShapeByPoints s;
public DrawPanel() {
points = new ArrayList<>();
s = null;
}
public void setShape(final ShapeByPoints s) {
this.s = s;
points.clear();
repaint();
}
public void modifyLastPoint(final Point p) {
points.get(points.size() - 1).setLocation(p);
repaint();
}
public void addPoint(final Point p) {
if (s != null && points.size() == s.getPointCount())
points.clear();
points.add(p);
repaint();
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (s != null && s.getPointCount() == points.size())
s.draw(g, points.toArray(new Point[s.getPointCount()]));
else {
points.forEach(p -> drawPoint(g, p));
for (int i = 1; i < points.size(); ++i)
drawLine(g, points.get(i - 1), points.get(i));
}
}
}
public static class EllipseByPoints implements ShapeByPoints {
#Override
public int getPointCount() {
return 2;
}
#Override
public void draw(final Graphics g, final Point... points) {
g.drawOval(Math.min(points[0].x, points[1].x), Math.min(points[0].y, points[1].y), Math.abs(points[1].x - points[0].x), Math.abs(points[1].y - points[0].y));
}
}
public static class PolygonByPoints implements ShapeByPoints {
private final int points;
public PolygonByPoints(final int points) {
this.points = points;
}
#Override
public int getPointCount() {
return points;
}
#Override
public void draw(final Graphics g, final Point... points) {
for (int i = 1; i < this.points; ++i)
drawLine(g, points[i - 1], points[i]);
drawLine(g, points[this.points - 1], points[0]);
}
}
public static class LineByPoints extends PolygonByPoints {
public LineByPoints() {
super(2);
}
}
public static class RectangleByPoints implements ShapeByPoints {
#Override
public int getPointCount() {
return 2;
}
#Override
public void draw(final Graphics g, final Point... points) {
g.drawRect(Math.min(points[0].x, points[1].x), Math.min(points[0].y, points[1].y), Math.abs(points[1].x - points[0].x), Math.abs(points[1].y - points[0].y));
}
}
private static JRadioButton createButton(final String buttonText, final ButtonGroup bg, final DrawPanel dp, final ShapeByPoints s) {
final JRadioButton btn = new JRadioButton(buttonText);
btn.addActionListener(e -> dp.setShape(s));
bg.add(btn);
return btn;
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final DrawPanel dp = new DrawPanel();
final MouseAdapter ma = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent mevt) {
dp.addPoint(mevt.getPoint());
}
#Override
public void mouseDragged(final MouseEvent mevt) {
dp.modifyLastPoint(mevt.getPoint());
}
#Override
public void mouseReleased(final MouseEvent mevt) {
dp.modifyLastPoint(mevt.getPoint());
}
};
dp.setPreferredSize(new Dimension(500, 350));
dp.addMouseListener(ma);
dp.addMouseMotionListener(ma);
final ButtonGroup bg = new ButtonGroup();
final JPanel buttons = new JPanel();
buttons.add(createButton("Line", bg, dp, new LineByPoints()));
buttons.add(createButton("Ellipse", bg, dp, new EllipseByPoints()));
buttons.add(createButton("Rectangle", bg, dp, new RectangleByPoints()));
buttons.add(createButton("Triangle", bg, dp, new PolygonByPoints(3)));
buttons.add(createButton("Pentagon", bg, dp, new PolygonByPoints(5)));
//... keep creating buttons here ...
final JPanel contents = new JPanel(new BorderLayout());
contents.add(dp, BorderLayout.CENTER);
contents.add(buttons, BorderLayout.PAGE_START);
final JFrame frame = new JFrame("Clicking shapes.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Notes:
Don't use paint unless there is good reason, which usually isn't. In most cases you want to draw the panel, in which case paintComponent should be enough.
Dont' forget to firstly call super.paintComponent inside paintComponent override. This should be the first call, because it will clear the panel for you from previous drawings.
Decide if you want to add JComponents which will draw the shapes, or draw the shapes yourself in a sub-classed panel. In this case, it seems like you are trying both (because Line extends JPanel and you also override paint). In my opinion, in this case, using an abstraction of a shape to be drawn between some points will do the job. That's why I created ShapeByPoints interface.
Swing is not thread safe, which means there can be conditions between competing accesses of the same variable. To avoid such scenarios, you can run every Swing related operation at the Event Dispatch Thread (or EDT for short). To explicitly make a call in the EDT, invoke a method like SwingUtilities.invokeLater, SwingUtilities.invokeAndWait, EventQueue.invokeLater and so on... There is enough material on the Internet about this topic. For example here.
Basically everything is redesigned from scratch in my approach (ie I did not modify your example code, because I guessed it would take a little bit more time, but that's just a guess).
I just created the ShapeByPoints interface which specifies how a shape is supposedly drawn (ie number of points it needs, and a draw method to do the drawing).
Then I created a DrawPanel extends JPanel which contains the state of each operation. For example if the user selects a pentagon to be drawn, he needs 5 points, but what happens when he/she has clicked only twice?... The DrawPanel is responsible for such scenarios and also for drawing the given shape when all the points are completed/clicked.
I added an instance of a DrawPanel in the frame, a MouseAdapter for user interaction and a couple of buttons to demonstrate this logic.
All you need to do now, is to implement ShapeByPoints as you like and supply an instance of it to the DrawPanel depending of the button pressed. I already did some examples on how to do it in the code (like an EllipseByPoints, LineByPoints, RectangleByPoints and PolygonByPoints).
Finally, as a bonus, I added the ability to drag the clicked points (ie not only click them, but click-and-drag them).
Hope it helps.
I want to make a simple addition program. In it I want to pass variables from Main_Window to Second_Window for addition and I want to get result on Second_Window multiple times. Means If I pass variables multiple times from Main_Window for addition then result should be on Second_Window not third and fourth Window.
Here I want All changes should be show on Second_Window not another on open.
These lines are written for passing variables from Main_Window.
Second_Window s = new Second_Window(a,b);
s.setVisible(true);
I'm going to take my code from my previous answer to one of your questions as my base code and add some functionality to it:
First of all, you need to create one and only one instance of your second window and have a method that can update the angles sent to it.
How do you do that you might be asking yourself, well it's easy, in your action listener you create the instance if the second frame was not created yet and updated it otherwise.
private ActionListener listener = e -> {
if (e.getSource().equals(submitButton)) {
if (!frame.isVisible()) {
circle = new MyCircle((Integer) box1.getSelectedItem(), (Integer) box2.getSelectedItem());
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
} else {
circle.updateAngles((Integer) box1.getSelectedItem(), (Integer) box2.getSelectedItem());
}
}
};
Note:
If you close the second window all previous data will be lost, if you want to save that state, then play with the frame visibility and initialize the MyCircle instance in the createAndShowGui() method in the code below.
Next thing is you need to keep track of all the angles you've added, for that you might need a List and iterate over it, or paint that to a BufferedImage and then paint that image on the JPanel. For this example we'll be using the List option.
However, for this example, if the data is too much, it might not display, to correct that, use a JScrollPane as well, however I'm leaving that up to you.
This example as well, makes the whole program to terminate only when you close the main window, but not if you close the second window.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RadiusDrawer {
private JFrame frame;
private JFrame mainFrame;
private int centerX = 50;
private int centerY = 50;
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
private int r = 100;
private JComboBox<Integer> box1;
private JComboBox<Integer> box2;
private JLabel label1;
private JLabel label2;
private JButton submitButton;
MyCircle circle;
private static final Integer[] ANGLES = new Integer[]{15, 30, 45, 60, 75, 90};
public static void main(String[] args) {
SwingUtilities.invokeLater(new RadiusDrawer()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
mainFrame = new JFrame("Main Frame");
mainFrame.add(createMainWindow());
mainFrame.pack();
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = e -> {
if (e.getSource().equals(submitButton)) {
if (!frame.isVisible()) {
circle = new MyCircle((Integer) box1.getSelectedItem(), (Integer) box2.getSelectedItem());
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
} else {
circle.updateAngles((Integer) box1.getSelectedItem(), (Integer) box2.getSelectedItem());
}
}
};
private JPanel createMainWindow() {
JPanel pane = new JPanel();
box1 = new JComboBox<>(ANGLES);
box2 = new JComboBox<>(ANGLES);
label1 = new JLabel("Angle 1");
label2 = new JLabel("Angle 2");
submitButton = new JButton("Submit");
submitButton.addActionListener(listener);
pane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(20, 30, 20, 30);
pane.add(box1, gbc);
gbc.gridx = 1;
pane.add(box2, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
pane.add(label1, gbc);
gbc.gridx = 1;
pane.add(label2, gbc);
gbc.gridy = 2;
pane.add(submitButton, gbc);
return pane;
}
#SuppressWarnings("serial")
class MyCircle extends JPanel {
int cx = 0;
int cy = 0;
double lineX = 0;
double lineY = 0;
double roundedX = 0;
double roundedY = 0;
int angle1 = 0;
int angle2 = 0;
int angle1HistoryX = 15;
int angle2HistoryX = 150;
int angleHistoryY = 300;
int angleHistoryYGap = 20;
Color angle1Color = Color.BLUE;
Color angle2Color = Color.RED;
List <Integer> angle1History;
List <Integer> angle2History;
public MyCircle(int angle1, int angle2) {
this.angle1 = angle1;
this.angle2 = angle2;
angle1History = new ArrayList<>();
angle2History = new ArrayList<>();
angle1History.add(angle1);
angle2History.add(angle2);
calculateCoords();
calculateCenter();
}
private void updateAngles(int angle1, int angle2) {
this.angle1 = angle1;
this.angle2 = angle2;
angle1History.add(angle1);
angle2History.add(angle2);
calculateCoords();
this.revalidate();
this.repaint();
}
private void calculateCoords() {
x1 = (int) (r * Math.cos(Math.toRadians(angle1)));
y1 = (int) (r * Math.sin(Math.toRadians(angle1))) * -1;
x2 = (int) (r * Math.cos(Math.toRadians(angle2)));
y2 = (int) (r * Math.sin(Math.toRadians(angle2))) * -1;
}
private void calculateCenter() {
cx = centerX + r;
cy = centerY + r;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawCircle(g2d, centerX, centerY, r);
drawRadius(g2d);
drawHistory(g2d);
}
private void drawCircle(Graphics2D g2d, int x, int y, int r) {
g2d.setColor(Color.BLACK);
g2d.draw(new Ellipse2D.Double(x, y, r * 2, r * 2));
}
private void drawRadius(Graphics2D g2d) {
g2d.setColor(angle1Color);
g2d.draw(new Line2D.Double(cx, cy, cx + x1, cy + y1));
g2d.setColor(angle2Color);
g2d.draw(new Line2D.Double(cx, cy, cx + x2, cy + y2));
}
private void drawHistory(Graphics2D g2d) {
g2d.setColor(angle1Color);
g2d.drawString("Angle1", angle1HistoryX, angleHistoryY);
for (int i = 0; i < angle1History.size(); i++) {
g2d.drawString(angle1History.get(i).toString(), angle1HistoryX, angleHistoryY + (angleHistoryYGap * (i + 1)));
}
g2d.setColor(angle2Color);
g2d.drawString("Angle2", angle2HistoryX, angleHistoryY);
for (int i = 0; i < angle2History.size(); i++) {
g2d.drawString(angle2History.get(i).toString(), angle2HistoryX, angleHistoryY + (angleHistoryYGap * (i + 1)));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 600);
}
}
}
That's it!
So basically you want to keep the second window to maximum one instance (or zero if the button in main window is never clicked), to do this you need to keep a static reference to the second window, if this reference already exists, you don't create a new one, but ask the existing one to display the calculation result.
A possible approach:
For the submit button in main window, you gather the values of the required parameters, and call the static method of the second window class. A static method is a method that belongs to a class, not an object.
btnSubmit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent aE) {
int azimuth = ...;
int elevation = ...;
SecondWindow.showResult(azimuth, elevation);
}
});
In the second window class, it keeps a static instance of this class, it is null initially. When showResult() is called, it checks if the instance isn't yet exists, then create a new second window and assign to the static reference. And it asks the instance to calculate and display the result in UI.
public class SecondWindow extends JFrame {
private static SecondWindow instance = null;
public static void showResult(int azimuth, int elevation) {
if (instance == null) {
instance = new SecondWindow();
}
instance.performShowResult(azimuth, elevation);
}
private void performShowResult(int azimuth, int elevation) {
// Display the result in UI.
}
}
The last thing to consider is whether you want to set the instance to null when it has been closed? If yes, add this code to the second window constructor:
public SecondWindow() {
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent aE) {
instance = null;
}
});
}
So when showResult() is called again, a new second window will be created.
If you think the creation of second window is somewhat "heavy", then you can just keep the second window closed (thus not set the instance to null), but ensure it is shown when showResult() is called. Like this:
public static void showResult(int azimuth, int elevation) {
if (instance == null) {
instance = new SecondWindow();
} else if (instance.isShowing() == false) {
instance.setVisible(true);
}
instance.performShowResult(azimuth, elevation);
}
i'm kind of new with Java graphics, i'm trying to create a simple crossroad GUI interface with 4 traffic lights on it, when using the following classes that I have created - I get a window with a large grey rectangle on it (I assume that since I didn't allocate a traffic lights in the center it has been filled with the default grey background), how do I control the size of the center of the JFrame?
This is what i'm looking to acheive:
This is what i'm getting:
This is the JFrame class.
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 400;
private static final int HEIGHT_OF_WINDOW = 400;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
this.setVisible(true);
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(tLightW, BorderLayout.WEST);
this.add(tLightN, BorderLayout.NORTH);
this.add(tLightE, BorderLayout.EAST);
this.add(tLightS, BorderLayout.SOUTH);
}
}
This is the Jpanel class.
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 100;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
this.setSize(CAR_DIAMETER, 120);
this.setBackground(Color.BLACK);
status = 0;
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(this.getX(), this.getY(), CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(this.getX(), this.getY()+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
System.out.println(this.getX()+" "+this.getY());
}
}
EDIT:
Following Hovercraft Full Of Eels' advise, here are my new classes:
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 900;
private static final int HEIGHT_OF_WINDOW = 900;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
setLayout(new GridLayout(3,3));
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(new JPanel());
this.add(tLightW);
this.add(new JPanel());
this.add(tLightN);
this.add(new JPanel());
this.add(tLightE);
this.add(new JPanel());
this.add(tLightS);
this.setVisible(true);
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 50;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
status = 0;
this.setPreferredSize(new Dimension(CAR_DIAMETER,2*CAR_DIAMETER+2*PERSON_HEIGHT));
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(100, 50, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(100, 50+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(100, 50+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(100, 50+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
}
}
Your problem is not that the center of the JFrame is too large, but rather it's because the size of your surrounding JPanels are too small. Understand that most Swing layout managers respect a components preferred size, and use this to set the size of the component. Your other problems include
using getX() and getY() to place your drawings. These values give the location of the JPanel within its container, but that won't help you place your drawing since when you draw within the JPanel the drawing's location is placed relative to the location of the pixel within the JPanel not its container, so using these methods will mess you up.
Calling the JFrame's setVisible(true) before adding all components. This risks not displaying all components.
Making your TrafficLight class extend JPanel. You're far better off using a single JPanel to do all the drawing and have your TrafficLight class not extend from any Swing component but rather be a logical class. Give it a public void draw(Graphics2D g2) method that you can call within the drawing JPanel's paintComponent method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class CrossRoads2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 100;
List<TrafficLight2> lights = new ArrayList<>();
public CrossRoads2() {
// create a timer to randomly change traffic light state
// and start it
new Timer(TIMER_DELAY, new TimerListener()).start();
// create 4 TrafficLight2 objects and place them at 4
// compass locations, and add to lights ArrayList
int x = (PREF_W - TrafficLight2.getWidth()) / 2;
int y = 0;
lights.add(new TrafficLight2(x, y));
x = 0;
y = (PREF_H - TrafficLight2.getHeight()) / 2;
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth());
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth()) / 2;
y = (PREF_H - TrafficLight2.getHeight());
lights.add(new TrafficLight2(x, y));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// cast g into a Graphics2 object
Graphics2D g2 = (Graphics2D) g;
// for smooth rendering
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the ArrayList, calling the draw method on each light
for (TrafficLight2 light : lights) {
light.draw(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// give our JPanel a decent size
return new Dimension(PREF_W, PREF_H);
}
// ActionListener that randomly changes the LightState of each traffic light
private class TimerListener implements ActionListener {
private Random random = new Random();
#Override
public void actionPerformed(ActionEvent e) {
for (TrafficLight2 light : lights) {
// random number 0 to 2
int randomIndex = random.nextInt(LightState.values().length);
// get one of the LightStates using the index above
LightState lightState = LightState.values()[randomIndex];
// set our light to this state
light.setLightState(lightState);
}
repaint();
}
}
private static void createAndShowGui() {
CrossRoads2 mainPanel = new CrossRoads2();
JFrame frame = new JFrame("Cross Roads");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class TrafficLight2 {
private static final int ELLIPSE_W = 40;
private static final int GAP = 4;
private int x;
private int y;
private LightState lightState = LightState.RED; // what color is bright
// map to hold our 3 ellipses, each one corresponding to a LightState
private Map<LightState, Shape> lightMap = new EnumMap<>(LightState.class);
public TrafficLight2(int x, int y) {
// create 3 ellipses, one each for RED, YELLOW, GREEN
// place each one below the previous
// associate each one with one of our RED, YELLOW, or GREEN LightStates
// putting the Ellipse into the map with the light state as key
this.x = x;
this.y = y;
int tempX = x + GAP;
int tempY = y + GAP;
lightMap.put(LightState.RED, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.YELLOW, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.GREEN, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
}
// called by JPanel's paintComponent
public void draw(Graphics2D g2) {
// iterate through the 3 LightStates
for (LightState ltSt : LightState.values()) {
// if the ltSt in the for loop is this traffic light's LightState
// then the display color should be bright
Color c = ltSt == lightState ? ltSt.getColor() :
// other wise the display color should be very dark
ltSt.getColor().darker().darker().darker();
g2.setColor(c);
g2.fill(lightMap.get(ltSt)); // fill the oval with color
g2.setColor(Color.BLACK);
g2.draw(lightMap.get(ltSt)); // draw a black border
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public LightState getLightState() {
return lightState;
}
public void setLightState(LightState lightState) {
this.lightState = lightState;
}
// static method for the width of our traffic lights
public static int getWidth() {
return 2 * GAP + ELLIPSE_W;
}
// static method for the height of our traffic lights
public static int getHeight() {
return 4 * GAP + 3 * ELLIPSE_W;
}
}
// enum that encapsulates the 3 possible states of the traffic light
enum LightState {
RED("Red", Color.RED), YELLOW("Yellow", Color.YELLOW), GREEN("Green", Color.GREEN);
private LightState(String text, Color color) {
this.text = text;
this.color = color;
}
private String text;
private Color color;
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
I am trying to make a GUI for a battleship game. One class is for creating the GUI itself and a second class is on top of it to manage the board in the game. My problem is that the JPanel creates twice once a mouse click happens (the mouse click is supposed to be where one is firing in the game and then marks that as a hit/miss). I'm not sure why it is creating twice. Is it because of the passing of a panel? Code below and a photo of what the code generates.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class BattleshipApplet extends JApplet implements MouseListener {
private final JButton playButton = new JButton("Play");
private final JLabel msgBar = new JLabel("Click Play to start game");
private BoardPanel panel;
public BattleshipApplet(){
playButton.addActionListener(this::playButtonClicked);
addMouseListener(this);
}
public void init(){
configureGui();
}
private void configureGui(){
setLayout(new BorderLayout());
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
buttons.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
buttons.add(playButton);
add(buttons, BorderLayout.NORTH);
msgBar.setBorder(BorderFactory.createEmptyBorder(10,10,5,5));
add(createBoardPanel(), BorderLayout.CENTER);
add(msgBar, BorderLayout.SOUTH);
}
private BoardPanel createBoardPanel(){
panel = new BoardPanel();
return panel;
}
private void displayMessage(String msg){
msgBar.setText(msg);
}
private void playButtonClicked(ActionEvent event){
displayMessage("Play button clicked!");
}
public void mouseClicked(MouseEvent e) {
panel.mouseClickedAt(e.getX(), e.getY());
e.consume();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
The board class using JPanel
[![import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class BoardPanel extends JPanel {
int mx, my;
boolean rect1Clicked;
//gamePlay a;
public void init(){
rect1Clicked = false;
}
/***Your applet shall show the status of the board before and after
each shot, including the number of shots made and the status of
each place (no shot or hit/miss shot). ***/
public void paint(Graphics g){
boolean miss = false;
for (int i=0; i<11; i++){
g.setColor(Color.blue);
g.drawLine(20,20+i*28, 300, 20+i*28);
}
for (int i=0; i<11; i++)
g.drawLine(20+i*28,20,20+i*28,300);
//if inside board
if(rect1Clicked == true){
g.setColor(Color.green);
//aligns to square to check in computer board for hit/miss
int bx =(my-20)/28;
int by =(mx-20)/28;
//check hit on board
//if shot was a miss
if(miss == true ){
//update to white
g.setColor(Color.white);
}
//if shot was a hit
if(miss == false){
//update to red
g.setColor(Color.red);
}
//compare to line for fill
int fillx = mx/2;
int filly = my/2 ;
if(mx<=47){
fillx = 20;
}
if(mx>47 && mx<=75){
fillx = 48;
}
if(mx>75 && mx<=103){
fillx = 76;
}
if(mx>103 && mx <=131){
fillx = 104;
}
if(mx>131 && mx<=159){
fillx = 132;
}
if(mx>159 && mx<=187){
fillx = 160;
}
if(mx>187 && mx <=215){
fillx = 188;
}
if(mx>215 && mx <=243){
fillx = 216;
}
if(mx>243 && mx <=271){
fillx = 244;
}
if(mx>271 && mx<=299){
fillx = 272;
}
if(mx>299){
fillx = 300;
}
//y comparisons
if(my<=47){
filly = 20;
}
if(my>47 && my<=75){
filly = 48;
}
if(my>75 && my<=103){
filly = 76;
}
if(my>103 && my <=131){
filly = 104;
}
if(my>131 && my<=159){
filly = 132;
}
if(my>159 && my<=187){
filly = 160;
}
if(my>187 && my <=215){
filly = 188;
}
if(my>215 && my <=243){
filly = 216;
}
if(my>243 && my <=271){
filly = 244;
}
if(my>271 && my<=299){
filly = 272;
}
if(my>299){
filly = 300;
}
g.drawString("("+mx+","+my+")",mx,my);
//25 describes size of square
g.fillOval(fillx, filly, 25, 25);
}
}
public void game(BoardPanel p){
//while game plays
}
public void mouseClickedAt(int x, int y){
mx = x;
my = y;
//user clicked inside of board space
if(mx>20 && mx<300 && my>20 && my<300){
//send to board in MainBattleship
rect1Clicked = true;
}
//updates board
repaint();
}
}][1]][1]
I am so lost, thank you for any help!
Suggestions:
Don't override a JPanel's paint method but rather its paintComponent method as this is safer, and later when you want to do animation, will result in smoother animation.
Most important you almost always need to call the super's painting method within your own, else the JPanel will not remove previous image artifacts that need to be cleaned up. So if you continue to override paint (although I recommend against, this) the first line of your override should be super.paint(g);, or if you override paintComponent then the first line should be super.paintComponent(g);, of course assuming that you're methods use a Graphics parameter named g.
Also, add the MouseListener to the JPanel, not to the applet, since it is the mouse click location on the panel that matters to you.
Also, use a grid of components or some math to greatly simplify your code -- that ugly list of if blocks should be replaced by a much simpler for loop, one using basic math.
Consider extracting that logic discussed in the point above out of your painting method and into a model of some kind, perhaps a 2D array of boolean.
You're using a lot of "magic" numbers in your code, numbers that should be changed to a combination of constants and mathematically derived numbers.
Notice what happens if you click on your GUI, and then resize it, or if you minimize and then restore it -- you lose all red circles except for the last one pressed. This is another reason to use a grid of boolean or other model to hold state of the game, and then use this model when drawing your GUI.
On further thinking, you might want a 2D array of an enum or an int array, since the grid cell state will likely be more than 2 values (true or false), but rather will be three values -- untested, hit, and miss, and you'll likely want to fill your oval with red if a hit or white if a miss.
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import javax.swing.*;
public class GridExample {
private static void createAndShowGui() {
final GridPanel gridPanel = new GridPanel();
JButton resetBtn = new JButton(new AbstractAction("Reset") {
#Override
public void actionPerformed(ActionEvent e) {
gridPanel.reset();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(resetBtn);
JFrame frame = new JFrame("GridExample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gridPanel);
frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
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 GridPanel extends JPanel {
private static final int ROWS = 10;
private static final int CELL_WIDTH = 28;
private static final int PAD = 20;
private static final int PREF_W = ROWS * CELL_WIDTH + 2 * PAD;
private static final int PREF_H = PREF_W;
private static final Color GRID_COLOR = Color.blue;
private static final Color CIRCLE_COLOR = Color.red;
private static final int SML_GAP = 2;
private boolean[][] grid = new boolean[ROWS][ROWS];
public GridPanel() {
addMouseListener(new MyMouse());
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public void reset() {
grid = new boolean[ROWS][ROWS]; // fills grid with false
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// draw grid:
g2.setColor(GRID_COLOR);
for (int i = 0; i <= ROWS; i++) {
int x1 = PAD + i * CELL_WIDTH;
int y1 = PAD;
int x2 = x1;
int y2 = PAD + CELL_WIDTH * ROWS;
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x1, y2, x2);
}
// iterate through the grid boolean array
// draw red circles if the grid value is true.
g2.setColor(CIRCLE_COLOR);
int w = CELL_WIDTH - 2 * SML_GAP; // width of the circle to draw
int h = w;
// nested for loop to go through the grid array
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[r].length; c++) {
if (grid[r][c]) {
int x = PAD + c * CELL_WIDTH + SML_GAP;
int y = PAD + r * CELL_WIDTH + SML_GAP;
g2.fillOval(x, y, w, h);
}
}
}
}
private class MyMouse extends MouseAdapter {
public void mousePressed(MouseEvent e) {
int x = e.getPoint().x;
int y = e.getPoint().y;
if (x < PAD || y < PAD) {
// clicked above or to right of grid
return;
}
int r = (y - PAD) / CELL_WIDTH;
int c = (x - PAD) / CELL_WIDTH;
// if clicked to right or below grid.
// the < 0 part is likely unnecessary, but why not be extra safe?
if (r >= ROWS || c >= ROWS || r < 0 || c < 0) {
return;
}
grid[r][c] = true;
repaint();
}
}
}
I have displayed an image(ball) inside the JApplet, now I want the image to move in a vertical way (up and down). The problem is I don't know how to do it.
Could someone has an idea about this matter?
You need to set the position of that image to some calculated value (means you caculate the vertical position using time, speed and maybe other restrictions).
How you'd set that position depends on how you draw the image.
Example, based on drawing in the applet's (or a nested component's) paint(Graphics g) method:
//first calculate the y-position
int yPos += timeSinceLastPaint * speed; //increment the position
if( (speed > 0 && yPos > someMaxY) || (speed < 0 && yPos <0 ) ) {
speed *= -1; //if the position has reached the bottom (max y) or the top invert the direction
}
//in your paint(Graphics g) method:
g.drawImage(image, yPos, x, null);
Then you'd have to constantly repaint the applet.
More information on animations in applets can be found here: http://download.oracle.com/javase/tutorial/uiswing/components/applet.html
another example for javax.swing.Timer with moving Ojbects created by paintComponent(Graphics g), and I have lots of Start, not some blurred Mikado :-)
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class AnimationBackground {
private Random random = new Random();
private JFrame frame = new JFrame("Animation Background");
private final MyJPanel panel = new MyJPanel();
private JLabel label = new JLabel("This is a Starry background.", JLabel.CENTER);
private JPanel stopPanel = new JPanel();
private JPanel startPanel = new JPanel();
public AnimationBackground() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
panel.setBackground(Color.BLACK);
for (int i = 0; i < 50; i++) {
Star star = new Star(new Point(random.nextInt(490), random.nextInt(490)));
star.setColor(new Color(100 + random.nextInt(155), 100 + random.nextInt(155), 100 + random.nextInt(155)));
star.setxIncr(-3 + random.nextInt(7));
star.setyIncr(-3 + random.nextInt(7));
panel.add(star);
}
panel.setLayout(new GridLayout(10, 1));
label.setForeground(Color.WHITE);
panel.add(label);
stopPanel.setOpaque(false);
stopPanel.add(new JButton(new AbstractAction("Stop this madness!!") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.stopAnimation();
}
}));
panel.add(stopPanel);
startPanel.setOpaque(false);
startPanel.add(new JButton(new AbstractAction("Start moving...") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.startAnimation();
}
}));
panel.add(startPanel);
frame.add(panel);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AnimationBackground aBg = new AnimationBackground();
}
});
}
private class Star extends Polygon {
private static final long serialVersionUID = 1L;
private Point location = null;
private Color color = Color.YELLOW;
private int xIncr, yIncr;
static final int WIDTH = 500, HEIGHT = 500;
Star(Point location) {
int x = location.x;
int y = location.y;
this.location = location;
this.addPoint(x, y + 8);
this.addPoint(x + 8, y + 8);
this.addPoint(x + 11, y);
this.addPoint(x + 14, y + 8);
this.addPoint(x + 22, y + 8);
this.addPoint(x + 17, y + 12);
this.addPoint(x + 21, y + 20);
this.addPoint(x + 11, y + 14);
this.addPoint(x + 3, y + 20);
this.addPoint(x + 6, y + 12);
}
public void setColor(Color color) {
this.color = color;
}
public void move() {
if (location.x < 0 || location.x > WIDTH) {
xIncr = -xIncr;
}
if (location.y < 0 || location.y > WIDTH) {
yIncr = -yIncr;
}
translate(xIncr, yIncr);
location.setLocation(location.x + xIncr, location.y + yIncr);
}
public void setxIncr(int xIncr) {
this.xIncr = xIncr;
}
public void setyIncr(int yIncr) {
this.yIncr = yIncr;
}
public Color getColor() {
return color;
}
}
private class MyJPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArrayList<Star> stars = new ArrayList<Star>();
private Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Star star : stars) {
star.move();
}
repaint();
}
});
public void stopAnimation() {
if (timer.isRunning()) {
timer.stop();
}
}
public void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
#Override
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
MyJPanel() {
this.setPreferredSize(new Dimension(512, 512));
}
public void add(Star star) {
stars.add(star);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Star star : stars) {
g.setColor(star.getColor());
g.fillPolygon(star);
}
}
}
}
How to move the image inside the JApplet ..?
Pretty much exactly the same way you might do it in a JFrame, JComponent or JPanel or...
Or to put that another way, nothing to do with applets and everything to do with Graphics2D. For more details, see the 2D Graphics Trail of the Java Tutorial.
When you've figured how to move an image and paint it to a Graphics2D, implement that logic in a JComponent or JPanel's paintComponent(Graphics) method and drop the component with moving image into a JApplet or JFrame (or a JPanel etc.).
For the animation side of it, use a javax.swing.Timer as seen in this example. This example does not extend any component. Instead, it creates a BufferedImage and adds it to a JLabel that is displayed to the user. When the timer fires, the code grabs the Graphics object of the image, and proceeds from there to draw the bouncing lines.
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class LineAnimator {
public static void main(String[] args) {
final int w = 640;
final int h = 480;
final RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
hints.put(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY
);
final BufferedImage bi = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB);
final JLabel l = new JLabel(new ImageIcon(bi));
final BouncingLine[] lines = new BouncingLine[100];
int factor = 1;
for (int ii=0; ii<lines.length; ii++) {
lines[ii] = new BouncingLine(w*factor,h*factor);
}
final Font font = new Font("Arial", Font.BOLD, 30);
ActionListener al = new ActionListener() {
int count = 0;
long lastTime;
String fps = "";
private final BasicStroke stroke = new BasicStroke(6);
public void actionPerformed(ActionEvent ae) {
count++;
Graphics2D g = bi.createGraphics();
g.setRenderingHints(hints);
g.setColor(new Color(55,12,59));
g.fillRect(0,0,w,h);
g.setStroke(stroke);
for (int ii=0; ii<lines.length; ii++) {
lines[ii].move();
lines[ii].paint(g);
}
if ( System.currentTimeMillis()-lastTime>1000 ) {
lastTime = System.currentTimeMillis();
fps = count + " FPS";
count = 0;
}
g.setColor(Color.YELLOW);
g.setFont(font);
g.drawString(fps,5,h-5);
l.repaint();
g.dispose();
}
};
Timer timer = new Timer(25,al);
timer.start();
JOptionPane.showMessageDialog(null, l);
//System.exit(0);
timer.stop();
}
}
class BouncingLine {
private final Color color;
private static final Random random = new Random();
Line2D line;
int w;
int h;
int x1;
int y1;
int x2;
int y2;
BouncingLine(int w, int h) {
line = new Line2D.Double(random.nextInt(w),random.nextInt(h),random.nextInt(w),random.nextInt(h));
this.w = w;
this.h = h;
this.color = new Color(
random.nextInt(255)
,random.nextInt(255)
,random.nextInt(255)
,64+random.nextInt(128)
);
x1 = (random.nextBoolean() ? 1 : -1);
y1 = (random.nextBoolean() ? 1 : -1);
x2 = -x1;
y2 = -y1;
}
public void move() {
int tx1 = 0;
if (line.getX1()+x1>0 && line.getX1()+x1<w) {
tx1 = (int)line.getX1()+x1;
} else {
x1 = -x1;
tx1 = (int)line.getX1()+x1;
}
int ty1 = 0;
if (line.getY1()+y1>0 && line.getY1()+y1<h) {
ty1 = (int)line.getY1()+y1;
} else {
y1 = -y1;
ty1 = (int)line.getY1()+y1;
}
int tx2 = 0;
if (line.getX2()+x2>0 && line.getX2()+x2<w) {
tx2 = (int)line.getX2()+x2;
} else {
x2 = -x2;
tx2 = (int)line.getX2()+x2;
}
int ty2 = 0;
if (line.getY2()+y2>0 && line.getY2()+y2<h) {
ty2 = (int)line.getY2()+y2;
} else {
y2 = -y2;
ty2 = (int)line.getY2()+y2;
}
line.setLine(tx1,ty1,tx2,ty2);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(color);
//line.set
g2.draw(line);
}
}
Update 1
I want to do it in JApplet(1) using the image(2), is it possible(3)?
The examples by mKorbel and myself feature either an image in a JLabel or custom rendering in a JPanel. In our case, we added the components to a JOptionPane & a JFrame. Either example could be just as easily added to a JApplet, or a JDialog, or as part of another panel, or.. See the Laying Out Components Within a Container lesson & Using Top-Level Containers in the Java Tutorial for more details.
Instead of the stars or lines in our examples, ..paint your image. My example goes so far as to demonstrate how to get the position to bounce around within the bounds of the container.
Sure it is possible, but "Batteries not included". Our intention is to give you some ideas that you can then adapt to your bouncing ball applet. I doubt anyone is going to create an example for you, using balls, in an applet. Though if you post an SSCCE that shows your intent and what you tried, I (and others) would often run with that source. If you want more specific answers, ask a more specific SSCCE. ;)
I want to do it in JApplet.
Why not both? You can have a hybrid application/applet as shown in this animation.