Button inside swing Rectangle2D.double - java

We have a swing application which displays a lot of rectangles. We use Rectangle2D.double class to draw the rectangles on a JPanel.
My requirement is this. Upon clicking the rectangle, I have to pick an image from the local filesystem and show it in a popup window or a frame.
My question is how can I provide a hyperlink or button inside that Rectangle2D.double rectangle.
Please let me know.
Thanks
-Jad.

I hope this is what you mean:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class RectButton extends JPanel {
Rectangle2D.Double rect;
public RectButton() {
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
Point point = e.getPoint();
System.out.println(checkRectArea(point));
// Do whatever else you want here.
}
});
}
public boolean checkRectArea(Point point) {
return rect.contains(point);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
rect = new Rectangle2D.Double(10, 10, 50, 50);
g2.draw(rect);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
RectButton r = new RectButton();
frame.add(r);
frame.setSize(new Dimension(300, 300));
frame.setVisible(true);
}
}
This program draws a rectangle and print true if you clicked inside the rectangle, false otherwise.

You want to put a MouseListener on the panel, that will catch all clicks anywhere on the panel. You can then get the location of the click from the event and determine which rectangle the click happened in, and then call up the code appropriate for that event and location.

Related

How to update (more than one?) JPanel on mouse click?

I have a JFrame, comprised of three JPanel containers, each panel has a filled-in circle (for example, red, white blue).
What I'd like to be able to do is update the color of the filled-in circle of that panel (make it darker with Color.RED.darker, for example) when that particular panel is clicked
I can't use an ActionListener, since panels aren't components but containers. I started out using MouseListener, but have now updated this to MouseAdapter. I am able to determine which JPanel was clicked - for testing purposes I'm printing out which panel was clicked to the console (for simplicity purposes I added a name to each panel).
EDIT: I got this mostly working - I can now repaint() the Jpanel that was clicked, making that cricle color darker, using the suggestion below of creating a setCircleColor (color) method, which calls repaint().This redraws the circle in that panel, using a darker color.
However, what I really also need to do is make the other two (non-clicked) cirlces on the other panels to repaint() with lighter colors.
But I can't see an easy way to handle this - how can I manipulate the other Jpanels that I didn't click on?
Here's my code:
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class TrafficLight {
// sets up the frame, calls the circle panels and adds them to the frame
public static void setUpGui() {
JFrame frame = new JFrame("Traffic Lights");
frame.setSize(300, 900);
frame.setLayout(new GridLayout(3, 0));
DrawCirclePanel redCircle = new DrawCirclePanel(Color.RED);
DrawCirclePanel yellowCircle = new DrawCirclePanel(Color.YELLOW);
DrawCirclePanel greenCircle = new DrawCirclePanel(Color.GREEN);
redCircle.setName("redCircle");
yellowCircle.setName("yellowCircle");
greenCircle.setName("greenCircle");
CircleListener cl = new CircleListener();
redCircle.addMouseListener(cl);
yellowCircle.addMouseListener(cl);
greenCircle.addMouseListener(cl);
frame.add(redCircle);
frame.add(yellowCircle);
frame.add(greenCircle);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args) {
setUpGui();
}
}
// the DrawCirclePanel class creates a panel and
// draws a filled-in circle on the panel
class DrawCirclePanel extends JPanel {
Color c;
Border blackline = BorderFactory.createLineBorder(Color.black);
// constructor for panel, takes Color as argument
// so we know what color circle to make
public DrawCirclePanel(Color color) {
this.c = color;
this.setLayout(new GridLayout(1, 0));
this.setBorder(blackline);
}
// draws the circle in the panel
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xWidth = this.getParent().getWidth();
g.setColor(c);
g.fillOval(1, 1, xWidth-1, xWidth-1);
}
public void setCircleColor(Color color) {
this.c = color;
this.getGraphics().setColor(c);
this.repaint();
}
}
class CircleListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
DrawCirclePanel cPanel = (DrawCirclePanel) e.getSource();
System.out.println(cPanel);
String name = cPanel.getName();
if (name == "redCircle") {
cPanel.setCircleColor(Color.red.darker());
}
else if (name == "yellowCircle") {
cPanel.setCircleColor(Color.yellow.darker());
}
else {
cPanel.setCircleColor(Color.green.darker());
}
}
}
So the answer turned out to be fairly simple.
Declare the panels with the circles as static variables so that the MouseListener class can access all three of the panels.
As Andrew Thompson noted, set up a setCircleColor method in DrawCirclePanel that repaints the circle with a different color as needed.
In the MouseListener class, determine with panel was called, then repaint all three of the circles, passing in the correct color - darker() or brighter() - as needed.
Here's the final code
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
import java.awt.Color;
import java.awt.event.*;
import java.awt.event.MouseEvent;
public class TrafficLight {
static DrawCirclePanel redCircle;
static DrawCirclePanel yellowCircle;
static DrawCirclePanel greenCircle;
// sets up the frame, calls the circle panels and adds them to the frame
public static void setUpGui() {
JFrame frame = new JFrame("Traffic Lights");
frame.setSize(300, 900);
frame.setLayout(new GridLayout(3, 0));
redCircle = new DrawCirclePanel(Color.RED.brighter().brighter());
yellowCircle = new DrawCirclePanel(Color.YELLOW.darker().darker());
greenCircle = new DrawCirclePanel(Color.GREEN.darker().darker());
redCircle.setName("redCircle");
yellowCircle.setName("yellowCircle");
greenCircle.setName("greenCircle");
CircleListener cl = new CircleListener();
redCircle.addMouseListener(cl);
yellowCircle.addMouseListener(cl);
greenCircle.addMouseListener(cl);
frame.add(redCircle);
frame.add(yellowCircle);
frame.add(greenCircle);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String[] args) {
setUpGui();
}
}
// the DrawCirclePanel class creates a panel and
// draws a filled-in circle on the panel
class DrawCirclePanel extends JPanel {
Color c;
Border blackLine = BorderFactory.createLineBorder(Color.BLACK);
// constructor for panel, takes Color as argument
// so we know what color circle to make
public DrawCirclePanel(Color color) {
this.c = color;
this.setLayout(new GridLayout(1, 0));
this.setBorder(blackLine);
}
// draws the circle in the panel
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int xWidth = this.getParent().getWidth();
g.setColor(c);
g.fillOval(0, 0, xWidth, xWidth);
}
// changes the color and calls repaint after a mouseClicked event
public void setCircleColor(Color color) {
this.c = color;
this.getGraphics().setColor(c);
this.repaint();
}
}
//abstract adapter class for receiving mouse events
class CircleListener extends MouseAdapter {
// determine which panel was clicked; redraws as brighter circle
// redraws other circles with dimmer (darker) color
public void mouseClicked(MouseEvent e) {
DrawCirclePanel cPanel = (DrawCirclePanel) e.getSource();
String name = cPanel.getName();
if (name.equals("redCircle")) {
TrafficLight.redCircle.setCircleColor(Color.RED.brighter().brighter());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.darker().darker());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.darker().darker());
} else if (name.equals("yellowCircle")) {
TrafficLight.redCircle.setCircleColor(Color.RED.darker().darker());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.brighter().brighter());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.darker().darker());
} else {
TrafficLight.redCircle.setCircleColor(Color.RED.darker().darker());
TrafficLight.yellowCircle.setCircleColor(Color.YELLOW.darker().darker());
TrafficLight.greenCircle.setCircleColor(Color.GREEN.brighter().brighter());
}
}
}

