I need to make an image map using Swing that displays a background image, and then when the mouse hovers over (or clicks) specific hotspots, I need to pop up a 'zoomed-in' image and have it display.
I was thinking of extending JPanel to include an image reference and have that drawn thru the paintComponent(g) method. This part I have done so far, and here's the code:
public class ImagePanel extends JPanel
{
private static final long serialVersionUID = 1L;
private Image image;
public ImagePanel(Image image)
{
setImage(image);
}
public void setImage(Image newImage)
{
image = newImage;
}
#Override
public void paintComponent(Graphics g)
{
Dimension size = getSize();
g.drawImage(image, 0, 0, size.width, size.height, this);
}
Could anyone recommend how I might listen for / respond to mouse clicks over defined hot-spots? Could someone additionally recommend a method for displaying the pop-ups? My gut reaction was to extend JPopupMenu to have it display an image, similar to the above code.
Thanks for any help!
To listen to the mouse clicks implement the MouseListener interface, and add it to your panel. Then when the click is recieved you can use a JPopupMenu as you suggested, or you could even use a glass pane to show the zoomed in image.
I'm guessing you want to achieve something similar to this post by Joshua Marinacci, he has also posted the source here, I would take a look at that.
I would probably:
create some instance of Shape that represents each of your hotspots (could be a plain boring old Rectangle, or see GeneralPath if you need to create fancy shapes)
register a MouseListener which iterates through each of the Shapes and calls its contains() method to see if the clicked coordinate is inside the hotspot in question
Related
I have an image of a map. When the user clicks a button, I want to put a red square box as a highlight on a certain location of the map like this:
How do I highlight a portion like this?
Currently, I am achieving this by creating a new image which has this area highlight and loading that image when the user clicks on the button. However in this case I'll have to develop 66 images and load one for each button.
Extend the JLabel to do custom painting.
You would keep an ArrayList of Rectangles that you want to paint. When you click a button you add a Rectangle to the ArrayList. Then in the paintComponent(...) method you iterate through the ArrayList to paint each Rectangle.
So the basic changes to the extend the JLabel would be:
private ArrayList<Rectangle> rectangles = new ArrayList<Rectangle>();
...
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Rectangle r: rectangles)
{
g.setColor( Color.RED );
g.drawRect(...);
}
}
public void addRectangle(Rectangle r)
{
rectangles.add( r );
}
For a working example of this approach check out the DrawOnComponent example found in Custom Painting Approaches.
Another option might be to use a JLayer to paint the Rectangles on the JLabel. Read the section from the Swing tutorial on How to Decorate Component With the JLayer Class for some working examples.
Either way you need to do custom painting. Try both approaches to see which you prefer.
I would like to have a JPanel transparent or fully invisible (not like panel.setVisible(false) but more something like opacity = 0.0f) which will then be put into a JLayeredPane, to make the bottom layer visible.
You know ask why. I need this to use the build in Layouts like BorderLayout and so on because I dont want to make my own layout and so on.
What I then tried was first of all the Implementation of a transparent JPanel which was more or less easy by doing just this:
public class ClearPanel extends JPanel {
private static final long serialVersionUID = 706731555521208974L;
private float opacity = 0.0f;
private Shape shape = null;
public ClearPanel() {
setBackground(new Color(0,0,0,0));
setOpaque(false);
}
public ClearPanel(float opacity) {
setOpaque(false);
setOpacity(opacity);
}
public ClearPanel(float opacity, Shape shape) {
setOpaque(false);
setOpacity(opacity);
setShape(shape);
}
// Here usually are some random getters and setters
#Override
public void paintComponent(Graphics go) {
Graphics2D g = (Graphics2D) go;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if(shape == null) shape = g.getClipBounds();
g.setColor(new Color(getBackground().getRed(), getBackground().getGreen(), getBackground().getBlue(), 255*opacity));
g.fill(shape);
}
}
So what this does is usually creating a more or less transparent Version of the JPanel. And it also works if don't use a layout but fixed Positions and Sizes but when you program something bigger it would be way easier to use available solutions.
The problem comes now:
When I try to use this in a JLayeredPane with the possibility to see the bottom layer, nothing happens, although it definetly should I need this because I want a bottom panel with a background Image and then I want some stuff ABOVE that panel. And I thought, ok I know Layers from Gimp, maybe Java offers this, so thats why I think that JLayeredPane could help.
Do you know a possibility to make the bottomlayers shine trough an other layer although you use a fixed layout or do you think, I will need to make my own layout or use public void paint(Graphics g) of JFrame to paint them step by step ?
Thanks in advance
PS: Sry for my English but after one day of try and error I can't write anything "perfect" any more...
I'm trying to make a simple application to draw shapes to a panel in a jFrame. The GUI is a NetBeans generated jFrame. The application has three panels. the first two hold button groups to select the shape and a color. There's a button to draw the shape to the third panel once the selections are made.
What the GUI looks like
Unfortunately I'm having no love and can't make it work. For now I just want to get the button to draw a shape then I'll add the button functionality. Here's the code I have so far.
private void btnDrawShapeActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Draw shape = new Draw();
pnlDrawPad.add(shape); //pnlDrawPad is the name of the jPanel
shape.drawing();
}
import javax.swing.*;
import java.awt.*;
public class Draw extends JPanel{
public Draw(){
super();
}
public void drawing(){
repaint();
}
//#Override <-- gives error "method does not override or implement a method from a supertype"
public void paintCompnent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.drawString("Hello World!",40,30);
}
}
When I click the "Draw Shape" button nothing happens. I've been searching for a couple days now and am not finding the answer. Actually, its making me more confused as to what to do.
How to deal with this?
Don't keep creating new panels for each shape.
Instead you need a single panel with an ArrayList of objects you want to paint. Then you customize the paintComponent(...) method of the panel to iterate through all the objects in the panel and paint them.
So you button will just add another object to the ArrayList and then invoke repaint() on the panel.
Check out Custom Painting Approaches. The DrawOnComponent example shows how to paint a Rectangle objects from an ArrayList to get you started.
I found that with NetBeans you don't need to Override the paintComponents() method. Since all the GUI stuff is done when you design it, you merely need to add the drawings to the panel or what ever you need to do. I found you that by declaring and instantiating a 2D graphics object then drawing it to the place you want.
private void btnDrawShapeActionPerformed(java.awt.event.ActionEvent evt) {
int centerX;
int centerY;
Graphics2D drawFx = (Graphics2D)pnlDrawPad.getGraphics();
drawFx.setColor(Color.ORANGE);
centerX = (int)(458*Math.random());
centerY = (int)(440*Math.random());
drawFx.fillOval(centerX-50, centerY-50, 100, 100);
drawFx.dispose();
}
Worked like a charm so now every time I push the draw shape button it draws a shape to the panel in a different location every time. I've since put in the radio button functionality to change shape color and fill and added a button to clear the screen.
Not sure if this is the ideal way to do it but for this stupid simple little program it works well enough and I'm done stressing over it.
I have a little issue with my GUI in NetBeans. I draw images (dots) when a user clics in a JPanel at the mouse clic location. This part works just fine. I store each image locations in two different ArrayList that contains the X location and Y location. Now what I want to do is to delete the latest image drawn in the Panel after a button is clicked. So what I did is remove the last index of both ArrayList, and then call repaint() to draw all the images from the locations in both X and Y ArrayList (code below).
What is weird is that I need to resize the GUI (put it in full screen or just change its' size) in order for the drawn images to show up again in the JPanel otherwise, the panel remains empty.
Here's the parts of code that are affected :
public void paint(Graphics g) {
super.paint(g);
for(int i=0;i<=listePointsX.size()-1;i++) {
try{
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("/myimage.png"));
Graphics graphe = jPanel1.getGraphics();
graphe.setColor(Color.BLACK);
graphe.drawImage(icon, this.listePointsX.get(i),this.listePointsY.get(i), rootPane);
}catch(Exception e1){
}
}
private void jButtonUndoActionPerformed(java.awt.event.ActionEvent evt) {
if(listePointsX.size()>0){
int lastObject= listePointsX.size();
listePointsX.remove(lastObject-1);
listePointsY.remove(lastObject-1);
jPanel1.repaint();
}
else{
}
}
Any idea what I need to do to some kind of "refresh" the whole thing? Am I doing something wrong? Tried searching about that but did not find anyting...
Graphics graphe = jPanel1.getGraphics(); is NOT how painting should work, instead, you should have overriden the panel's paintComponent method and painted the points within in.
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing
Instead, your panel should be doing ALL the work, managing the points in the ArrayList and painting them. You parent component "might" have the ability to add or remove points if that meets your design requirements, but the core responsibility remains with the panel.
Avoid performing any long running or block operations within the paint methods, they should run as fast as possible. Since the image never changes, you should simply load once (either when the class is constructed or when you first need the image) and keep using the same reference.
Alright it worked just fine now. I had to do it the way you told me up here. I created a new class that extends jPanel (below). Then in my main Form, had to create an object of this class. Whenever a user makes a click, it will call this Drawing class object and add an item to the ArrayList (this object manages everything regarding created points... It looks like this :
public class MyDrawingClass extends JPanel {
ArrayList<Integer> arrayListPointX = new ArrayList<>();
ArrayList<Integer> arrayListPointY = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
try{
BufferedImage icon = ImageIO.read(getClass().getResourceAsStream("/images/dot.png"));
g.setColor(Color.BLACK);
if(arrayListPointX.size()<=0){
...
}
else{
for(int i=0;i<listePointsX.size();i++){
g.setColor(Color.BLACK);
g.drawImage(icon, listePointsX.get(i), listePointsY.get(i), rootPane);
}
}
}catch(Exception e){
...
}
}
So if I want to "undo", let's say my object of "MyDrawingClass" is called "draw". I can do : draw.arrayListPointX.remove(draw.arrayListPointX.size()-1); and call repaint(); to display remaining points.
Thanks for your tips appreciate it ! :)
I'm a java beginner, and my java skills suck on this. In my jframe, i have this jpanel(below) which draws the image. I want the image changed whenever a radiobutton(in another frame) fires an ItemEvent and click something like a save button that'll fire an ActionEvent. i intend this to be of use for a character-selecting interface, as in MMORPGs, etc. Image panel below is intended for weapon-selecting interface in my game.
class Weapons extends JPanel {
private Image weaponimage = weapon2.getImage();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(weaponimage, 0, 0, this);
}
}
weapon2 is an ImageIcon. thanks in advance.
The simplest approach is not to have a custom panel at all, but use JLabel as the image container. Then you can change the image with:
label.setIcon(new ImageIcon(theNewWeaponImage));
Edit: since it looks like you already have Icons, you can use those directly:
label.setIcon(theNewWeaponIcon);