复杂的按钮布局

时间:2012-01-19 20:39:20

标签: java swing jbutton

我想要实现一个看起来像button的按钮:

Round button w/metallic border

但外圈分为四个部分,因此我有四个图像来制作它的按钮加上中间按钮的中间图像。对于四个按钮的划分,想象一下有十字或X.如何布置按钮以实现这样的组件?

我尝试使用BorderLayoutGridBagLayout,但由于摆动按钮的矩形形状而产生的空间在每个按钮图像之间产生了太大的空间,因此它看起来不太好。我现在正在考虑JLayeredPane添加按钮,但我认为会出现问题,因为如果其他按钮位于按钮上,则按钮的某些部分将无法点击。

是否可以用我想要的功能(5个按钮)来实现这种形状的组件?

1 个答案:

答案 0 :(得分:1)

我意识到这个问题很久以前就被问过了,但我会创建一个JComponent,然后呈现该按钮然后检查以查看图像的哪个部分被点击。像这样:

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;
    }
}

简而言之,按钮绘制为您链接的图像。然后,当您单击图像时,它会检查您单击图像的哪个部分。这是通过创建两个与图像上的椭圆相匹配的椭圆来完成的。如果您单击的点位于较小的椭圆内,则会通知中间的ActionListeners。如果它不在那里,但它在较大的椭圆内,那么我们知道点击是在环中。然后我们检查椭圆的哪一侧被点击并通知适当的动作听众。

此外,请注意:PERCENT_PADDING_MIDDLEPERCENT_PADDING_EDGE特定于您链接的图片,并假设图片周围的填充相等。这是通过获取图像上的填充像素数并将其除以图像的宽度来确定的。