JPanel shows only the first custom JComponent added

I wrote a program that draws triangles on the screen. However only the first triangle is shown. How can I make multiple custom JComponents visible?
I already tried to create something like a draw() method but then I can't perform any actions on this object like i. e. I would like the color of the triangle to change whenever I click on it. For this I would need a MouseListener but it won't work with the draw() method.
View.java file:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class View extends JPanel {
public View()
{
setPreferredSize(new Dimension(300, 300));
add(new Triangle(20, 50, Color.red)); //this one will react to mouseClicked
add(new Triangle(100, 200, Color.pink)); //this one doesn't appear
}
public static void main(String []args)
{
JFrame frame = new JFrame("Trianlge test");
frame.add(new View());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Triangle p3 = new Triangle(60, 120, Color.blue); //this one won't react to mouseClicked()
p3.draw(g);
}
}
Triangle.java file:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.GeneralPath;
import javax.swing.JComponent;
public class Triangle extends JComponent implements MouseListener{
private int x,y;
private Color c;
public Triangle(int x, int y, Color c)
{
this.x = x;
this.y = y;
this.c = c;
setPreferredSize(new Dimension(100, 100));
addMouseListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
GeneralPath path = new GeneralPath();
g2d.setColor(c);
path.moveTo(x, y);
path.lineTo(x, y);
path.lineTo(x+50, y);
path.lineTo(x, y-50);
path.closePath();
g2d.fill(path);
repaint();
}
public void draw(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
GeneralPath path = new GeneralPath();
g2d.setColor(c);
path.moveTo(x, y);
path.lineTo(x, y);
path.lineTo(x+50, y);
path.lineTo(x, y-50);
path.closePath();
g2d.fill(path);
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
c = Color.cyan;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
JFrame frame = new JFrame();
First of all that statement in your View class is completely unnecessary. You would not create a JFrame instance in the constructor of a component. Also your code never references the variable which is a good indication it is not needed.
However, the main problem is your concept of creating custom components is wrong:
setPreferredSize(new Dimension(100, 100));
You attempt to set the preferred size of the component.
add(new Triangle(100, 200, Color.pink)); //this one doesn't appear
But then you attempt to do you custom painting at (100, 200) which is outside the size of the component. So the painting logic clipped at the size of component so you don't see anything being painted.
Custom painting should be done relative to (0, 0) of the component, not relative to the parent component.
If you you want to randomly position components on the parent panel then you need to:
set the parent panel to use a null layout
set the location of each component you add to the panel
set the size of each component you add to the panel.
basically you need to take over the functionality of the layout manager.
Other problems with your current painting code:
Don't invoke repaint() in a painting method. This will essentially cause an infinite painting loop. If you need animation you use a Swing Timer to schedule the animation.
Don't invoke paintComponent(...) directly. Swing will invoke paintComponent() when a component needs to be repainted.
However, I would suggest that if you want to paint Shapes on a panel, Then you forget about creating custom components. Instead you keep an ArrayList of the Shapes you want to paint and then in the paintComponent() method of the panel you iterate through the ArrayList to paint each shape.
For an example of this approach take a look at the Draw On Component example found in Custom Painting Approaches.
Note:
If you really want to be able to handle mouse events then you need to use a Shape object to represent your shapes to do proper hit detection. If you just display your shape as a component, then the mouse hit will be detected if you click anywhere in the rectangular area of the component, not just the triangle part that you actually paint. The Shape class has a contains(...) method you can use to determine if you actually click in the Shape or not.
Check out Playing With Shapes for more information on this concept.
Set a border to Triangle components like this:
public Triangle(int x, int y, Color c)
{
this.x = x;
this.y = y;
this.c = c;
setPreferredSize(new Dimension(100, 100));
addMouseListener(this);
// Set this border to see the boundaries of this component.
// When you are done, you may remove this.
setBorder(BorderFactory.createLineBorder(Color.black));
}
Then you can better understand the bounds of the components.
Pink triangle is not visible because it is outside component's boundary.
p3 triangle does not react to mouse clicks because it is just a drawing. Only components react to mouse and other events.
Notice that components are rectangle in shape. So, the mouse listener you have added works anywhere on the component; not only on the area of triangle.
You are drawing triangles in two ways in this program.
1. By adding Triangle components. (Like "add(new Triangle(20, 50, Color.red));")
2. By drawing p3 in paintComponent() method.
From software designing perspective, better to stick to one approach. Otherwise it can be confusing and error prone.

what is Alternative of Paint and repaint function?

Is there any function which can be replaced with paint() and repaint()in java.
I have a scenario.
There is a triangle (Triangle 1). when user will click in triangle another triangle (Triangle 2) will appear and 1st (Triangle 1) will be removed from screen. (coded using JFrame, paint() and repaint())
I have achieved it so far. but problem is when I minimize or change size of window with mouse the output window, it just paint again Triangle 1 rather than Triangle 2. OR Clear the whole screen if i call g2d.clearRect(0, 0, 1000, 1000);
triangle.reset();
Note: These both functions are to remove previous triangle (Triangle 1).
Is there any function that state should not be changed on minimize or when window size changed ?
or can we override repaint() or anything helping according to scenario.
Here is the Working code. Execute it, Click in triangle then minimize and see again. You will get the problem more clearly.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Triangle_shape extends JFrame implements ActionListener {
public static JButton btnSubmit = new JButton("Submit");
public Triangle_shape() {
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLayout(new BorderLayout());
frame.add(new TrianglePanel(), BorderLayout.CENTER);
frame.add(btnSubmit, BorderLayout.PAGE_END);
frame.pack();
frame.repaint();
frame.setTitle("A Test Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
public static class TrianglePanel extends JPanel implements MouseListener {
private Polygon triangle, triangle2;
public TrianglePanel() {
//Create triangle
triangle = new Polygon();
triangle.addPoint(400, 550); //left
triangle.addPoint(600, 550); //right
triangle.addPoint(500, 350); //top
//Add mouse Listener
addMouseListener(this);
//Set size to make sure that the whole triangle is shown
setPreferredSize(new Dimension(300, 300));
}
/**
* Draws the triangle as this frame's painting
*/
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.draw(triangle);
}
//Required methods for MouseListener, though the only one you care about is click
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
/**
* Called whenever the mouse clicks. Could be replaced with setting the
* value of a JLabel, etc.
*/
public void mouseClicked(MouseEvent e) {
Graphics2D g2d = (Graphics2D) this.getGraphics();
Point p = e.getPoint();
if (triangle.contains(p)) {
System.out.println("1");
g2d.clearRect(0, 0, 1000, 1000);
triangle.reset();
g2d.setColor(Color.MAGENTA);
triangle2 = new Polygon();
triangle2.addPoint(600, 550); // left
triangle2.addPoint(700, 350); //top
triangle2.addPoint(800, 550); //right
g2d.draw(triangle2);
} else {
System.out.println("Triangle dont have point");
}
}
}
}
paint() and repaint() work fine, but you are not using them as they are designed to be used. The window system doesn't keep a persistent image of what a component looks like. It expects your component to be able to repaint its entire appearance on demand (such as when the window is resized or un-minimized).
If you grab a Graphics object with getGraphics() and draw something on the component, the stuff you draw will indeed be lost if/when the entire component needs to be repainted. So you should not do that, but instead make sure your paintComponent method has all the information it needs to completely repaint the component.
If you only want one triangle on the screen at once, do not create a separate variable triangle2. Just replace the one triangle you have by altering your mouse click handler as follows:
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
if (triangle.contains(p)) {
triangle = new Polygon();
triangle.addPoint(600, 550); // left
triangle.addPoint(700, 350); //top
triangle.addPoint(800, 550); //right
repaint();
} else {
System.out.println("Point not in triangle");
}
}
Your paintComponent method should call super.paintComponent to ensure the background is painted, but otherwise you do not need to change it:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.draw(triangle);
}
Alternatively if you are trying to keep multiple triangles on the screen, for example, to add a new one at every click, you should add them into a list of shapes which will be drawn whenever the component needs to be repainted:
private final List<Shape> shapes = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for (Shape s : shapes)
g2d.draw(s);
}
Then to control the set of shapes that is on screen, manipulate the contents of the list, and call repaint();.
For instance, to add a new shape to the ones on screen:
Polygon triangle = new Polygon();
triangle.addPoint(200, 300);
triangle.addPoint(200, 200);
triangle.addPoint(300, 200);
shapes.add(triangle);
repaint();
To remove all shapes from the screen:
shapes.clear();
repaint();
You should also make sure that you switch to the Swing thread at the start of the program, because it is not safe to interact with Swing components from the main thread. In main:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
.
.
.
frame.setVisible(true);
}
});
}
Your mouseClicked method should not create a new Triangle object. After resetting triangle just add new points to it. Also don't draw in this method but call repaint:
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
if (triangle.contains(p)) {
System.out.println("1");
triangle.reset(); // change the current triangle
triangle.addPoint(600, 550); // new left
triangle.addPoint(700, 350); // new top
triangle.addPoint(800, 550); // new right
repaint(); // force repainting
} else {
System.out.println("Triangle dont have point");
}
}
Now if you want many triangles you should have a collection of Polygons. Like this :
public static class TrianglePanel extends JPanel implements MouseListener {
private Vector<Polygon> triangles;
public TrianglePanel() {
n = 0;
// Create an empty collection
triangles = new Vector<Polygon>();
//Create first triangle
Polygon triangle = new Polygon();
triangle.addPoint(400, 550); //left
triangle.addPoint(600, 550); //right
triangle.addPoint(500, 350); //top
// Add the triangle to the collection
triangles.add(triangle);
//Add mouse Listener
addMouseListener(this);
//Set size to make sure that the whole triangle is shown
setPreferredSize(new Dimension(300, 300));
}
/**
* Draws the triangles as this frame's painting
*/
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Polygon p : triangles) // Draw all triangles in the collection
g2d.draw(p);
}
//Required methods for MouseListener, though the only one you care about is click
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
/**
* Called whenever the mouse clicks. Could be replaced with setting the
* value of a JLabel, etc.
*/
public void mouseClicked(MouseEvent e) {
Graphics2D g2d = (Graphics2D) this.getGraphics();
Point p = e.getPoint();
// Do what you want with p and the collection
// For example : remove all triangles that contain the point p
ListIterator<Polygon> li = triangles.listIterator();
while (li.hasNext()) {
if (li.next().containsâ„—) li.remove();
}
// Do what you want to update the list
// For example: Add a new triangle...
Polygon triangle = new Polygon();
triangle.addPoint(600+n, 550); // left
triangle.addPoint(700+n, 350); //top
triangle.addPoint(800+n, 550); //right
triangles.add(triangle); // add the new triangle to the list
n += 10; // next new triangle will be "moved" right
repaint();
}
private int n;
}

