There are many more types of "widgets" that can be used in GUI programs. For the most part, they work similarly to the ones we have seen - they can be created and added to windows in the same way as buttons or labels. They each have different constructors and methods specific to the type of widget it is:
A check box is like an input for a boolean value. It can either be checked or unchecked.
Radio buttons are buttons where only one button in a group can be selected. Choosing any one will unselect the others.
A text field is a String input where the user can enter a single String.
A text area is like a larger region where the user can write text.
A password field is like a text field except that the input is not displayed.
A combo box is a drop down list which allows the user to select one option.
A slider allows the user to enter a number in a given range visually.
A progress bar shows the user a percentage.
There are also a number of "dialogs" which you can present the user where they supply some information:
Prompts the user to enter a color.
Prompts the user to enter a file from their computer.
There are more types of layouts beyond the "BoxLayout" that we saw previously. This page lists them along with visual examples of how they work.
If necessary, programs can use multiple layouts at one time. To do this, you need to create a JPanel object. A JPanel is a place where multiple widgets can be placed together, using some layout.
For example, in the Calculator example below, the overall window uses a vertical BoxLayout. This layout contains three things: a JLabel for the display, a JPanel for most of the buttons, and finally another JPanel for two more buttons. The middle panel uses a GridLayout while the bottom panel uses horizontal BoxLayout.
Using this approach, you can arrange widgets on the screen in whichever way you want.
The slightly longer example below is a simple GUI calculator program written in Java.
Some notes on the program:
// CalculatorGui.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
// represents one of our calculator buttons
// the order is how they will be added to the grid
enum ButtonType {
// row 1
N1,
N2,
N3,
PLUS,
// row 2
N4,
N5,
N6,
MINUS,
// row 3
N7,
N8,
N9,
TIMES,
// row 4
N0,
DOT,
SQRT,
DIVIDE,
// bottom pane
EQUALS,
CLEAR
}
// the calculator can be in any of these states
enum CalculatorState {
DEFAULT, // reading in numbers but not yet at an operator
ADDING, // we entered one number and pushed the + button, now reading the second
SUBTRACTING, // ditto but for -
MULTIPLYING, // ditto but for *
DIVIDING, // ditto but for /
DONE // we have finished a calculation and are displaying the result
// we can use this for future results, but a number will overwrite
}
// the ButtonListener handles each button press
class ButtonListener implements ActionListener {
// each button listener stores which button it is listening to
// it also stores the calculator object so it can tell it when
// the buttons are pressed
private ButtonType button;
private Calculator calc;
// given the button we are listening for, and the calculator object
public ButtonListener(ButtonType b, Calculator c) {
button = b;
calc = c;
}
@Override
public void actionPerformed(ActionEvent e) {
// check which button we are listening for
switch (button) {
case N0:
case N1:
case N2:
case N3:
case N4:
case N5:
case N6:
case N7:
case N8:
case N9:
// for any of the digits, find out what digit it is
// then add it to the calculator
String stringDigit = button.name().substring(1);
int digit = Integer.parseInt(stringDigit);
calc.addDigit(digit);
break;
// for the other buttons, call the appropriate calculator method
case DOT:
calc.setDot();
break;
case PLUS:
calc.setOperator('+');
break;
case MINUS:
calc.setOperator('-');
break;
case TIMES:
calc.setOperator('*');
break;
case DIVIDE:
calc.setOperator('/');
break;
case SQRT:
calc.sqrt();
break;
case EQUALS:
calc.equals();
break;
case CLEAR:
calc.clear();
break;
}
}
}
// the calculator takes care of the actual calcualtions, and stores the
// current number this class extends the JLabel so it can also serve as
// a display of the current number
class Calculator extends JLabel {
// the state which we are in
CalculatorState state;
// the current value we are using
private double value;
// the saved value if we are doing an operator
private double saved;
// whether or not new digits are added after the dot
private boolean dot;
// the amount of places after the dot to add
private int place;
public Calculator() {
// clear should initialize us to 0, so just call that
clear();
}
// called to clear out the calculator
public void clear() {
// set all the default values
value = 0.0;
saved = 0.0;
dot = false;
place = 0;
state = CalculatorState.DEFAULT;
// update the display
update();
}
// update the display with the current value
public void update() {
// convert the value to a string
String stringValue = Double.toString(value);
// since we are a JLabel, we have a setText function, just call it
setText(stringValue);
}
// called when the user pushes one of the digit buttons
public void addDigit(int digit) {
// if the state is done, reset things first
if (state == CalculatorState.DONE) {
clear();
}
// if there has not been a dot yet
if (!dot) {
// add this digit to the end, but multiplying the current value
// by ten and then adding it in
value = value * 10 + digit;
} else {
// add this digit in the next decimal place available
value = value + digit / (Math.pow(10.0, place));
place++;
}
// update the display
update();
}
// called when equals gets pushed
public void equals() {
// apply the operator depending on our state
switch (state) {
case DEFAULT:
// do nothing!
break;
case ADDING:
value = saved + value;
break;
case SUBTRACTING:
value = saved - value;
break;
case MULTIPLYING:
value = saved * value;
break;
case DIVIDING:
value = saved / value;
break;
}
// update to the new value
update();
// go to the DONE state
state = CalculatorState.DONE;
}
// called when the dot button is pushed
public void setDot() {
// if we already hada a dot, this is an error!
if (dot) {
throw new IllegalStateException("Can't have two decimal points");
}
// set the dot
dot = true;
place = 1;
}
// called when an operator button is pushed
public void setOperator(char operator) {
// save the current value;
saved = value;
// reset value to read
value = 0.0;
dot = false;
place = 0;
// remember the operator so we can apply it later
switch (operator) {
case '+':
state = CalculatorState.ADDING;
break;
case '-':
state = CalculatorState.SUBTRACTING;
break;
case '*':
state = CalculatorState.MULTIPLYING;
break;
case '/':
state = CalculatorState.DIVIDING;
break;
default:
throw new IllegalArgumentException("Invalid Operator");
}
}
// called for the square root operation
public void sqrt() {
value = Math.sqrt(value);
update();
}
}
public class CalculatorGui {
// just returns the name which should be used for a button
public static String buttonName(ButtonType b) {
switch (b) {
// all the numbered ones just return the number part
case N0:
case N1:
case N2:
case N3:
case N4:
case N5:
case N6:
case N7:
case N8:
case N9:
// return the name's substring starting at 1
return b.name().substring(1);
// the rest
case DOT:
return ".";
case PLUS:
return "+";
case MINUS:
return "-";
case TIMES:
return "×";
case DIVIDE:
return "÷";
case SQRT:
return "√";
case EQUALS:
return "=";
case CLEAR:
return "C";
}
throw new IllegalArgumentException("Invlaid button type " + b.name());
}
public static void main(String args[]) {
// create the calculator object which handles calculations
Calculator calc = new Calculator();
// create and set up the window.
JFrame frame = new JFrame("Calculator");
// make the program close when the window closes
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// we need a "panel" for the grid buttons
JPanel grid = new JPanel();
// create a grid layout for the 16 buttons other than clear and equals
GridLayout layout = new GridLayout(4, 4);
grid.setLayout(layout);
// add those first 16 buttons to it
for (int i = 0; i < 16; i++) {
// find this button type
ButtonType b = ButtonType.values()[i];
// create a GUI button for it with the right name
String name = buttonName(b);
JButton button = new JButton(name);
// create a listener for it
ButtonListener listener = new ButtonListener(b, calc);
button.addActionListener(listener);
// add it to the grid
grid.add(button);
}
// now we create a vertical box layout for the textbox, grid of buttons
// and the row of buttons at the bottom
BoxLayout mainLayout = new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS);
frame.getContentPane().setLayout(mainLayout);
// add the calculator display line to the layout
frame.getContentPane().add(calc);
// add the grid
frame.getContentPane().add(grid);
// now create another panel and layout for displaying the two buttons at bottom
JPanel bottom = new JPanel();
BoxLayout bottomLayout = new BoxLayout(bottom, BoxLayout.X_AXIS);
// create the bottom two buttons and listeners for them
JButton clear = new JButton(buttonName(ButtonType.CLEAR));
ButtonListener clearListener = new ButtonListener(ButtonType.CLEAR, calc);
clear.addActionListener(clearListener);
JButton equals = new JButton(buttonName(ButtonType.EQUALS));
ButtonListener equalsListener = new ButtonListener(ButtonType.EQUALS, calc);
equals.addActionListener(equalsListener);
// add them to the bottom panel
bottom.add(clear);
bottom.add(equals);
// add the bottom row to the window
frame.getContentPane().add(bottom);
// display the window.
frame.pack();
frame.setVisible(true);
}
}
Copyright © 2024 Ian Finlayson | Licensed under a Creative Commons BY-NC-SA 4.0 License.