Search code examples
javaswingjbutton

Java Swing drawing PlayButton for a basic music player


Maybe my case it's a simple confusion of ideas. How do draw a button like this using Shape?

enter image description here

I don't mind the rounded corners, heres my aproach for a round corner button.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;

import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicButtonUI;

public class PlayButtonUI extends BasicButtonUI{

    protected Shape shape;

    @Override
    protected void installDefaults(AbstractButton b) {
        super.installDefaults(b);
        b.setOpaque(false);//removes that annoying default background
    }

    @Override public void paint(Graphics g, JComponent c) {
        Graphics2D g2 = (Graphics2D)g;
        AbstractButton b = (AbstractButton) c;
        ButtonModel model = b.getModel();
        drawButtonShape(b);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);//smoth borders
        if(model.isArmed()) {
            g2.setColor(Color.RED);//color when button is pressed
        }else{
            g2.setColor(Color.GREEN);//default button color
        }
        g2.fill(shape);//aplying color
        super.paint(g2, c);
    }

    private void drawButtonShape(JComponent c) {
        //button shape is drawn here, 16 are the border radius
        shape = new RoundRectangle2D.Float(0, 0, c.getWidth()-1, c.getHeight()-1,16, 16);
    }

}

I don't really know how to draw anything at all, this class was a result from a chaotic example that i found somewhere, and then simplified by myself until it just worked, i left some comments for the important lines.

I've been looking for a while, and found this example in oracle docs. https://docs.oracle.com/javase/tutorial/2d/geometry/arbitrary.html

I don't really know how to convert Graphics2D to Shape, please tell me if i'm taking the wrong way.


Solution

  • So, I've spent the better of the day banging my head against this problem, trying to do a whole bunch of trig magic ... I can't even do simple card tricks :P

    Then I realised, there are other tricks I could do...

    Play me

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.geom.GeneralPath;
    import javax.swing.AbstractButton;
    import javax.swing.ButtonModel;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    import javax.swing.plaf.basic.BasicButtonUI;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    JButton btn = new JButton();
                    btn.setUI(new PlayButtonUI());
                    frame.add(btn);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class PlayButtonUI extends BasicButtonUI {
    
            @Override
            public Dimension getPreferredSize(JComponent c) {
                return new Dimension(200, 200);
            }
    
            @Override
            public void paint(Graphics g, JComponent c) {
                Graphics2D g2 = (Graphics2D) g;
                AbstractButton b = (AbstractButton) c;
                ButtonModel model = b.getModel();
                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//smoth borders
                if (model.isArmed()) {
                    g2.setColor(Color.BLACK);//color when button is pressed
                } else {
                    g2.setColor(Color.GRAY);//default button color
                }
    
                float thinkness = Math.min(c.getWidth(), c.getHeight()) * 0.1f;
    
                Shape shape = shapeFor(c, thinkness);
                g2.setStroke(new BasicStroke(thinkness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
                g2.fill(shape);//aplying color
                g2.draw(shape);
    
                super.paint(g2, c);
            }
    
            private Shape shapeFor(JComponent c, float thickness) {
                GeneralPath gp = new GeneralPath();
    
                double width = c.getWidth();
                double height = c.getHeight();
                double vPos = height / 2.0;
                double hPos = width - thickness;
    
                gp.moveTo(0.0 + thickness, 0.0 + thickness);
                gp.lineTo(hPos, vPos);
                gp.lineTo(0.0 + thickness, height - thickness);
                gp.closePath();
    
                return gp;
            }
    
        }
    }
    

    So, this is a slight "cheat". What this actually does is uses the properties of the Stroke to generate rounded edges, rather than trying to use curveTo or compound shapes

    Have a look at Stroking and Filling Graphics Primitives for more details