3

I would like to create something like the following in Swing:

enter image description here

The top part is relatively easy: I can just create a table and display it. What I'm having trouble with is the square plus and minus buttons at the bottom, which are designed to add a new item or remove the selected item respectively. In particular, I haven't been able to make the square shape because on Mac OS X and some other platforms, JButtons are rectangles with rounded corners and I can't find a way to change that. Also, I'm wanting to make sure it's a perfect square and without any space in between buttons.

How can this be accomplished in a cross-platform way on Swing?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Thunderforge
  • 19,637
  • 18
  • 83
  • 130

4 Answers4

5

JButtons are rectangles with rounded corners and I can't find a way to change that.

Change the Border:

button.setBorder( new LineBorder(Color.BLACK) );

Edit.

Another approach is to create your own icon from an existing button. Something like the following:

JButton button = new JButton("+");
Dimension size = button.getPreferredSize();
size.x += 6;
size.y += 6;
button.setPreferredSize(size);
Rectangle rectangle = new Rectangle(3, 3, size.x - 3, size.y - 3);
ScreenImage buttonImage = ScreenImage(button, rectangle);
ImageIcon icon = new ImageIcon(buttonImage);

JButton plus = new JButton(icon);
plus.setBorder( ... );

The above code should create an image of your button on any platform. I increased the preferred size to avoid taking an image of the border.

