0

Currently I am trying to add an icon which should be as big as the button (added to GridBagLayout). I want to scale the icon to the size of the button, but my button shows its size as 0.

I tried to print out the size of the button (after creating it and also after adding it). It always gives me 0 and therefore an ArgumentException (width (0) and height (0) must be non-zero).

Also I want to dynamically size the font as big as the button is (no implementation yet but also doesn't work because the buttons doesn't give its size). Does anybody has a solution for dynamic resizing using LayoutManager? Maybe there is a way to get the cell size and then scale the image to the cell size?

Here is my test code:


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


    public class TestResize extends JFrame {

    private Container cp;
    private JPanel levelPane;
    private GridBagConstraints gbc = new GridBagConstraints();


    public TestResize() {
        super();
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        int frameWidth = 200;
        int frameHeight = 200;
        setSize(frameWidth, frameHeight);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (d.width - getSize().width) / 2;
        int y = (d.height - getSize().height) / 2;
        setLocation(x, y);
        setTitle("temp");
        setResizable(false);
        cp = getContentPane();
        cp.setLayout(new BorderLayout());

        createLevelMenu();


        setVisible(true);
    } // end of public temp


    public static void main(String[] args) {
        new TestResize();
    } // end of main

    public void createLevelMenu(){

        if (levelPane == null){
            levelPane = new JPanel(new GridBagLayout());

            gbc.fill = GridBagConstraints.NONE;

            JButton back = new JButton();
            back.setOpaque(false);
            back.setBackground(null);
            back.setBorder(null);

            addObject(0,0,1,1,GridBagConstraints.FIRST_LINE_START,back);
            Image img = (new ImageIcon("Content/Graphics/UI/returnIcon.png")).getImage().getScaledInstance(back.getWidth(),back.getHeight(),Image.SCALE_SMOOTH);
            back.setIcon(new ImageIcon(img));

            JPanel menu = new JPanel(new BorderLayout());
            menu.setBackground(Color.RED);
            JLabel l = new JLabel("Demo");
            l.setFont(l.getFont().deriveFont(30)); //Font Size as big as Possible (adjust
            l.setHorizontalAlignment(SwingConstants.CENTER);
            l.setVerticalAlignment(SwingConstants.CENTER);
            menu.add(l,BorderLayout.CENTER);
            gbc.fill = GridBagConstraints.BOTH;
            addObject(2,2,1,4,GridBagConstraints.CENTER,menu);

            JButton info = new JButton("Info");
            addObject(3,3,1.25f,1.25f,GridBagConstraints.LAST_LINE_END,info);

            cp.add(levelPane, BorderLayout.CENTER);
        }
    }

    public void addObject(int gridX,int gridY, double weightX,double weightY,int anchor, JComponent comp){
        gbc.gridx =gridX;
        gbc.gridy = gridY;
        gbc.weightx = weightX;
        gbc.weighty = weightY;
        gbc.anchor = anchor;
        levelPane.add(comp,gbc);
    }


    }
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Kevin Kumar
  • 71
  • 1
  • 6
  • While it’s not commonly done, [Icon is an interface](https://docs.oracle.com/en/java/javase/12/docs/api/java.desktop/javax/swing/Icon.html) which you can implement yourself. – VGR Apr 23 '19 at 14:05
  • 1
    See also [this answer](https://stackoverflow.com/a/21066065/418556) to How to change font size of JButton according to its size? – Andrew Thompson Apr 24 '19 at 10:01

1 Answers1

0

I tried to print out the size of the button (after creating it and also after adding it). It always gives me 0 and therefore an ArgumentException (width (0) and height (0) must be non-zero).

yes this would be for a lot of reasons

  • the container is not validated
  • even if validated their is no guarantee for the size as long as it's return of getTopLevelAncestor() is not displayable ! (witch in this case your TestResize object)
  • setting the border to null makes half the way to make your button goes to 0,0 in size as the first reliable call for the size is the preferred size and when it holds no contents and no even insets (the border insets) the ui will give it 0,0 as it's totally empty !

so the concept was wrong from the start and even if you didn't made the border null the size will be equal to the border insets (and for all of the api border's that is tight !!! even for a plain "hi" with font size of 5!!!).

so your best bet in situation like this (but not yours as i mentioned) is to call

back.getPreferredSize()

for this to work(in your situation ) you have to make the starting size of the image your self then the when setting the icon to the button the ui will give it an extra size.

Also I want to dynamically size the font as big as the button

@AndrewThompson pointed to a great examples in that answer he wrote and @trashgod good examples too all of them are spectacular!! +1 for that.


Edit

After that ask the Buttons its size and then add the Icon with the same size?

you have to understand that when using layout managers every thing size is relative to it's parent !! for your example the icon button --> the level panel (witch it's the content pane) --> the root pane JLayeredPane object --> the root pane it self --> finally the frame !! every thing is resized based on the parent size but if in case the parent size 0,0 then don't expect the sub-component's to get more than that !! and just keep in mind that GridBagLayout is the most complex of all the layout and it can behave in a strange way .

and the usage of

component.getWidth();
//and
component.getHeight();

is to get the real size 'ON-THE-SCREEN' the return might not be even related to the getSize() or the others (maximum ,minimum) .

so the best way to get the icon the same as the size you have to make a ComponentListener

Icon with the same size

