Search code examples
javaswinguser-interfacelayout-managergraphics2d

Confusion regarding the working of flowlayout


I have developed a small swing application in which there is a square rotating in the upper half and there is a button in the lower half which can stop/run the square to rotate. I have used the GridLayout to place the rotating square and the button. (Another alternative is to use 2 JPanels ,one is with rotating square and second contains the button.Using this button appears of proper size.)

Here is the code :-

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;


public class Rotation {
JButton jbtn=new JButton("Stop");
component jpn2=new component();    //created a JPanel named jpn2 and got a reference to its timer object.
Timer timer=jpn2.timer;
Rotation()
{


    JFrame jfrm=new JFrame("Rotating a square about a center");
    jfrm.setSize(400,400);
    jfrm.setLayout(new GridLayout(2,1));
    jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //JPanel jpnl=new JPanel();

    //jpnl.add(jbtn);

    jbtn.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){
        if(e.getActionCommand().equals("Stop"))
        {
            timer.stop();
            jbtn.setText("Spin");
        }
        if(e.getActionCommand().equals("Spin"))
        {
            timer.start();
            jbtn.setText("Stop");
        }

    }});

    jfrm.add(jpn2);
    jfrm.add(jbtn);

    //jfrm.add(new JButton("Click"));
    jfrm.setVisible(true);
    //jfrm.setOpacity(0.8f);
}
public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
    //JFrame.setDefaultLookAndFeelDecorated(true);
    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
    SwingUtilities.invokeLater(new Runnable(){public void run(){new Rotation();}});
}

}

class component extends JPanel implements ActionListener
{
Timer timer;
int theta=0;
component()
{
    timer=new Timer(10,this);
    timer.start();
}
public void paintComponent(Graphics g)
{
    super.paintComponent(g);
    Graphics2D g2=(Graphics2D)g;
    g2.rotate(theta,100,100);
    g2.fillRect(50, 50, 100,100);
}
public void actionPerformed(ActionEvent e)
{
    //Changing a global variable and then drawing the rectangle again and hence indirectly the square rotates.
    theta=theta+10;
    if(theta==360)
        theta=0;
    repaint();
}
}

Here is the output:-

output

But my confusion is when i decided to use FlowLayout instead of GridLayout i'm getting only the button and no rotating square. By,as far as i have read,FlowLayout places components in a row and if space is less than it uses multiple rows. Can anyone resolve this small stupid problem of mine which currently i am not able to resolve.


Solution

  • As others have said (+1 to mKorbel and HFOE)

    • The problem is you use setSize(..) rather call pack() before setting JFrame to visible.

    • You will also have to override getPrefferedSize(..) in JPanel class which will return the size of your square multiplied by 2 (or else when it rotates it wont fit).

    • On a side note dont throw any excpetion in main(..) never a good thing.

    see below for the code (uses FlowLayout but also works with GridLayout):

    Using new FlowLayout():

    enter image description here

    Using new GridLayout(2,1):

    enter image description here

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class Rotation {
    
        JButton jbtn = new JButton("Stop");
        component jpn2 = new component();    //created a JPanel named jpn2 and got a reference to its timer object.
        Timer timer = jpn2.timer;
    
        Rotation() {
    
    
            JFrame jfrm = new JFrame("Rotating a square about a center");
    
            // jfrm.setLayout(new FlowLayout());
            jfrm.setLayout(new GridLayout(2, 1));
    
            jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            jbtn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (e.getActionCommand().equals("Stop")) {
                        timer.stop();
                        jbtn.setText("Spin");
                    }
                    if (e.getActionCommand().equals("Spin")) {
                        timer.start();
                        jbtn.setText("Stop");
                    }
    
                }
            });
    
            jfrm.add(jpn2);
            jfrm.add(jbtn);
    
            jfrm.pack();
            jfrm.setVisible(true);
        }
    
        public static void main(String args[]) {
            try {
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
    
                ex.printStackTrace();
            }
    
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new Rotation();
                }
            });
        }
    }
    
    class component extends JPanel implements ActionListener {
    
        Timer timer;
        int theta = 0;
        int width = 100, height = 100;
    
        component() {
            timer = new Timer(10, this);
            timer.start();
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.rotate(theta, 100, 100);
            g2.fillRect(50, 50, width, height);
        }
    
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(width * 2, height * 2);//multiply by 2 to fit while rotating
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            //Changing a global variable and then drawing the rectangle again and hence indirectly the square rotates.
            theta = theta + 10;
            if (theta == 360) {
                theta = 0;
            }
            repaint();
        }
    }