You will need to use the Screen Image class.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • That does indeed make it square, but the button loses the gradient. Any way to have the best of both worlds? – Thunderforge Apr 27 '13 at 22:03
  • 1
    Post your SSCCE showing the problem. A Border has nothing to do with the gradient of the entire button (that I'm aware of). – camickr Apr 27 '13 at 22:04
  • Creating a new button with `JButton button = new JButton("+");` and then adding that line makes a square border, but it removes the gradient that makes it look like a button. – Thunderforge Apr 27 '13 at 22:10
  • That is not a SSCCE. The Gradient is painted for me. I use JDK7 on Windows 7. If code doesn't work for you then post a SSCCE. Maybe it is a platform issue. Maybe you are doing something wrong. – camickr Apr 27 '13 at 22:17
  • I'm on Mac OS X, if that matters. And I don't have anything more in that part of the code right now other than those two lines, so that is my SSCCE. – Thunderforge Apr 27 '13 at 22:19
  • 1
    Then you have a platform issue. That is why you post a SSCCE with every question. You want help so make an effort to post your code so we can see what you are doing. I don't know how Mac works so I can't offer any more help. This is a custom painting problem that can't be solved in a generic manner. If you don't like the way the button is button then you will need to do custom painting. – camickr Apr 27 '13 at 22:22
  • [Here](http://www.javarichclient.com/how-to-add-a-gradient-background-to-any-jcomponent/) is a way to get the gradient back by overriding `JButton.paintComponent`. – user0 Apr 27 '13 at 22:24
  • I was not aware that the Mac and Windows Java implementations behaved differently in this regard. It looks like user0's implementation would work, although it looks like you'd have to pick your colors ahead of time, which means it isn't truly platform independent. – Thunderforge Apr 27 '13 at 22:26
  • Different operating systems have different [Swing look and feels](http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html). – user0 Apr 27 '13 at 22:35
  • Yes, every LAF does its own custom painting for components. See my edit for a hack you might be able to use. – camickr Apr 27 '13 at 22:39
4

This is most easily achieved by returning a preferred size that is NxN - where N is the larger of preferred width or height.

Square Buttons

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

class SquareButton extends JButton {

    SquareButton(String s) {
        super(s);
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension d = super.getPreferredSize();
        int s = (int)(d.getWidth()<d.getHeight() ? d.getHeight() : d.getWidth());
        return new Dimension (s,s);
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                JComponent gui = new JPanel(new FlowLayout());
                for (int ii=0; ii<5; ii++) {
                    gui.add(new SquareButton("" + ii));
                }

                gui.setBorder(new EmptyBorder(4, 8, 4, 8));

                JFrame f = new JFrame("Square Buttons");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See http://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);
            }
        };

        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Note this technique will automatically account for the PLAF and button text/icon, whereas the technique shown at [this answer](http://stackoverflow.com/a/16257225/418556) is fragile and will fail if the text or PLAF are set after button creation. – Andrew Thompson Apr 28 '13 at 07:16
  • When you say that it will account for the PLAF and text/icon, are you saying it will automatically resize itself based on what is actually on the button, even if it's changed? Also, is there a way to make it have less space around the text, as in the example in the question? – Thunderforge Apr 28 '13 at 23:47
  • *"When you say that it will account for the PLAF and text/icon, are you saying it will automatically resize itself based on what is actually on the button, even if it's changed?"* I'm amazed people would ask me a question they can resolve by themselves in around 10 minutes. **Try it.** *"less space around the text"* Look to insets and the PLAF. – Andrew Thompson Apr 29 '13 at 02:35
  • 1
    I had access yesterday when I asked the question and accepted an answer, I didn't when you posted your answer today. I was simply asking for clarification on what you were saying. Sorry to have offended you. – Thunderforge Apr 29 '13 at 05:54
0

You can set the size of a button using using setPreferredSize():

JButton button = new JButton("+");
button.setPreferredSize(new Dimension(10, 10));

You might be able to remove the rounded corners using:

button.setBorder(BorderFactory.createEmptyBorder());

If this does not work then you can override the paintComponent() method on JButton.

user0
  • 163
  • 5
  • `createEmptyBorder()` completely removes any border, making the button indistinguishable from a JLabel, at least on Mac OS X. – Thunderforge Apr 27 '13 at 22:18
  • 1
    So it turns out that on Mac OS X, if you do `button.setPreferredSize(new Dimension(20, 20));` and don't do anything with the border, the resulting button is large enough to display the + sign, it drops the rounded corners, it's square, *and* it has the gradient. So this actually winds up being the right solution, much to my surprise. It looks like Mac OS X defaults to a rounded button unless you modify *any* aspect of it, at which point it drops to changes to buttons. – Thunderforge Apr 27 '13 at 22:30
  • Interesting, edited my answer to reflect your feedback. After some experimentation on Windows XP; JButtons have square corners by default, setting no border has no effect and the gradient is only lost if you use `setBackgroundColour()`. – user0 Apr 27 '13 at 23:33
  • Note [this technique](http://stackoverflow.com/a/16259843/418556) will automatically account for the PLAF and button text/icon, whereas the technique shown on *this* answer is fragile and will likely fail if the text or PLAF are set after button creation. – Andrew Thompson Apr 28 '13 at 07:17
-1

Well in order to make them square, you have 2 options: 1. Make the button hold an icon image of just a square image of a transparent image. 2. you could set the button dimensions on your own. I am not sure how to set the dimensions, but that is a an option you could choose. You can just create a JToolBar that is set on the BorderLayout.SOUTH end of the window whenever you add, and whatever buttons are added onto that, will be right next to each other. To add the buttons do this:

    JButton button1 = new JButton("+");
    JButton button2 = new JButton("-");
    JToolBar toolbar = new JToolBar();

    <JPanel,JFrame,Whatever>.add(toolbar, BorderLayout.SOUTH);

    toolbar.add(button1);
    toolbar.add(button2);

This will add the toolbar onto the JFrame, JPanel, or whatever you're adding it onto, and it will set it to the bottom of the screen as you see above.

Hope this helps!

user2277872
  • 2,963
  • 1
  • 21
  • 22
  • 1
    While the image idea is creative, it will not work, as all images are square (and are simply wrapped by the rounded edges). Setting the button dimensions manually also will not affect the shape, only the size. – FThompson Apr 27 '13 at 21:58