I am trying to create application where user can draw lines in Canvas. User can select direction from dropdown list and enter length of line. First line starts from center of Canvas, the next one starts where the previous one ended and so on - user can draw multiple lines one by one and all lines are connected.
I have two classes - TurtleApplet which creates GUI and Canvas with program logic:
public class TurtleApplet extends JApplet implements ActionListener
{
private JComboBox direction;
private JRadioButton activeButton, passiveButton;
private Button drawButton;
private ButtonGroup group;
private TextField pixels;
private Canvas canvas;
private JPanel panel;
private JPanel panelRadio;
private Button quitPr;
public void init()
{
//directions
String[] directionStrings = { "Right", "Left", "Up", "Down"};
direction = new JComboBox(directionStrings);
//direction.setSelectedIndex(4);
//Buttons
activeButton = new JRadioButton("Aktīvs");
passiveButton = new JRadioButton("Neaktīvs");
quitPr = new Button("Iziet");
//Canvas
canvas = new Canvas();
//canvas.setSize(600, 500);
//canvas.setBackground(Color.red);
canvas.setBorder(BorderFactory.createTitledBorder("Turtle drawing"));
//Panels
panel = new JPanel();
panelRadio =new JPanel();
panel.setLayout(new FlowLayout());
panelRadio.setLayout(new FlowLayout());
//actionListener
activeButton.addActionListener(this);
passiveButton.addActionListener(this);
activeButton.setSelected(true);
quitPr.addActionListener(this);
//Add radiobuttons
group = new ButtonGroup();
group.add(activeButton);
group.add(passiveButton);
//Add Buttons to panel
panelRadio.add(activeButton);
panelRadio.add(passiveButton);
//textfield
pixels = new TextField(12);
//Draw button
drawButton = new Button("Zīmēt");
drawButton.addActionListener(this);
direction.addActionListener(this);
panel.add(panelRadio);
panel.add(pixels);
panel.add(direction);
panel.add(drawButton);
panel.add(quitPr);
getContentPane().add(panel,"North");
getContentPane().add(canvas, "Center");
setSize(650,550);
}
public void actionPerformed( ActionEvent e)
{
if ( e.getSource() == activeButton ) {
drawButton.setVisible(true);
pixels.setEditable(true);
} else if (e.getSource() == passiveButton) {
drawButton.setVisible(false);
pixels.setEditable(false);
} else if(e.getSource() == quitPr){
System.exit(0);
}else if(e.getSource() == drawButton){
int y = Integer.parseInt(pixels.getText());
canvas.addPatt(direction.getSelectedIndex(), Integer.parseInt(pixels.getText()));
repaint();
}
//repaint();
}
}
public class Canvas extends JPanel {
private static final int RIGHT=0, LEFT=1, UP=2, DOWN=3;
public static final int WIDTH=600, HEIGHT=500;
private int direction = 0 ;
private int pixels;
//rivate List points;
public Polygon t = new Polygon();
//public Dimension d = getSize();
public int x = WIDTH/2;
public int y = HEIGHT/2;
public Canvas() {
setSize(WIDTH, HEIGHT);
}
public void addPatt(int pat, int lev) {
direction = pat;
pixels = lev;
}
public void paintComponent(Graphics g) {
switch (direction) {
case LEFT:
drawLineLeft(g, pixels);
break;
case RIGHT:
drawLineRight(g, pixels);
break;
case UP:
drawLineUp(g, pixels);
break;
case DOWN:
drawLineDown(g, pixels);
break;
}
}
private void drawLineLeft(Graphics g, int pix){
if(pix > 0){
g.drawLine(x, y, x-10*pix, y);//left
x =x -10*pix;
}
}
private void drawLineUp(Graphics g, int pix){
if(pix > 0){
g.drawLine(x, y, x, y-10*pix);//up
y = y-10*pix;
}
}
private void drawLineRight(Graphics g, int pix){
if(pix > 0){
g.drawLine(x, y, x+10*pix, y);//right
x = x+10*pix;
}
}
private void drawLineDown(Graphics g, int pix){
if(pix > 0){
g.drawLine(x, y, x, y+10*pix);// down
y = y+10*pix;
}
}
}
Applet works, but the problem is to save previous lines, when new one is drawn. When user enters direction and length of line and presses the button, new line appears on the screen, but the previous one disappears. I know that the problem is with paintComponent method, but I don't know how to exactly correct my code to make all lines visible. I was suggested to store point coordinates in array and then paint lines by looping throught array in paintComponent, but I don't know how to achieve this. Maybe there is better solution?
As I said in your last question, the case state is like an if-else, you only ever allow it to draw a single line. You need to maintain a List of "lines" which can be iterated over each time the paintComponent method is called
Because a line is represented by multiple properties, it's best to encapsulate that information into a simple class or POJO
public enum Direction {
UP, DOWN, LEFT, RIGHT
}
public class Line {
private Direction direction;
private int length;
public Line(Direction direction, int length) {
this.direction = direction;
this.length = length;
}
public Direction getDirection() {
return direction;
}
public int getLength() {
return length;
}
}
Here, I've separated the direction properties into a simple enum, this allows you to reference the properties more easily elsewhere within your program
Then you maintain a List of Lines, which when paintComponent is called, you simply re-iterate over and repaint...
public class Canvas extends JPanel {
public static final int WIDTH = 600, HEIGHT = 500;
public int x = WIDTH / 2;
public int y = HEIGHT / 2;
private List<Line> lines;
public Canvas() {
lines = new ArrayList<>(25);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
public void addPatt(Direction direction, int length) {
lines.add(new Line(direction, length));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Line line : lines) {
switch (line.getDirection()) {
case UP:
drawLineUp(g, line.getLength());
break;
case DOWN:
drawLineDown(g, line.getLength());
break;
case LEFT:
drawLineLeft(g, line.getLength());
break;
case RIGHT:
drawLineDown(g, line.getLength());
break;
}
}
}
private void drawLineLeft(Graphics g, int pix) {
if (pix > 0) {
g.drawLine(x, y, x - 10 * pix, y);//left
x = x - 10 * pix;
}
}
private void drawLineUp(Graphics g, int pix) {
if (pix > 0) {
g.drawLine(x, y, x, y - 10 * pix);//up
y = y - 10 * pix;
}
}
private void drawLineRight(Graphics g, int pix) {
if (pix > 0) {
g.drawLine(x, y, x + 10 * pix, y);//right
x = x + 10 * pix;
}
}
private void drawLineDown(Graphics g, int pix) {
if (pix > 0) {
g.drawLine(x, y, x, y + 10 * pix);// down
y = y + 10 * pix;
}
}
}
Remember, painting in Swing is destructive, you are expected to repaint the entire state of the component every time the paintComponent is called.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works
I was suggested to store point coordinates in array and then paint lines by looping throught array in paintComponent, but I don't know how to achieve this.
It depends on your exact requirement.
If you need the ability to add/remove lines, then this might be the best approach.
If you only need the ability to add lines then you might want to paint the lines directly to a BufferedImage and then just display the BufferedImage as an Icon on a JLabel.
Check out Custom Painting Approaches which compares the two approaches and provides working examples of both approaches.
Related
I am done with randomizing circles, now my challenge is on adding circles to a straight line queue like the actual queue(i want the circles to queue from left to right preferably at the top of the Frame. When i call the removeCircle(), i want the circle to leave the queue and move down the frame. Can you help me with the addCircle() and removeCircle() methods. Thank you in advance.
public class AirTrafficLanding
{
private static void createAndShowUI()
{
PlanePanel panel = new PlanePanel(1);
JFrame frame = new JFrame("Air traffic Landing System");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.add( panel );
frame.pack();
frame.setVisible( true );
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
static class PlanePanel extends JPanel implements ActionListener
{
private ArrayList<Plane> planes = new ArrayList<Plane>();
public PlanePanel(int planeCount)
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setLayout( null );
setBackground( Color.BLACK );
Random random = new Random();
for (int i = 0; i < planeCount; i++)
{
Plane plane = new Plane();
plane.setRandomColor(true);
plane.setLocation(0, 700);
//plane.setLocation(random.nextInt(screenSize.width), random.nextInt(screenSize.height));
plane.setMoveRate(32, 32, 1, 1, true);
plane.setSize(32, 32);
planes.add( plane );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Plane plane: planes)
{
plane.draw(g);
}
}
public void startAnimation()
{
Timer timer = new Timer(55, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Plane plane : planes)
{
plane.move(this);
}
}
}
static class Plane
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Plane()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.ORANGE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
I am expecting to start with zero circles in a queue and add one circle at 10 seconds interval. I want one circle to move from the queue to the bottom of the queue after every 15 seconds. I am done with randomizing circles, now my challenge is on adding circles to a straight line queue like the actual queue(i want the circles to queue from left to right preferably at the top of the Frame. When i call the removeCircle(), i want the circle to leave the queue and move down the frame. Can you help me with the addCircle() and removeCircle() methods. Thank you in advance.
Problem solving should be done one problem at a time. Don't attempt to write the entire program before you start testing.
So break your program down to the basics:
You need a Timer for the animation to move each object. So start by adding a single object to the ArrayList and then when the Timer fires you iterate through the ArrayList and move each object. It doesn't matter that there is only a single object in the ArrayList to start.
Then you create another Timer that fires every 15 seconds to add another object to the ArrayList.
Then you add logic to give this object a random color
Then you add logic to give this object a priority.
Once all the above is done you add another Timer to remove and object from the ArrayList.
The basic structure of your code needs to be changed.
You should NOT be using static variables. So you need to redesign the structure of your classes.
You should have a CirclePanel class. This class will contain the properties needed to paint the objects, so it will contain the ArrayList. It will also need methods like "addCircle()" and "removeCircle()". These methods will be invoke by the appropriate Timer.
You need to use a Swing Timer for all the Timers, not a util Timer. When the Swing Timer fires the code will execute single threaded on the Event Dispatch Thread which should prevent the ConcurrentModificationException from happening.
Check out: How do I paint multiple objetcs that move at different speeds in Java? for an example to help with the restructuring.
It doesn't do everything you need but it is a good start. It will animate an ArrayList of object. So you will need to modify the code to populate the ArrayList one object at a time and remove one object at a time.
Edit:
If you want to add more circles then you need another Timer. Using the original code I provide you with you can do something like:
public void startAnimation()
{
//Timer timer = new Timer(75, this);
//timer.start();
Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
});
timer.start();
Timer timer2 = new Timer(3000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
addBalls(1);
}
});
timer2.start();
}
So the first Timer animates the balls already in the ArrayList. The second Timer adds a new ball to the ArrayList.
I'll let you write the Timer to remove a ball from the ArrayList. I have already suggested this is a single statement in the ActionListener.
I am working on homework for class, and its late because I can't seem to understand the material despite all the research that I am doing. I am a beginner and do not know much in the way of java. Also, this is my first post so please be forgiving when you are reading this.
I am building on source code from my textbook, which I updated recently for past homework, but now I am trying to generate a class that draws multiple squares and moves those objects independently and at different speeds. They will all need to rebound off the walls as well. I followed the instructions and created two arrays that will hold the random x and y values between 1 and 10. However, I struggle with arrays and I am sure that I am not doing it correctly. So, I would love some feedback to see if I have it set up correctly.
I have a the jpanel pulling up and drawing, and as long as there is 1 square it is working fine bouncing off the walls, but things change when I draw more than one. The do not move independently and they also share the same speed. Some even disappear from time to time. This has really thrown me off. I appreciate any help!
In short, I am trying to paint new squares that all travel in different directions and at different speeds. Per the instructions we are suppose create and use a two arrays that handle the x and y values.
Here is what I have so far:
public class DotsPanel extends JPanel
{
private int delay = 15;
private final int SIZE = 7, IMAGE_SIZE = 3; // radius of each dot
private Timer timer;
private int x, y, i;
private ArrayList<Point> pointList;
static int [] xarray = new int [1000];
static int [] yarray = new int [1000];
Random rand = new Random();
//-----------------------------------------------------------------
// Constructor: Sets up this panel to listen for mouse events.
//-----------------------------------------------------------------
public DotsPanel()
{
pointList = new ArrayList<Point>();
int [] xarray = new int [1000];
int [] yarray = new int [1000];
timer = new Timer(delay, new ReboundListener());
addMouseListener (new DotsListener());
addMouseMotionListener (new DotsListener());
setBackground(Color.gray);
setPreferredSize(new Dimension(700, 500));
for(int i = 0; i < xarray.length; i++)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
timer.start();
}
//-----------------------------------------------------------------
// Draws all of the dots stored in the list.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.setColor(Color.BLUE);
for (Point spot : pointList)
{
page.fillRect(spot.x-SIZE, spot.y-SIZE, 25, 25);
page.drawString("Count: " + pointList.size(), 5, 15);
}
}
//*****************************************************************
// Represents the listener for mouse events.
//*****************************************************************
private class DotsListener implements MouseListener, MouseMotionListener
{
//--------------------------------------------------------------
// Adds the current point to the list of points and redraws
// the panel whenever the mouse button is pressed.
//--------------------------------------------------------------
public void mousePressed(MouseEvent event)
{
pointList.add(event.getPoint());
repaint();
}
public void mouseDragged(MouseEvent event)
{
// initially I had two xarray and yarray in here just like in
// mouseClicked
// but it did not change anything when removed
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event)
{
xarray[i] = rand.nextInt(7);
yarray[i] = rand.nextInt(7);
}
public void mouseReleased(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mouseMoved(MouseEvent e) {}
}
private class ReboundListener implements ActionListener
{
//--------------------------------------------------------------
// Updates the position of the image and possibly the direction
// of movement whenever the timer fires an action event.
//--------------------------------------------------------------
public void actionPerformed(ActionEvent event)
{
for (Point spot : pointList)
{
spot.x += xarray[i];
spot.y += yarray[i];
if (spot.x <= 0 || spot.x >= 700)
xarray[i] = xarray[i] * -1;
if (spot.y <= 0 || spot.y >= 500)
yarray[i] = yarray[i] * -1;
repaint();
}
}
}
}
However, I struggle with arrays and I am sure that I am not doing it correctly.
I wouldn't use Arrays.
Instead, have a Ball object manage its own state. Then you can have different color, speed, size etc for each Ball. Then when the Timer fires you just calculate the new position and repaint the Ball.
Here is an example to get you started:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BallAnimation4
{
private static void createAndShowUI()
{
BallPanel panel = new BallPanel();
JFrame frame = new JFrame("BallAnimation4");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setSize(800, 600);
frame.setLocationRelativeTo( null );
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible( true );
panel.addBalls(5);
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class BallPanel extends JPanel implements ActionListener
{
private ArrayList<Ball> balls = new ArrayList<Ball>();
public BallPanel()
{
setLayout( null );
setBackground( Color.BLACK );
}
public void addBalls(int ballCount)
{
Random random = new Random();
for (int i = 0; i < ballCount; i++)
{
Ball ball = new Ball();
ball.setRandomColor(true);
ball.setLocation(random.nextInt(getWidth()), random.nextInt(getHeight()));
ball.setMoveRate(32, 32, 1, 1, true);
// ball.setMoveRate(16, 16, 1, 1, true);
ball.setSize(32, 32);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
public void startAnimation()
{
Timer timer = new Timer(75, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Ball ball : balls)
{
ball.move(this);
}
}
class Ball
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Ball()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.BLUE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
Since your Arrays only contain the Point you want to paint you don't have any information about the speed each point should be moved at. The best you could do is create a random amount each point should be moved each time its location is changed. This would give erratic movement as each time you move a point the distance would be random.
If you want more constant speed then you would need to create a second Array to contain the distance each point should move every time.
This starts to get messy creating a new Array every time you want a new property to be unique for the object you want to paint. That is why the approach to create a custom Object with multiple properties is easier to manage.
I created an app that contains a square that bounces every time it touches an edge of the frame.I don't have issues lunching the app,the problem is that i don't know how to create various threads in order to have multiples squares inside the frame.
I tried multiple things but i can't figure out where i should create the threads.
I also noticed that the square is visible only when i add it directly inside the frame and not when i put it inside a JPanel.
Square.java
public class Square extends JComponent implements ActionListener {
int width = 20;
int height = 20;
double y = Math.random() * 360;
double x = Math.random() * 360;
boolean xMax = false;
boolean yMax = false;
boolean xMin = true;
boolean yMin = true;
Rectangle2D.Double square = new Rectangle2D.Double(x, y, width, height);
public Square() {
Timer t = new Timer(2, this);
t.start();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(Color.BLUE);
g2.fill(square);
x_y_rules();
}
public void x_y_rules() {
if (xMax == true) {
x = x - 0.5;
if (x <= 0) {
xMax = false;
}
} else {
x = x + 0.5;
if (x >= this.getWidth()) {
xMax = true;
}
}
if (yMax == true) {
y = y - 0.5;
if (y <= 0) {
yMax = false;
}
} else {
y = y + 0.5;
if (y >= this.getHeight()) {
yMax = true;
}
}
square.setFrame(x, y, width, height);
}
#Override
public void actionPerformed(ActionEvent arg0) {
repaint();
}
}
App.java
public class App extends JFrame {
public static void main(String[] args) {
JFrame jf = new JFrame();
Square sqr = new Square();
jf.setSize(400, 400);
jf.setVisible(true);
jf.add(sqr);
jf.setDefaultCloseOperation(EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
}
}
Is it normal that despite i put a time of 2 inside the Timer,the square moves very slowly?
Issues:
you've got program logic, the x_y_rules() method call, inside of the paintComponent method. Get it out as it does not belong there, and instead put it into the Timer's ActionListener code where it belongs.
you can give each Square its own Swing Timer if you want. This isn't really a threading issue since each Timer's ActionListener will run on the EDT.
Two milliseconds is an unrealistic time slice to expect to use in a Swing Timer and no timer will run that fast. 11 to 13 is about the fastest to expect or hope for.
if you want your sprite to move faster, give it a greater value for delta-x and delta-y in your movement code.
Your JComponent has no preferred size defined which is likely why it's not showing up in the JPanel, since the default FlowLayout will size it then to [0, 0]. Override its getPreferredSize() and have it return a reasonable Dimension value.
you're calling setVisible(true) on your JFrame before adding all components, a no-no.
Ok,i put a getPrefferedSize() inside the square class but i've encountered a problem: the squares are not "together",it's like they're bouncing on separate panels
Then your program structure is broken. You really don't want create separate Swing components, and in fact your Square class shouldn't extend JComponent or JPanel. Rather
Square should be a logical class, one that extends from nothing (other than default Object).
Give it a drawing method, say public void draw(Graphics g) {....}
Create one class that extends JPanel, say called DrawingPanel, and override its paintComponent method.
Give the DrawingPanel class an ArrayList<Square> so that it can hold multiple Square objects.
Give the DrawingPanel class a Swing Timer
In the DrawingPanel class's Timer, have it update the position of all the Squares in the ArrayList, and then call repaint()
In the paintComponent method, iterate through all the Squares in the list, using a for loop, and call each one's draw method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private static final Color[] SQUARE_COLOR = { Color.BLUE, Color.CYAN, Color.DARK_GRAY,
Color.BLACK, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
Color.PINK, Color.RED, Color.YELLOW };
List<Square> squareList = new ArrayList<>();
public DrawingPanel() {
// create a bunch of squares
for (int i = 0; i < SQUARE_COLOR.length; i++) {
squareList.add(new Square(SQUARE_COLOR[i], PREF_W, PREF_H));
}
setBackground(Color.WHITE);
// create and start the timer
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// simply draw all the squares in the list
for (Square square : squareList) {
square.draw(g);
}
}
// set size of JPanel
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// simply iterate through list and move all squares
for (Square square : squareList) {
square.move();
}
repaint(); // then repaint the GUI
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
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());
}
}
// this class does *not* extend JPanel or JComponent
class Square {
public static final int WIDTH = 20;
// location of Square
private double sqrX;
private double sqrY;
// X and Y speed
private double deltaX;
private double deltaY;
// width and height of DrawingPanel JPanel
private int dpWidth;
private int dpHeight;
// image to draw
private Image image;
public Square(Color color, int dpWidth, int dpHeight) {
this.dpWidth = dpWidth;
this.dpHeight = dpHeight;
// create square at random location with random speed
sqrX = Math.random() * (dpWidth - WIDTH);
sqrY = Math.random() * (dpHeight - WIDTH);
deltaX = Math.random() * 10 - 5;
deltaY = Math.random() * 10 - 5;
// one way to draw it is to create an image and draw it
image = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, WIDTH, WIDTH);
g.dispose();
}
public void move() {
// check that we're not hitting boundaries
if (sqrX + deltaX < 0) {
deltaX = Math.abs(deltaX);
}
if (sqrX + deltaX + WIDTH >= dpWidth) {
deltaX = -Math.abs(deltaX);
}
sqrX += deltaX;
// check that we're not hitting boundaries
if (sqrY + deltaY < 0) {
deltaY = Math.abs(deltaY);
}
if (sqrY + deltaY + WIDTH >= dpHeight) {
deltaY = -Math.abs(deltaY);
}
sqrY += deltaY;
}
public void draw(Graphics g) {
int x = (int) sqrX;
int y = (int) sqrY;
g.drawImage(image, x, y, null);
}
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I created a "bullet" that moves in the positive x direction after pressing the space bar. It resets at a certain position once it goes off screen. However, how can I make it so that every time I press the space bar, a bullet is shot? Is creating a new object necessary, or can I keep the same object but instantiate a second time while the first is still animating (I feel like that wouldn't work). I'm a newbie so any help is appreciated.
Edit: My code thus far. The first is for the PlayState and the second is the ball class
public class GameMain {
private static final String GAME_TITLE = "LoneBall (Chapter 5)";
public static final int GAME_WIDTH = 800;
public static final int GAME_HEIGHT = 450;
public static Game sGame;
public static void main(String[] args) {
JFrame frame = new JFrame(GAME_TITLE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false); // Prevents manual resizing of window
sGame = new Game(GAME_WIDTH, GAME_HEIGHT);
frame.add(sGame);
frame.pack();
frame.setVisible(true);
frame.setIconImage(Resources.iconimage); // This is the new line!
}
}
public class PlayState extends State {
private static final int PADDLE_WIDTH = 15;
private static final int PADDLE_HEIGHT = 60;
private Ball ball;
private static final int BALL_DIAMETER = 10;
private int playerScore = 0;
private Font scoreFont;
#Override
public void init() {
scoreFont = new Font("SansSerif", Font.BOLD, 25);
ball = new Ball(300, 200, BALL_DIAMETER, BALL_DIAMETER);
}
#Override
public void update() {
ball.update();
if (ball.isDead()) {
playerScore++;
ball.reset();
}
//adjust player score here
}
#Override
public void render(Graphics g) {
// Draw Background
g.setColor(Resources.darkRed);
g.fillRect(0, 0, GameMain.GAME_WIDTH,
GameMain.GAME_HEIGHT);
// Draw Separator Line
// Draw Paddles
g.setColor(Color.white);
g.drawImage(Resources.gun, 0, 150, 300, 200, null);
// Draw Ball
g.setColor(Color.yellow);
g.fillOval(ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
// Draw UI
g.setFont(scoreFont); // Sets scoreFont as current font
g.drawString("" + playerScore, 350, 40); // Draws String using current
// font
}
#Override
public void onClick(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void onKeyPress(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
ball.velX = 10;
}
}
#Override
public void onKeyRelease(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP
|| e.getKeyCode() == KeyEvent.VK_DOWN) {
}
}
}
Then the ball class:
public class Ball {
private int x, y, width, height;
public int velX;
private Rectangle oval;
public Ball(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
final int velX = 0;
oval = new Rectangle(x, y, width, height);
}
public void update() {
x += velX;
}
private void updateRect() {
oval.setBounds(x, y, width, height);
}
public boolean isDead() {
return (x < 0 || x + width > GameMain.GAME_WIDTH || y < 0 || y > GameMain.GAME_HEIGHT);
}
public void reset() {
x = 300;
y = 200;
velX = 0;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getRect() {
return oval;
}
}
You make no mention of your GUI library, so I'll assume that it's a Swing application, but hopefully you'll fix this by giving us a lot more information and your current pertinent code.
One solution is to use Key Bindings to bind a bullet creation action to a spacebar press. This gets around the issue of KeyListeners and focus problems that plague their use.
e.g.,
// assuming displayed a main JPanel called mainJPanel
ActionMap actionMap = mainJPanel.getActionMap();
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = mainJPanel.getInputMap(condition);
// assuming a class, CreateBulletAction, that extends AbstractAction
// and that creates your bullets
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space");
actionMap.put("space", new CreateBulletAction());
I can not resize the two rectangles, but only the right one. What should I add to my code ?
I want also that when the lower edge of the left rectangle is moved, it also moves the upper edge of the right rectangle.
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class Resizing extends JPanel {
Rectangle rect = new Rectangle(100,100,150,150);
Rectangle rect2 = new Rectangle(300,100,150,150);
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(0, 0, 200));
g2.fill(rect);
g2.setColor(new Color(0, 0, 200));
g2.fill(rect2);
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Resizing essai = new Resizing();
Resizer1 rectangle = new Resizer1(essai);
essai.addMouseListener(rectangle);
essai.addMouseMotionListener(rectangle);
f.add(essai);
Resizing test2 = new Resizing();
Resizer2 rectangle2 = new Resizer2(test2);
test2.addMouseListener(rectangle2);
test2.addMouseMotionListener(rectangle2);
f.add(test2);
f.setSize(600,400);
f.setLocation(100,100);
f.setVisible(true);
}
}
class Resizer1 extends MouseAdapter {
Resizing component;
boolean dragging = false;
// Give user some leeway for selections.
final int PROX_DIST = 3;
public Resizer1(Resizing r) {
component = r;
}
#Override
public void mousePressed(MouseEvent e) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is set for resizing, allow dragging.
dragging = true;
}
}
#Override
public void mouseReleased(MouseEvent e) {
dragging = false;
}
#Override
public void mouseDragged(MouseEvent e) {
if(dragging){
Point p = e.getPoint();
Rectangle r = component.rect;
int type = component.getCursor().getType();
int dy = p.y - r.y;
switch(type) {
case Cursor.N_RESIZE_CURSOR:
int height = r.height - dy;
r.setRect(r.x, r.y+dy, r.width, height);
break;
case Cursor.S_RESIZE_CURSOR:
height = dy;
r.setRect(r.x, r.y, r.width, height);
break;
default:
System.out.println("unexpected type: " + type);
}
component.repaint();
}
}
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if(!isOverRect(p)) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is not over rect reset it to the default.
component.setCursor(Cursor.getDefaultCursor());
}
return;
}
// Locate cursor relative to center of rect.
int outcode = getOutcode(p);
Rectangle r = component.rect;
switch(outcode) {
case Rectangle.OUT_TOP:
if(Math.abs(p.y - r.y) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.N_RESIZE_CURSOR));
}
break;
case Rectangle.OUT_BOTTOM:
if(Math.abs(p.y - (r.y+r.height)) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.S_RESIZE_CURSOR));
}
break;
default: // center
component.setCursor(Cursor.getDefaultCursor());
}
}
/**
* Make a smaller Rectangle and use it to locate the
* cursor relative to the Rectangle center.
*/
private int getOutcode(Point p) {
Rectangle r = (Rectangle)component.rect.clone();
r.grow(-PROX_DIST, -PROX_DIST);
return r.outcode(p.x, p.y);
}
/**
* Make a larger Rectangle and check to see if the
* cursor is over it.
*/
private boolean isOverRect(Point p) {
Rectangle r = (Rectangle)component.rect.clone();
r.grow(PROX_DIST, PROX_DIST);
return r.contains(p);
}
}
class Resizer2 extends MouseAdapter {
Resizing component;
boolean dragging = false;
// Give user some leeway for selections.
final int PROX_DIST = 3;
public Resizer2(Resizing r) {
component = r;
}
#Override
public void mousePressed(MouseEvent e2) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is set for resizing, allow dragging.
dragging = true;
}
}
#Override
public void mouseReleased(MouseEvent e2) {
dragging = false;
}
#Override
public void mouseDragged(MouseEvent e2) {
if(dragging){
Point p = e2.getPoint();
Rectangle r = component.rect2;
int type = component.getCursor().getType();
int dy = p.y - r.y;
switch(type) {
case Cursor.N_RESIZE_CURSOR:
int height = r.height - dy;
r.setRect(r.x, r.y+dy, r.width, height);
break;
case Cursor.S_RESIZE_CURSOR:
height = dy;
r.setRect(r.x, r.y, r.width, height);
break;
default:
System.out.println("unexpected type: " + type);
}
component.repaint();
}
}
#Override
public void mouseMoved(MouseEvent e2) {
Point p = e2.getPoint();
if(!isOverRect(p)) {
if(component.getCursor() != Cursor.getDefaultCursor()) {
// If cursor is not over rect reset it to the default.
component.setCursor(Cursor.getDefaultCursor());
}
return;
}
// Locate cursor relative to center of rect.
int outcode = getOutcode(p);
Rectangle r = component.rect2;
switch(outcode) {
case Rectangle.OUT_TOP:
if(Math.abs(p.y - r.y) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.N_RESIZE_CURSOR));
}
break;
case Rectangle.OUT_BOTTOM:
if(Math.abs(p.y - (r.y+r.height)) < PROX_DIST) {
component.setCursor(Cursor.getPredefinedCursor(
Cursor.S_RESIZE_CURSOR));
}
break;
default: // center
component.setCursor(Cursor.getDefaultCursor());
}
}
/**
* Make a smaller Rectangle and use it to locate the
* cursor relative to the Rectangle center.
*/
private int getOutcode(Point p) {
Rectangle r = (Rectangle)component.rect2.clone();
r.grow(-PROX_DIST, -PROX_DIST);
return r.outcode(p.x, p.y);
}
/**
* Make a larger Rectangle and check to see if the
* cursor is over it.
*/
private boolean isOverRect(Point p) {
Rectangle r = (Rectangle)component.rect2.clone();
r.grow(PROX_DIST, PROX_DIST);
return r.contains(p);
}
}
Problems I see on inspection of your code:
You are adding 2 JPanels to your JFrame, but only one will show since they're being added in a default fashion to a BorderLayout-using container. Edit: I see now why you're doing this, but as explained below, you should not be doing this. Only create one Resizing object and add it once to the JFrame. The one Resizing will show both rectangles, and the single MouseAdapter should be coded to allow you to interact with both rectangles.
You are not making your Rectangle fields private and are allowing outside classes (namely Resizer1) to directly access and minipulate your fields. You'd be better off using public methods that allow other classes to selectively query your class for its state or to change your class's state.
Don't use two Resizer classes, Resizer1 and Resizer2, and it is this as well as your adding two Resizing objects to the JFrame that are in fact the main reason for your problems. Instead, use just one Resizer class, and use it as a single MouseAdapter added to a single Resizing object. Then in this single class, allow both rectangles to be changed.
Please post comments if you have any questions.
You ask:
Thank you, but how can I allow both rectangles to be changed ? In class Resizer, there is only one component (r) ?
There is only one component, but it holds two rectangles, and it knows the difference between the two rectangles since it has two rectangle variables.
Edit 2
Consider:
Editing your original question and adding your new code to the bottom, deleting your redundant new question.
Creating a non-GUI object, say called MyRectangle, that holds a Rectangle object.
This new class can have methods that allow your to pass in a Point or an x and y positions, and return information to let the calling code know if the mouse is over the top or bottom edge (your code already does this, so this should be no problem for you).
This new class will have mutator (setter) methods that allow outside classes set its rectangle y position and height.
The new class will have a fill method that accepts a Graphics2D parameter and uses it to fill the Rectangle that it holds.
Then give your Resizer class a List<MyRectangle>, actually an ArrayList of them, say called myRectangleList, and you can add two MyRectangle objects to it
Then give Resizer a getMyRectangleList method that returns the list.
Then in your MouseAdapter, iterate through the List to see if the mouse is over an edge
etc...
e.g.,
class MyRectangle {
private Rectangle rect;
private String name;
public MyRectangle(int x, int y, int width, int height, String name) {
rect = new Rectangle(x, y, width, height);
this.name = name;
}
public void fill(Graphics2D g2) {
g2.fill(rect);
}
public int getOutcode(Point p) {
// return ... do what you need to figure tihs out
}
public boolean isOverRect(Point p) {
// return ... do what you need to figure tihs out
}
public String getName() {
return name;
}
#Override
public String toString() {
return name + " " + rect.toString();
}
}
And then something like:
public class Resizing2 extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color RECT_COLOR = Color.blue;
private List<MyRectangle> rectangleList = new ArrayList<>();
public Resizing2() {
rectangleList.add(new MyRectangle(100, 100, 150, 150, "Rect 1"));
rectangleList.add(new MyRectangle(300, 100, 150, 150, "Rect 2"));
}
public List<MyRectangle> getRectangleList() {
return rectangleList;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (MyRectangle rect : rectangleList) {
rect.fill(g2);
}
}
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
// ..... etc...