Graphics in Java - 2. Writing GUI programs
We're going to be considering applications here; we'll do applets some other time (briefly). As mentioned earlier, we're learning Swing, not AWT nor JavaFX.
All GUI programs and graphics programs are based on a JFrame. The JFrame looks after the following:
- title of window
JFrame frame = new JFrame("TITLE OF WINDOW");
orframe.setTitle("Title something");
- size of window
frame.setSize(600, 400);
- location of window
frame.setLocationRelativeTo(null); // centre window
orframe.setLocation(100,100);
- buttons to close, resize, minimize window
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
- whether the window can be resized
frame.setResizable(true);
- the "look and feel" of the window
frame.add(panel);
frame.pack();
if needed- You must end up with this:
frame.setVisible(true);
You cannot draw on a JFrame, nor place GUI components on a JFrame.
Instead you do these things to a JPanel. This is then placed on the JFrame.
Various patterns for writing Swing GUI programs
Remember: Keep the graphics and drawing part of your program separate from the calculation and data management part of your program. You frequently have two separate classes to do this.
Note: These examples are extremely simple.
If you want to draw on the JPanel you have to make a subclass of JPanel. (See next page)
If you want to animate anything or are concerned about response time,
you have to use SwingUtilities.invokeLater() to start something called the Event Dispatch Thread.
See http://www.javacreed.com/swing-worker-example/ and Pattern3 below.
Later on, all of your Swing programs will start using this method.
Pattern 1 main class makes everything
import javax.swing.*; public class SimpleGUI { public static void main(String args[]) { JFrame window = new JFrame("Simple GUI"); window.setSize(200,60); window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); window.setLocationRelativeTo(null); JPanel panel = new JPanel(); JButton btn1 = new JButton("Nada"); panel.add(btn1); window.add(panel); window.setVisible(true); } } |
0. don't forget to import packages 1. change SimpleGUI to be your classname. 2. Set up the JFrame 3. Make JPanel. Add stuff to it. 4. Add JPanel to JFrame. Pack and Show JFrame. |
Pattern 2 main class extends JFrame.
This is nice because now we don't have to make all of our methods and global variables static
There are other advantages, but I can't think of them right now
import javax.swing.*; public class SimpleGUI2 extends JFrame { public static void main(String[] args) { new SimpleGUI2(); } SimpleGUI2 () { this.setTitle("Simple GUI #2"); this.setSize(200,60); this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); this.setLocationRelativeTo(null); JPanel panel = new JPanel(); JButton btn1 = new JButton("Nada"); panel.add(btn1); this.add(panel); this.setVisible(true); } } |
1. This whole class is now a JFrame object 2. All main() does is make an object by calling the constructor. You could also write: SimpleGUI2 window = new SimpleGUI2(); 3. constructor 4. Same as before, but the object is referred to as "this" |
Pattern 3 main class starts the Event thread
This is needed if you want to animate anything, i.e. use a Timer or a Thread.
You just copy the blue text exactly as it is.
import javax.swing.*; public class GUIthread extends JFrame { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new GUIthread(); } }); } //end main GUIthread() { this.setTitle("Simple GUI - runnable"); this.setSize(200,60); this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); this.setLocationRelativeTo(null); //set up the JPanel JPanel panel = new JPanel(); JButton btn1 = new JButton("Nada"); panel.add(btn1); this.add(panel); this.setVisible(true); } } |
1. extends JFrame as before 2. Copy this exactly. It starts the "Event Dispatch Thread" with SwingUtilities.invokeLater() 3. constructor |
Order of events
This is generally the order of things that you do to make a GUI
- make JFrame
- set it up
- make JPanel
- make all GUI objects
- position them, etc
- add them to JPanel
- repeat step 2 for as many panels as needed
- add panel(s) to JFrame
- pack the JFrame if needed
- You have to setLocationRelativeTo(null) after packing because pack changes the size of the JFrame.
- set the JFrame to be visisble
Pack vs Validate
** The former information that I had here was incorrect. **
In order to arrange the components in a layout properly, Swing runs something called validate()
on the container.
You normally never have to call this directly.
Validate is automatically called by setVisible()
and pack()
, as well as any time the window is resized.
The only(?) time that you need to explicitly need to call validate()
is if you add components to the JFrame or JPanel AFTER you have run setVisible(), as seen in the example below.
After you add (or remove) Components to/from a Container,
you must do a Container.validate() to let the components jostle about (i.e. let the LayoutManager determine their new sizes and positions).
This does not happen automatically, to avoid recalculating the layout after every add in a continuous stream of adds.
It would just cause annoying flicker.
pack()
arranges the components in the smallest possible space. Sometimes this is what you want to do.
The JFrame pack() method tells the frame window to resize itself to the minimum size required to hold all its components.
Instead of having to determine the size of the JFrame, pack tells it to be "just big enough."
You need to use pack()
when you are setting the size of the JPanel to be a certain size. Please see the write up here.
import java.awt.GridLayout; import java.awt.BorderLayout; import java.awt.Color; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class GridButtons { public static void main(String[] args) { new GridButtons(); } /* Program to see how a grid layout handles a non-optimum number of buttons * **************************************************************************** * 1-4 buttons: 1 column * 5 buttons: 2x2 + 1 below * 5-8: 2 columns filling up until we get 4 rows (4x2) * 9 buttons: 3x3 grid * 10-12: add buttons on 4th row to get the full number. * 12: 4 rows, 3 columns as desired. * 13-15: move extra buttons to the end of each row (giving 4 columns) and start row 4 again * 16: 4x4 * 17-20: take 3 last buttons off bottom row, add another column (5 columns) and rearrange the buttons. * 16 and 17 are now on the bottom and there are 3 empty spaces. * 20: 4x5 * each time a row is filled up, this repeats. Add another column, move 3 buttons to the end of each row, * and start row 4 again. */ GridButtons() { JFrame window = new JFrame("Adding Buttons to 4rows x 3cols Grid Layout"); window.setSize(600, 300); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setLocationRelativeTo(null); window.setResizable(true); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(4,3,2,2)); //4x3 layout panel.setBackground(new Color(150,0,100)); window.add(panel, BorderLayout.CENTER); window.setVisible(true); //Add buttons for (int i = 1; i <= 19; i++) { try { Thread.sleep(500); } catch (InterruptedException e) {} panel.add(new JButton(""+i)); //TEST WHAT RUNS VALIDATE window.validate(); //window.repaint(); // X does not run validate() //window.setVisible(true); //runs validate(), as does pack() //resizing the window also runs validate() } } }
Other
Example JOptionPane
/* This short program shows how to get input using JOption Panes. * The surprising thing is that you don't have to actually use a full GUI! * You can use it with console based programs too (instead of Scanner)! * JOptionPane acts like both a message box and an input box. */ import javax.swing.*; public class TestOptionPane { public static void main(String[] args) { String name = JOptionPane.showInputDialog(null, "What's your name?","Title: Thanks to Yong Joon for this!",JOptionPane.PLAIN_MESSAGE); //handle CANCEL option if(name == null){ System.out.println("Cancel pressed"); //this goes to standard output }else{ System.out.println("OK pressed"); } //custom title, no icon String str2 = "Hey, this text may show up somewhere."; JOptionPane.showMessageDialog(null, str2,"A \"plain\" message to " + name, JOptionPane.PLAIN_MESSAGE); } }