How to create a Rectangle with the default x and y values at the top-left corner of the content pane, not the screen

I'm adding a function in my game that takes a screenshot and saves it to a new image. I have no problems with the filepath or anything of that sort, but rather the Rectangle through which the screenshot is taken. If I make a new Rectangle, like such:
new Rectangle(0, 0, 500, 500);
then it creates a 500 x 500 Rectangle at the top left of the computer screen, not at the top left of the content pane. The content pane to which I am referring is much smaller than the screen and located in the center. Thanks for reading, any help would be appreciated.
For any component (which can be a JPanel, Container, Window, or basically anything), you can use this to get the Rectangle that will represent its bounds on the screen:
Rectangle getBoundsOnScreen( Component component ) {
Point topLeft = component.getLocationOnScreen();
Dimension size = component.getSize();
return new Rectangle( topLeft, size );
}
So if you wanted just the content pane of a JFrame:
getBoundsOnScreen( frame.getContentPane() );
For stuff like an entire JFrame, you can just do this:
frame.getBounds();
Look at the example below. I first create a RectangleComponent class, which extends the Rectangle class:
import javax.swing.*;
import java.awt.*;
public class RectangleComponent extends JComponent
{
Rectangle rectangle;
public RectangleComponent(int xspace,int yspace, int width, int height)
{
rectangle = new Rectangle(xspace, yspace, width, height);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.draw(rectangle);
}
}
Now we create a class that generates the main window, where you add the Rectangle component:
import javax.swing.*;
import java.awt.*;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
}
public static void main(String[] args) {
JFrame frame = new JFrame("Java Rulez");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100,100,600,600);
frame.getContentPane().setBackground(Color.YELLOW);
frame.add(new RectangleComponent(0, 0, 500, 500));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

