6

I have a frame with an image covering it, And i want that every time some one clicks on a different object in the image , it will act as a button and do something.
The problem is , Is that those objects are not simple shapes, so i was thinking about drawing my own invisible buttons in the shape of those objects.

Is that possible? or what would be a better way to accomplish such a thing?

-thank you

Dani Gilboa
  • 599
  • 4
  • 13
  • 30
  • 1
    Will be following this. I attempting something very similar on android a while back and ended up settling for just invisible buttons over the odd shapes :) – sealz Aug 08 '13 at 17:01
  • it will act as a button and do something. is for example something like as virtual remote for TV – mKorbel Aug 08 '13 at 17:19
  • 1
    See also the approach shown [here](http://stackoverflow.com/q/10861852/230513). – trashgod Aug 08 '13 at 17:30

4 Answers4

3

Concept: invisible buttons with circular and polygonal dispatch areas

First, you'll want a class defining an invisible button, extending javax.swing.AbstractButton so that it's still a fully functional button that you can add listeners to.

public abstract class InvisibleButton extends AbstractButton {

    public abstract boolean contains(Point point);

    @Override
    public boolean isVisible() {
        return false;
    }
}

Then, of course, you'll want implementations of that class. Here's two examples: one using a polygon for complex shapes, one using a circle.

public class PolygonalButton extends InvisibleButton {

    private Polygon area = null;

    public PolygonalButton(Polygon area) {
        this.area = area;
    }

    @Override
    public boolean contains(Point point) {
        return area.contains(point);
    }
}

public class CircularButton extends InvisibleButton {

    private int x;
    private int y;
    private double radius;

    public CircularButton(int x, int y, double radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public boolean contains(Point point) {
        double dx = x - point.x;
        double dy = y - point.y;
        return Math.sqrt(dx * dx + dy * dy) <= radius;
    }
}

Finally, you'll need to implement a container that handles all of these buttons, but you should use a panel instead of a frame. Rather than hook each individual listener, you can simply override the frame's event processors and pass them to the necessary buttons.

public class InvisibleButtonImagePanel extends JPanel {

    private BufferedImage image = null;
    private List<InvisibleButton> buttons = new ArrayList<>();

    public InvisibleButtonImagePanel(BufferedImage image) {
        this.image = image;
    }

    public void add(InvisibleButton button) {
        buttons.add(button);
    }

    public void remove(InvisibleButton button) {
        buttons.remove(button);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(image.getWidth(), image.getHeight());
    }

    @Override
    public void processKeyEvent(KeyEvent event) {
        for (InvisibleButton button : buttons) {
            if (button.isFocusOwner()) {
                button.dispatchEvent(event);
            }
        }
        super.processKeyEvent(event);
    }

    @Override
    public void processMouseEvent(MouseEvent event) {
        for (InvisibleButton button : buttons) {
            if (button.contains(event.getPoint())) {
                button.dispatchEvent(event);
            }
        }
        super.processMouseEvent(event);
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.drawImage(image, 0, 0, null);
        super.paintComponent(g);
    }
}

You'll probably want to rename the panel to something less bulky, and maybe implement your own advanced image code, but that's the basic idea behind it.

FThompson
  • 28,352
  • 13
  • 60
  • 93
2

Can you track the users mouse position via Coordinates?

 PointerInfo a = MouseInfo.getPointerInfo();
        Point b  = a.getLocation();
        int x = (int)b.getX();
        int y = (int)b.getY();

Now put a ActionPerformed Method and test if x and y = e.g. 200, 300 do this. That way when a user clicks on a spot (your aiming for area) it will redirect him to a method.

Jebathon
  • 4,310
  • 14
  • 57
  • 108
2

Define an interface for a region like:

//represents any clickable area.
public interface IButton{
    boolean contains(int x, int y);
}

Then, if you want a circular area to be clickable, define a class which checks if a x,y coordinate is within some distance of a position.

public class CircleButton implements IButton{
    Point center;
    double radius;   
    public CircleButton(int x, int y, double radius){
        this.center = new Point(x,y);
        this.radius = radius;
    }
    //check if x,y coords are within a radius 
    //from the center of this circle button
    public boolean contains(int x, int y){
        double dx = x-center.x;
        double dy = y-center.y;
        return (Math.sqrt(dx*dx+dy*dy) <= radius);
    }
}

Create a list of IButtons. You'll iterate over these to see if a user clicked on one of your invisible buttons.

List<IButton> buttons = new List<IButton>();
buttons.add(new CircleButton(100,100,200);

Then each time someone clicks on your frame iterate over your invisible buttons using the location of that mouse click.

public void mouseReleased(MouseEvent e){
    for(IButton b : buttons){
       if(b.contains(evt.getX(),e.getY()){
           //do something depending on what button was clicked.
       }
    }
} 

You can easily see how you could define invisible rectangle buttons like this, or even irregular polygon shapes. You'd just need to implement the contains method correctly.

merfanzo
  • 33
  • 4
William Morrison
  • 10,953
  • 2
  • 31
  • 48
2

And i want that every time some one clicks on a different object in the image , it will act as a button and do something.

You can create an ArrayList of Shape objects. Then you add a MouseListener to the component containing the image. Whenever a mousePressed event is generated you loop through the ArrayList and use the containts(...) method of the component to determine if the user click on a Shapes corrdinates.

Check out Playing With Shapes. The code won't help with your immediate problem, but it will give you an idea of the type of Shapes that you can define.

camickr
  • 321,443
  • 19
  • 166
  • 288