Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex button layout

I would like to realize a button that looks like this button:

Round button w/metallic border

But with the outer ring divide in four parts so that I have four images to make buttons of it plus the middle image for the middle button. For the division of the four buttons imagine there is a cross or an X. How can I layout the buttons to achieve a component like this?

I tried with BorderLayout and GridBagLayout, but spaces due to rectangular shape of swing buttons make too much space between each button image, so it doesn't look good. I'm now thinking of JLayeredPane to superimpose the buttons, but i think there will a problem as some part of buttons will not be clickable if another buttons is over it.

Is it possible to realise component of this shape with the functionnalities (5 buttons) i want ?

like image 705
xtrem06 Avatar asked Aug 04 '26 16:08

xtrem06


1 Answers

I realize this question was asked quite a while ago, but I would create a JComponent that renders as that button then checks to see which part of the image was clicked. Like so:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.SwingConstants;

public class FivePartCircleButton extends JComponent implements SwingConstants
{
    private static final float PERCENT_PADDING_MIDDLE = 0.1571428571428571f;
    private static final float PERCENT_PADDING_EDGE = 0.0535714285714286f;
    private static Image button;

    private List<ActionListener> topListeners = new LinkedList<ActionListener>();
    private List<ActionListener> rightListeners = new LinkedList<ActionListener>();
    private List<ActionListener> bottomListeners = new LinkedList<ActionListener>();
    private List<ActionListener> leftListeners = new LinkedList<ActionListener>();
    private List<ActionListener> middleListeners = new LinkedList<ActionListener>();
    private String actionCommand;

    public FivePartCircleButton()
    {
        try
        {
            if (button == null)
                button = Toolkit.getDefaultToolkit().createImage(new URL("http://mygimptutorial.com/preview/round-web20-button-with-metal-ring.jpg"));
        }
        catch (MalformedURLException e)    {e.printStackTrace();}

        this.setPreferredSize(new Dimension(280, 280));
        this.addMouseListener(mouseAdapter);
    }

    private MouseAdapter mouseAdapter = new MouseAdapter()
    {
        @Override
        public void mouseClicked(MouseEvent e)
        {
            Ellipse2D innerCircle = getShapeOfOval(PERCENT_PADDING_MIDDLE);
            Ellipse2D outerCircle = getShapeOfOval(PERCENT_PADDING_EDGE);

            if (innerCircle.contains(e.getPoint())) //clicked in the inner circle
                processClick(middleListeners);
            else if (outerCircle.contains(e.getPoint())) //clicked in the outer ring
            {            
                float lineFromTopLeftToBottomRight = e.getY() * ((float)getWidth() / (float)getHeight()); //if we split this button diagonally (top left to bottom right), then this is the x position of that line at this y point
                float lineFromTopRightToBottomLeft = getWidth() - lineFromTopLeftToBottomRight; // the same line as tlBrDividerX but mirrored

                if (e.getX() < lineFromTopLeftToBottomRight) //clicked on the bottom left half of the ring
                {
                    if (e.getX() < lineFromTopRightToBottomLeft) //clicked on the left quadrant of the ring
                        processClick(leftListeners);
                    else //clicked on the bottom quadrant of the ring
                        processClick(bottomListeners); 
                }
                else //clicked on the top right half of the ring
                {
                    if (e.getX() < lineFromTopRightToBottomLeft) //clicked on the top quadrant of the ring
                        processClick(topListeners);
                    else //clicked on the right quadrant of the ring
                        processClick(rightListeners);
                }
            }
        }
    };

    /**
     * Informs all of the listeners that an action has been performed
     * @param listeners - which set of listeners to inform
     */
    private void processClick(List<ActionListener> listeners)
    {
        for (ActionListener l : listeners)
            l.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
    }

    /**
     * @param listener - the listener to add
     * @param side - one of SwingConstants.TOP, SwingConstants.RIGHT, SwingConstants.BOTTOM, SwingConstants.LEFT, SwingConstants.CENTER,  
     */
    public void addActionListener(ActionListener listener, int side)
    {
        switch (side)
        {
        case TOP:
            topListeners.add(listener);
            break;
        case RIGHT:
            rightListeners.add(listener);
            break;
        case BOTTOM:
            bottomListeners.add(listener);
            break;
        case LEFT:
            leftListeners.add(listener);
            break;
        case CENTER:
            middleListeners.add(listener);
            break;
        }
    }

    /**
     * Creates an oval based on the size of this component with the given padding percentage
     * @param percentPadding
     * @return an oval with the given padding
     */
    private Ellipse2D getShapeOfOval(float percentPadding)
    {
        float x = getWidth() * percentPadding;
        float y = getHeight() * percentPadding;
        float w = getWidth() - x - x;
        float h = getHeight() - y - y;

        Ellipse2D circle = new Ellipse2D.Float(x, y, w, h);
        return circle;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        g.drawImage(button, 0, 0, this.getWidth(), this.getHeight(), this);
    }

    /**
     * Sets the action command for this button.
     * @param actionCommand - the action command for this button
     */
    public void setActionCommand(String actionCommand)
    {
        this.actionCommand = actionCommand;
    }
}

In a nutshell the button paints as the image you linked to. Then when you click on the image it checks to see what part of the image you clicked. This is done by creating two ovals that match the ovals on your image. If the point you clicked is inside the smaller oval then it notifies the middle ActionListeners. If it's not inside that, but it is inside the larger oval, then we know the click was in the ring. We then check to see which side of the oval was clicked and notify the appropriate action listeners.

Also, please note: PERCENT_PADDING_MIDDLE and PERCENT_PADDING_EDGE are specific to the image you linked to and assume equal padding around the image. This was determined by taking the number of padding pixels on the image and dividing it by the width of the image.

like image 159
Lunchbox Avatar answered Aug 07 '26 06:08

Lunchbox



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!