Clicking on a drawn object

I've got a class called Shape which inherits from JPanel.
A number of sub-classes in turn extend the Shape classes, one for each type of shape.
Each shape has its own overriden paint() method, which draws the respective shape.
I would like to be able to click on any shape, and am trying to implement this logic for now. Please note that each shape has been added to an arrayList.
However, the contains statement always returns false, even when I have clearly clicked inside the shape.
Any ideas?
Never override paint() in JPanel rather paintComponent(..)
Im not quite sure I understand however I made a short example which I hope will help.
Basically it is a simple JFrame with a DrawingPanel (my own class which extends JPanel and the shapes are drawn on). This panel will create shapes (only 2 for testing) add them to an ArrayList and draw them to the JPanel via paintComponent(..) and a for loop, it also has a MouseAdapter to check for user mouseClicked(..) evnets on the JPanel. When a click is made we iterate through each Shape in the ArrayList and check whether the Shape contains the point or not, and if so prints its class name and uses instance of to check what type of Shape is clicked and prints appropriate message:
Output (after clicking both shapes):
Clicked a java.awt.geom.Rectangle2D$Double
Clicked a rectangle
Clicked a java.awt.geom.Ellipse2D$Double
Clicked a circle
ShapeClicker.java:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeClicker {
public ShapeClicker() {
JFrame frame = new JFrame();
frame.setTitle("Shape Clicker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
initComponents(frame);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//create frame and components on EDT
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ShapeClicker();
}
});
}
private void initComponents(JFrame frame) {
frame.add(new ShapePanel());
}
}
//custom panel
class ShapePanel extends JPanel {
private Shape rect = new Rectangle2D.Double(50, 100, 200, 100);
private Shape cirlce = new Ellipse2D.Double(260, 100, 100, 100);
private Dimension dim = new Dimension(450, 300);
private final ArrayList<Shape> shapes;
public ShapePanel() {
shapes = new ArrayList<>();
shapes.add(rect);
shapes.add(cirlce);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
for (Shape s : shapes) {
if (s.contains(me.getPoint())) {//check if mouse is clicked within shape
//we can either just print out the object class name
System.out.println("Clicked a "+s.getClass().getName());
//or check the shape class we are dealing with using instance of with nested if
if (s instanceof Rectangle2D) {
System.out.println("Clicked a rectangle");
} else if (s instanceof Ellipse2D) {
System.out.println("Clicked a circle");
}
}
}
}
});
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
for (Shape s : shapes) {
g2d.draw(s);
}
}
#Override
public Dimension getPreferredSize() {
return dim;
}
}
If you are implementing Shape you have to implement the contains method yourself. The default implementation for Shape always returns false.
If your Shape is bounded by curves that you know how to intersect (or determine if a point is on one or the other side), you can use the even-odd rule. Cast a ray from the point tested in any direction not parallel to a straight line. If the number of intersections is odd, the point is inside. If the number of intersections is even, the point is outside.
The built-in classes implement this method, so you can use/extend the Polygon, Ellipse2D.Double or RoundRectangle2D.Double class and have a filled polygon / ellipse / round rectangle that knows its inside.

Categories

Resources