I am trying to get the console to print when the mouse is pressed within an object of class RenderCanvas which extends JPanel. However, I am getting no feedback when I press the mouse down in the window. Any suggestions as to what I might be able to change to make the MouseListener work?
Here is my code:
RenderCanvas Class:
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.event.MouseAdapter;
public class RenderCanvas extends JPanel {
private List<Rect> rectangles = new ArrayList<Rect>();
private List<Line> lines = new ArrayList<Line>();
public void renderCanvas() {
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
addRect(0, 0, 200, 200, Color.ORANGE);
System.out.println(e);
}
});
}
public void paintComponent(Graphics g) {
for (Rect rectangle : rectangles) {
rectangle.draw(g);
}
for (Line line : lines) {
line.draw(g);
}
}
public void addRect(int x, int y, int width, int height, Color color) {
Rect rectangle = new Rect(x, y, width, height, color);
this.rectangles.add(rectangle);
}
public void addLine(int y, int width, Color color) {
Line line = new Line(y, width, color);
this.lines.add(line);
}
}
Main Class:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Main {
public static void main(String[] args) {
JFrame window = new JFrame("Window");
RenderCanvas canvas = new RenderCanvas();
window.setContentPane(canvas);
window.setSize(640, 360);
window.setLocation(640, 360);
window.setVisible(true);
}
}
Thanks in advance for any help!
public void renderCanvas() is NOT a constructor. Change
public void renderCanvas()
to
public RenderCanvas()
Notice the upper-case "R" and the absence of the "void" return type
void RenderCanvas() is not being called. I believe you mean just public RenderCanvas() instead of public void RenderCanvas, since you're only using the ctor in the main method.
I think you're intending this method:
public void renderCanvas() {
to be a constructor for the RenderCanvas class; it's not, though, for two reasons: it's not capitalized the same way (small r vs capital R) and also it has a return type. Constructors have no return type; the line should look like
public RenderCanvas() {
Because this isn't a constructor, it's a method, and nobody's calling it, so your event handler is never being added.
Related
I'm creating a code where I need to change a Panel color with the one I'm still pressed on when I pass over it. For instance if let's say I pressed on a green panel and drag it over another one, this one should get the green color. However it doesn't work everytime, like sometimes it does change the color but sometimes it doesn't.
import java.awt.Color;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.util.Random;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.event.MouseInputListener;
public class Etabli extends JFrame {
JPanel paneprinci;
CarrePanel selected;
public Etabli() {
this.setVisible(true);
setTitle("Cadre");
setDefaultCloseOperation(EXIT_ON_CLOSE);
paneprinci=new JPanel(null);
setSize(600,600);
selected=new CarrePanel();
for (int i=0;i<10;i++) {
paneprinci.add(new CarrePanel(new Carre()));
}
this.getContentPane().add(paneprinci);
}
public static void main (String[]args) {
javax.swing.SwingUtilities.invokeLater(
new Runnable() {
public void run() {
Etabli e=new Etabli();
}
});
}
public class CarrePanel extends JPanel implements MouseInputListener{
private Carre carre;
private boolean etat;
private int xprev;
private int yprev;
public CarrePanel(Carre c) {
setBounds(new Random().nextInt(500),new Random().nextInt(500), 50, 50);
addMouseListener(this);
addMouseMotionListener(this);
this.setBackground(c.getColor());
this.carre=c;
}
public CarrePanel() {
setBounds(new Random().nextInt(500),new Random().nextInt(500), 50, 50);
addMouseListener(this);
addMouseMotionListener(this);
}
public void setCarre(Carre c) {
carre=c;
}
public Carre getCarre() {
return carre;
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(this.carre.getColor());
}
#Override
public void mouseEntered(MouseEvent e) {
if (selected.getCarre()!=null) {
this.carre.setColor(selected.getCarre().getColor());
this.setBackground(selected.getCarre().getColor());
}
}
#Override
public void mousePressed(MouseEvent e) {
etat=true;
selected.setCarre(this.carre);
xprev=e.getXOnScreen();
yprev=e.getYOnScreen();
}
#Override
public void mouseReleased(MouseEvent e) {
etat=false;
if(selected.getCarre()==this.carre) {
selected.setCarre(null);;
}
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseDragged(MouseEvent e) {
if(etat) {
int x=this.getX()+e.getXOnScreen()-xprev;
int y=this.getY()+e.getYOnScreen()-yprev;
this.setLocation(x,y);
xprev=e.getXOnScreen();
yprev=e.getYOnScreen();
}
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
}
Here's the code for Carre ( which is square in French )
import java.awt.Color;
import java.util.Arrays;
import java.util.Random;
import java.awt.Color;
public class Carre {
private Color color;
public Carre() {
color=new Color(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255));
}
public Color getColor() {
return color;
}
public void setColor(Color c) {
color=c;
}
}
What I don't understand is why does it works sometimes, I don't know if the problem comes from how I did my drag event or if there's something wrong elsewhere.
Thank you for your awnser.
What I don't understand is why does it works sometimes,
When you drag a square to another square you will notice that sometimes the dragged square is painted:
below the other square
above the other square
When the square is painted above the other square a mouseEntered event is not generated because the default logic will only generate the event for the top component.
When the square is painted below the other square, then your mouseEntered event is generated and your logic works as expected.
The order of painting of a component is determined by its ZOrder. So you can adjust the ZOrder of the dragged component to be the last component painted.
In the mousePressed logic you can add:
JPanel parent = (JPanel)getParent();
parent.setComponentZOrder(this, parent.getComponentCount() - 1);
This will make sure that the dragged component is always painted below the other components.
Also note that your etat variable is not needed. The mouseDragged event is only generated while the mouse is pressed.
I created the following GUI.
I added a java.awt.Rectangle to the Carre class to holds the bounds of each rectangle. The Carre class now holds all of the information to draw a Carre instance.
I created a CarreModel class to hold a java.util.List of Carre instances. I create all of the Carre instances in the constructor of the CarreModel class.
I created one JFrame and one drawing JPanel. I left your JFrame extend, but generally, you should use Swing components. You only extend a Swing component, like JPanel in CarrePanel, when you want to override one of the class methods.
I separated the MouseListener and MouseMotionListener from the CarrePanel JPanel class. I find it helps to keep separate functions in separate methods and classes. It makes it much easier to focus on one part of the application at a time.
The MouseListener mousePressed method has a function at the bottom to move the selected carre to the top of the Z-order. This makes the carre move more visually appealing.
The MouseListener updateCarreColor method checks for carre intersections and changes the color of any carre that intersects the selected carre.
Here's the complete runnable code. I made all the classes inner classes so I can post the code as one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
public class Etabli extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Etabli();
}
});
}
private CarreModel model;
private CarrePanel paneprinci;
public Etabli() {
this.model = new CarreModel();
this.setTitle("Cadre");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
paneprinci = new CarrePanel();
this.add(paneprinci, BorderLayout.CENTER);
this.pack();
this.setLocationByPlatform(true);
this.setVisible(true);
}
public class CarrePanel extends JPanel {
private static final long serialVersionUID = 1L;
public CarrePanel() {
this.setPreferredSize(new Dimension(550, 550));
ColorListener listener = new ColorListener();
this.addMouseListener(listener);
this.addMouseMotionListener(listener);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Carre carre : model.getCarres()) {
g2d.setColor(carre.getColor());
Rectangle r = carre.getBounds();
g2d.fillRect(r.x, r.y, r.width, r.height);
}
}
}
public class ColorListener implements MouseInputListener {
private Carre selectedCarre;
private Point pressedPoint;
#Override
public void mouseClicked(MouseEvent event) {
}
#Override
public void mouseEntered(MouseEvent event) {
}
#Override
public void mousePressed(MouseEvent event) {
pressedPoint = event.getPoint();
selectedCarre = null;
for (Carre carre : model.getCarres()) {
if (carre.getBounds().contains(pressedPoint)) {
selectedCarre = carre;
break;
}
}
if (selectedCarre != null) {
List<Carre> carres = model.getCarres();
carres.remove(selectedCarre);
carres.add(selectedCarre);
}
}
#Override
public void mouseReleased(MouseEvent event) {
updateCarrePosition(event.getPoint());
updateCarreColor();
}
#Override
public void mouseExited(MouseEvent event) {
}
#Override
public void mouseDragged(MouseEvent event) {
updateCarrePosition(event.getPoint());
updateCarreColor();
}
#Override
public void mouseMoved(MouseEvent event) {
}
private void updateCarrePosition(Point point) {
int x = point.x - pressedPoint.x;
int y = point.y - pressedPoint.y;
if (selectedCarre != null) {
selectedCarre.incrementBounds(x, y);
paneprinci.repaint();
pressedPoint.x = point.x;
pressedPoint.y = point.y;
}
}
private void updateCarreColor() {
if (selectedCarre != null) {
for (Carre carre : model.getCarres()) {
if (!carre.equals(selectedCarre) &&
carre.getBounds().intersects(selectedCarre.getBounds())) {
carre.setColor(selectedCarre.getColor());
paneprinci.repaint();
}
}
}
}
}
public class CarreModel {
private final List<Carre> carres;
public CarreModel() {
this.carres = new ArrayList<>();
for (int i = 0; i < 10; i++) {
this.carres.add(new Carre());
}
}
public List<Carre> getCarres() {
return carres;
}
}
public class Carre {
private Color color;
private final Random random;
private Rectangle bounds;
public Carre() {
random = new Random();
int red = random.nextInt(128);
int green = random.nextInt(128);
int blue = random.nextInt(128);
color = new Color(red, green, blue);
int x = random.nextInt(500);
int y = random.nextInt(500);
bounds = new Rectangle(x, y, 50, 50);
}
public void incrementBounds(int x, int y) {
bounds.x += x;
bounds.y += y;
}
public Color getColor() {
return color;
}
public void setColor(Color c) {
color = c;
}
public Rectangle getBounds() {
return bounds;
}
}
}
I'm a beginner in Java and this time I'm trying to learn more by finding code examples and editing them, for example from this website. I have a JFrame and each time it (or more precise the JPanel in it) is clicked on, a circle is drawn which will grow/expand outwards like a water ripple. Each circle starts with a certain radius and will be removed or redrawn when reaching a bigger radius (I chose radius "r" from 10 to 200). I have two programs for this which almost work but are missing something. If I'm correct, I might also know what their problems are but I can't figure out how to solve them:
One generates growing circles but all of them have the same size, no matter when they're generated, since I can't find out how to assign the radius to a single circle. The code is adapted from here:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Ripples extends JPanel implements ActionListener{
public int r = 10;
private ArrayList<Point> p;
public Ripples() {
p = new ArrayList<>();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
p.add(new Point(e.getX(), e.getY()));
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.CYAN);
for (Point pt : p) {
g2.drawOval(pt.x-r, pt.y-r, 2*r, 2*r);
}
}
#Override
public void actionPerformed(ActionEvent evt) {
if(r<200){
r++;
} else {
r = 10;
}
repaint();
}
public static void Gui() {
JFrame f = new JFrame();
Ripples p = new Ripples();
p.setBackground(Color.WHITE);
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(500,300);
f.setVisible(true);
Timer t = new Timer(20,p);
t.start();
}
public static void main(String[] args) {
Gui();
}
}
The other program(I've forgotten where I got it from) has circles with different radii depending on when they were generated, however the circles "flicker", because -as far as I understand- they are all processed at the same time, because the code doesn't include an Array to store and update each circle individually like the one above does:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Ripples extends JPanel {
int x,y;
int r = 10;
public Ripples(int x, int y) {
this.x = x;
this.y = y;
Timer t = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (r<200) {
r++;
} else {
r=10;
}
revalidate();
repaint();
}
});
t.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.CYAN);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawOval(x-r,y-r,2*r,2*r);
}
public static void Gui() {
JFrame f = new JFrame("Water Ripples");
JPanel p0 = new JPanel();
p0.setBackground(Color.WHITE);
f.add(p0);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setBounds(100,100,600,500);
f.setVisible(true);
f.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
Ripples rG = new Ripples(e.getX(), e.getY());
rG.setBackground(Color.WHITE);
f.add(rG);
}
});
}
public static void main(String[] args) {
Gui();
}
}
So how can I solve this so that I get the circles growing independent from each other? I'd prefer a solution/improvement/hint for the upper code because I think its structured better than the second one. Also, I apologize for not splitting the code into more classes and for possibly not sticking to naming conventions. I appreciate your help, thank you very much!
I added a Circle class to your Ripples code. This allows the ActionListener to treat each circle independently.
I started the GUI with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
Here's the code.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Ripples extends JPanel implements ActionListener {
private List<Circle> circles;
public Ripples() {
circles = new ArrayList<>();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent event) {
circles.add(new Circle(event.getPoint()));
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.CYAN);
g2.setStroke(new BasicStroke(3f));
for (Circle circle : circles) {
Point p = circle.getCenter();
int radius = circle.getRadius();
g2.drawOval(p.x - radius, p.y - radius,
2 * radius, 2 * radius);
}
}
#Override
public void actionPerformed(ActionEvent evt) {
for (Circle circle : circles) {
circle.incrementRadius();
}
repaint();
}
public static void createGUI() {
JFrame f = new JFrame("Ripples");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Ripples p = new Ripples();
p.setBackground(Color.WHITE);
p.setPreferredSize(new Dimension(500, 500));
f.setContentPane(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
Timer t = new Timer(20, p);
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createGUI();
}
});
}
public class Circle {
private int radius;
private final Point center;
public Circle(Point center) {
this.center = center;
this.radius = 10;
}
public void incrementRadius() {
radius += 1;
radius = (radius > 200) ? 10 : radius;
}
public int getRadius() {
return radius;
}
public Point getCenter() {
return center;
}
}
}
Edited to add:
I reworked the Ripples class code to separate the concerns. I created a DrawingPanel class to hold the drawing panel, a RipplesListener class to hold the MouseAdapter code, an Animation class to hold the Runnable that runs the animation of the circles, a RipplesModel class to hold the List of Circle instances, and finally, the Circle class.
I could have used a Swing Timer for the animation, but I'm more familiar with creating and running my own animation thread.
Yes, this code is more complicated than the original example. The coding style used here can be carried into larger, more complex Swing GUI development.
Here's the revised code. I hope it's a better example.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
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.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Ripples implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Ripples());
}
private Animation animation;
private DrawingPanel drawingPanel;
private RipplesModel model;
public Ripples() {
model = new RipplesModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Ripples");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent event) {
stopAnimation();
frame.dispose();
System.exit(0);
}
});
drawingPanel = new DrawingPanel(model);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(this, model);
new Thread(animation).start();
}
public void repaint() {
drawingPanel.repaint();
}
private void stopAnimation() {
if (animation != null) {
animation.setRunning(false);
}
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private RipplesModel model;
public DrawingPanel(RipplesModel model) {
this.model = model;
setBackground(Color.WHITE);
setPreferredSize(new Dimension(500, 500));
addMouseListener(new RipplesListener(model));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(3f));
List<Circle> circles = model.getCircles();
for (Circle circle : circles) {
Point p = circle.getCenter();
int radius = circle.getRadius();
g2.setColor(circle.getColor());
g2.drawOval(p.x - radius, p.y - radius,
2 * radius, 2 * radius);
}
}
}
public class RipplesListener extends MouseAdapter {
private RipplesModel model;
public RipplesListener(RipplesModel model) {
this.model = model;
}
#Override
public void mousePressed(MouseEvent event) {
model.addCircle(new Circle(event.getPoint(),
createColor()));
}
private Color createColor() {
Random random = new Random();
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);
return new Color(r, g, b);
}
}
public class Animation implements Runnable {
private volatile boolean running;
private Ripples frame;
private RipplesModel model;
public Animation(Ripples frame, RipplesModel model) {
this.frame = frame;
this.model = model;
this.running = true;
}
#Override
public void run() {
while (running) {
sleep(20L);
incrementRadius();
}
}
private void incrementRadius() {
List<Circle> circles = model.getCircles();
for (Circle circle : circles) {
circle.incrementRadius();
}
repaint();
}
private void sleep(long delay) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.repaint();
}
});
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
}
public class RipplesModel {
private List<Circle> circles;
public RipplesModel() {
this.circles = new ArrayList<>();
}
public void addCircle(Circle circle) {
this.circles.add(circle);
}
public List<Circle> getCircles() {
return circles;
}
}
public class Circle {
private int radius;
private final Color color;
private final Point center;
public Circle(Point center, Color color) {
this.center = center;
this.color = color;
this.radius = 10;
}
public void incrementRadius() {
radius = (++radius > 200) ? 10 : radius;
}
public Color getColor() {
return color;
}
public int getRadius() {
return radius;
}
public Point getCenter() {
return center;
}
}
}
I'd prefer a solution/improvement/hint for the upper code
The second code is better because it uses:
a custom class to contain information about the object to be painted
an ArrayList to contain the objects to be painted
a Timer for the animation.
because I think its structured better than the second one.
Not a good reason. Use the code that provides the functionality that you require.
Restructure the code yourself. That is part of the learning experience.
Issues with the second code:
It doesn't compile. Why post code that doesn't compile? This implies you haven't even tested it.
the initial radius is assigned when the Position object is created.
When the Timer fires you need to iterate through the ArrayList to update the radius of each Position object.
The radius of the Position object is used in the painting code.
As an added change, maybe call the Position class Ripple. Then you can add another custom property for the Color of the ripple. Then when you add the Ripple to the ArrayList you randomly generate a Color. Then in the painting method you use the Color property of the Ripple class. This is how you make objects and painting more flexible and dynamic.
What piccolo example below should actually do? I draws nothing for me. And paint method never called:
package test.piccolo;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PPaintContext;
import edu.umd.cs.piccolox.PFrame;
public class SimpleEllipseNode extends PNode {
private Ellipse2D ellipse;
// This nodes uses an internal Ellipse2D to define its shape.
public Ellipse2D getEllipse() {
if (ellipse == null)
ellipse = new Ellipse2D.Double();
return ellipse;
}
// This method is important to override so that the geometry of
// the ellipse stays consistent with the bounds geometry.
public boolean setBounds(double x, double y, double width, double height) {
if (super.setBounds(x, y, width, height)) {
ellipse.setFrame(x, y, width, height);
return true;
}
return false;
}
// Non rectangular subclasses need to override this method so
// that they will be picked correctly and will receive the
// correct mouse events.
public boolean intersects(Rectangle2D aBounds) {
return getEllipse().intersects(aBounds);
}
// Nodes that override the visual representation of their super
// class need to override a paint method.
public void paint(PPaintContext aPaintContext) {
Graphics2D g2 = aPaintContext.getGraphics();
g2.setPaint(getPaint());
g2.fill(getEllipse());
}
public static void main(String[] args) {
PFrame frame = new PFrame() {
#Override
public void initialize() {
PNode aNode = new SimpleEllipseNode();
//PNode aNode = new PText("Hello World!");
// Add the node to the canvas layer so that it
// will be displayed on the screen.
getCanvas().getLayer().addChild(aNode);
}
};
}
}
The ellipse is not painted probably because the bounds of SimpleEllipseNode are empty. Add these lines to initialize() :
aNode.setPaint(Color.RED);
aNode.setBounds(0, 0, 100, 100);
And it should paint a red ellipse. There is an NPE in setBounds() since the ellipse is not initialized yet. This can be fixed by adding getEllipse(); as a first line in setBounds().
Not sure what is the reason behind overriding paint() and setBounds(), as usually you can get away easily by using compositional nodes. For example:
import java.awt.Color;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolox.PFrame;
public class SimpleEllipseNode {
public static void main(final String[] args) {
PFrame frame = new PFrame() {
#Override
public void initialize() {
final PPath circle = PPath.createEllipse(0, 0, 100, 100);
circle.setPaint(Color.RED);
getCanvas().getLayer().addChild(circle);
}
};
}
}
When I run the below code I get two of the same error: non-static method cannot be referenced from static context. The two offending lines are:
gladiator[a] = new Gladiator();
graphic.startUpdate();
If I change the Gladiator class to static, that error goes away, but won't that make it so individual Gladiators can't have their own independent variables?
The startUpdate() method won't let me change it to static without throwing an error which says "modifier static is only allowed in constant variable declaration". Clearly I am using my update timer in the wrong place. Any ideas?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.util.ArrayList;
public class Test extends JPanel{
abstract class graphic {
public Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public int[] location = new int[] {screenSize.width/2,screenSize.height/2};
void startUpdate() {
new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}).start();
}
}
public class Gladiator extends graphic {
void draw(final Graphics g) {
g.setColor(Color.green);
g.fillArc(location[0], location[1], 100, 100, 45, 90);
g.setColor(Color.black);
g.fillArc((location[0]+50-10),(location[1]+50-10), 20, 20, 0, 360);
}
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
}
public void setLocation(int x, int y){
//this.location[0] = x;
//this.location[1] = y;
}
public static void main(String[] args){
Gladiator[] gladiator = new Gladiator[2];
ArrayList<Gladiator> gladiatorList = new ArrayList<Gladiator>();
JFrame jf=new JFrame();
jf.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
jf.setPreferredSize(Toolkit.getDefaultToolkit().getScreenSize());
jf.add(new Test());
jf.pack();
jf.setVisible(true);
for (int a =0; a < 2; a++) {
gladiator[a] = new Gladiator();
gladiatorList.add(gladiator[a]);
System.out.println("add "+a);
}
graphic.startUpdate();
}
}
There is a lot of problems, the errors you get are just hiding the rest.
But to get you started:
The abstract class graphic should probably extend JComponent. Instead of the draw method you should override paintComponent. Both graphic and Gladiator should be in its own file, not inside Test.
Location array could be called int x and int y respectively to make it easier in the future.
For more info on repaint see here.
I am trying to write a program that allows the user to draw a polygon by drawing a line every time they click within the frame. On the first click, a small square should be drawn. Every click following, a line should be drawn from where the endpoint of the last line is to where the user has clicked. Once the user clicks within the square that was made originally, the polygon will be completed and the square will disappear. My code is as follows; it runs but it does not operate correctly.
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.util.ArrayList;
import javax.swing.JFrame;
public class DrawPolygonComponent {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setSize(300, 400);
frame.setTitle("Draw a Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final ArrayList<Point2D.Double> path = new ArrayList();
//addRectangle(frame, 10, 10);
//
class ClickListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent me) {
if (path.isEmpty()) {
System.out.println("First");
addRectangle(frame, me.getX(),me.getY());
path.add(new Point2D.Double(me.getX(),me.getY()));
} else {
System.out.println("Second");
Point2D.Double prev = path.get(path.size()-1);
addLine(frame, (int) prev.x, (int) prev.y,me.getX(),me.getY());
path.add(new Point2D.Double(me.getX(),me.getY()));
frame.repaint();
}
}
public void mousePressed(MouseEvent me) {}
public void mouseReleased(MouseEvent me) {}
public void mouseEntered(MouseEvent me) {}
public void mouseExited(MouseEvent me) {}
}
MouseListener listener = new ClickListener();
frame.addMouseListener (listener);
frame.setVisible (true);
}
public static void addRectangle(JFrame frame, int x , int y) {
RectangleComponent r = new RectangleComponent(x, y, 10, 10);
frame.add(r);
}
public static void addLine(JFrame frame, int x1, int y1, int x2, int y2) {
LineComponent line = new LineComponent(x1, y1, x2, y2);
frame.add(line);
}
}
/////////other
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
public class LineComponent extends JComponent {
private int px;
private int py;
private int x;
private int y;
public LineComponent(int px, int py, int x, int y){
this.px=px;
this.py=py;
this.x=x;
this.y=y;
}
#Override
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D)g;
g2.drawLine(px,py,x,y);
}
}
////////other
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
public class RectangleComponent extends JComponent {
private Rectangle box;
public RectangleComponent(int x,int y, int l, int w){
box = new Rectangle(x,y,l,w);
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D)g;
g2.fill(box);
g2.draw(box);
}
}
You've attached your mouse listener to the frame, but not provided any means for the frame to paint your paths...
The component you have setup to "apparently" paint the polygon has not been added to the frame.
Instead.
Create a custom component, using something like JPanel. Attach the mouse listener to this component. Override it's paintComponent method. When a mouse event occurs (that should generate a new line), call repaint to request an update to the component.
Within the paintComponent method, re-draw all the lines.
Take a look at Java Applet Polygon array and How can I draw a polygon using path2d and see if a point is within it's area? and drawPolygon keeps drawing lines from starting (mousePressed) location to current (mouseDragged) location for some conceptional ideas
Ps You may also like to check out Performing Custom Painting