this is a bad idea because if the L & F have a default insets for button the you will give it more than what it would take!! and with the ComponentListener you will make it have an animation of resizing !!

here is refactored version of your class , ComponentAdapter is an implementation of the interface ComponentListener that implements every method in empty way , am using it so i don't have to implement all other methods too .


import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import mdlaf.utils.MaterialImages;

public class TestResize extends JFrame {

    private Container cp;
    private JPanel levelPane;
    private GridBagConstraints gbc = new GridBagConstraints();

    public TestResize() {
        super();
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        int frameWidth = 200;
        int frameHeight = 200;
        setSize(frameWidth, frameHeight);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int x = (d.width - getSize().width) / 2;
        int y = (d.height - getSize().height) / 2;
        setLocation(x, y);
        setTitle("temp");
        setResizable(false);
        cp = getContentPane();
        cp.setLayout(new BorderLayout());
        createLevelMenu();
        GridBagLayout gbl = (GridBagLayout) levelPane.getLayout();
        gbc = gbl.getConstraints(back);
        gbc.fill = GridBagConstraints.BOTH;
        levelPane.remove(back);
        levelPane.add(back, gbc);
        back.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                //here you can add an image relative to the new size !!
                if (back.getIcon() == null) {
                    fixIcon();
                } else if (back.getIcon().getIconHeight() != back.getHeight() - heightPadding || back.getIcon().getIconWidth() != back.getWidth() - widthPadding) {

                    fixIcon();
                }
            }

        });
        setVisible(true);
    } // end of public temp

    private int widthPadding;
    private int heightPadding;

    private void fixIcon() {
        Image img = (new ImageIcon(MaterialImages.BACK_ARROW)).getImage().getScaledInstance(back.getWidth() - widthPadding, back.getHeight() - heightPadding, Image.SCALE_SMOOTH);
        back.setIcon(new ImageIcon(img));
    }

    public static void main(String[] args) {
        new TestResize();
    } // end of main
    JButton back;

    public void createLevelMenu() {

        if (levelPane == null) {
            levelPane = new JPanel(new GridBagLayout());

            gbc.fill = GridBagConstraints.NONE;

            back = new JButton();
            back.setOpaque(false);
            back.setBackground(null);
            back.setBorder(null);
            addObject(0, 0, 1, 1, GridBagConstraints.FIRST_LINE_START, back);
            JPanel menu = new JPanel(new BorderLayout());
            menu.setBackground(Color.RED);
            JLabel l = new JLabel("Demo");
            l.setFont(l.getFont().deriveFont(30)); //Font Size as big as Possible (adjust
            l.setHorizontalAlignment(SwingConstants.CENTER);
            l.setVerticalAlignment(SwingConstants.CENTER);
            menu.add(l, BorderLayout.CENTER);
            gbc.fill = GridBagConstraints.BOTH;
            addObject(2, 2, 1, 4, GridBagConstraints.CENTER, menu);

            JButton info = new JButton("Info");
            addObject(3, 3, 1.25f, 1.25f, GridBagConstraints.LAST_LINE_END, info);

            cp.add(levelPane, BorderLayout.CENTER);
        }
    }

    public void addObject(int gridX, int gridY, double weightX, double weightY, int anchor, JComponent comp) {
        gbc.gridx = gridX;
        gbc.gridy = gridY;
        gbc.weightx = weightX;
        gbc.weighty = weightY;
        gbc.anchor = anchor;
        levelPane.add(comp, gbc);
    }

}

the widthPadding and the heightPaddingis the total of the L & F insets for button's , i can't help you with that you have to do it by your self, this is a L & F dependent . but for a hack you can change the componentResized method to this (i discourage this)

public void componentResized(ComponentEvent e) {
                //here you can add an image relative to the new size !!
                if (back.getIcon() == null) {
                    back.removeComponentListener(this);
                    fixIcon();
                }

I have already seen this example but I find it a little complicated, so I thought there is maybe a better (easier) solution.

sadly i don't think so , but @AndrewThompson answer seems easy to learn , i would study that if i want to start reading about GlyphVector it's neat and it's simple , and happy coding .

OverLoadedBurden
  • 606
  • 1
  • 5
  • 14
  • First of all thank you for your comment. @AndrewThompson Thanks for this post, I have already seen this example but I find it a little complicated, so I thought there is maybe a better (easier) solution. Still thanks. – Kevin Kumar Apr 25 '19 at 09:27
  • The problem is I use a LayoutManager (in this example GridBagLayout). I don´t want to use any absolute values, so is it possible to make the Button as big as the LayoutManager (my Grid) allows. After that ask the Buttons its size and then add the Icon with the same size? – Kevin Kumar Apr 25 '19 at 09:37
  • also i forgot to mention that – OverLoadedBurden Apr 26 '19 at 12:29
  • `` is it possible to make the Button as big as the LayoutManager (my Grid) allows`` is kinda tricky !! the button location is (0,0) with not other mates at the 0 row or the the 0 column , the GBL calculates the maximum width and height based on the maximum width on the column and the maximum height on the row , so in this case the button size is considered the maximum of each ! , and making size of the button the same as size of the icon is possible IF you overrided the ```paintComponent``` and the ```getPreferredSize``` of the button.@KevinKumar – OverLoadedBurden Apr 26 '19 at 12:35