Working with Swing Components: Spinners, Split Panes, Tabbed Panes, and Tables
Using Spinners
A JSpinner is a composite component that allows a user to select a value from an ordered sequence, such as numbers or dates. It consists of a text field for displaying and editing the current value, and two small buttons (up and down arrows) for stepping through the sequence. Unlike a combo box, a spinner does not display a dropdown list of all possible values, making it suitable for sequences where the values are obvious or when the set of values is very large.
The sequence of values and the current value are managed by a SpinnerModel. Swing provides several standard models:
SpinnerListModel: For selecting from an array orListof objects.SpinnerNumberModel: For selecting from a range of numbers with a defined step size.SpinnerDateModel: For selecting from a range of dates, incrementing or decrementing a specific calendar field.
Here's an example of creating a spinner to select a time of day:
import javax.swing.*;
import java.awt.*;
import java.util.Calendar;
import java.util.Date;
public class TimeSpinnerDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Time Spinner Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
// Create a model for hours (0-23)
SpinnerNumberModel hourModel = new SpinnerNumberModel(12, 0, 23, 1);
JSpinner hourSpinner = new JSpinner(hourModel);
hourSpinner.setEditor(new JSpinner.NumberEditor(hourSpinner, "00"));
// Create a model for minutes (0-59)
SpinnerNumberModel minuteModel = new SpinnerNumberModel(0, 0, 59, 1);
JSpinner minuteSpinner = new JSpinner(minuteModel);
minuteSpinner.setEditor(new JSpinner.NumberEditor(minuteSpinner, "00"));
frame.add(new JLabel("Hour:"));
frame.add(hourSpinner);
frame.add(new JLabel("Minute:"));
frame.add(minuteSpinner);
frame.pack();
frame.setVisible(true);
}
}
Custom Spinners
You can create custom spinner models by extending AbstractSpinnerModel. For example, a model that cycles through a list of weekdays:
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class CyclingWeekdayModel extends SpinnerListModel {
private final Object[] weekdays = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
private int currentIndex = 0;
public CyclingWeekdayModel() {
super(new String[]{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"});
}
@Override
public Object getNextValue() {
currentIndex = (currentIndex + 1) % weekdays.length;
return super.getList().get(currentIndex);
}
@Override
public Object getPreviousValue() {
currentIndex = (currentIndex - 1 + weekdays.length) % weekdays.length;
return super.getList().get(currentIndex);
}
}
Using Split Panes
A JSplitPane is a container that divides its space into two parts, either horizontal or vertically. The user can drag the divider to resize the two components. It's common to place each component inside a JScrollPane to allow scrolling if the content is larger than the available space.
Here's how to create a split pane with a text area and a list:
import javax.swing.*;
import java.awt.*;
public class SplitPaneDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Split Pane Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
// Create a text area
JTextArea textArea = new JTextArea("This is the text area.");
JScrollPane textScrollPane = new JScrollPane(textArea);
// Create a list
String[] data = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};
JList<string> list = new JList<>(data);
JScrollPane listScrollPane = new JScrollPane(list);
// Create the split pane
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScrollPane, textScrollPane);
splitPane.setOneTouchExpandable(true); // Adds one-touch expand buttons
splitPane.setDividerLocation(150); // Initial divider location in pixels
frame.add(splitPane, BorderLayout.CENTER);
frame.setSize(400, 300);
frame.setVisible(true);
}
}</string>
Controlling the Split Pane
You can control the split pane's behavior using methods like setDividerLocation and setResizeWeight. The setResizeWeight method determines how extra space is distributed when the split pane is resized. A value of 0.5 gives equal weight to both components.
// Give equal weight to both components when the split pane is resized
splitPane.setResizeWeight(0.5f);
// Set the divider location to a specific pixel position
splitPane.setDividerLocation(200);
// Reset the divider to its preferred location
splitPane.resetToPreferredSizes();
Using Tabbed Panes
A JTabbedPane allows you to group multiple components (like panels) into a single container, with each component accessible via a tab. This is useful for organizing a complex user interface into manageable sections.
Here's an example of a tabbed pane with three tabs:
import javax.swing.*;
import java.awt.*;
public class TabbedPaneDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Tabbed Pane Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
JTabbedPane tabbedPane = new JTabbedPane();
// Create the first tab
JPanel panel1 = new JPanel();
panel1.add(new JLabel("This is the first tab."));
tabbedPane.addTab("General", panel1);
// Create the second tab
JPanel panel2 = new JPanel();
panel2.add(new JLabel("This is the second tab."));
tabbedPane.addTab("Appearance", panel2);
// Create the third tab
JPanel panel3 = new JPanel();
panel3.add(new JLabel("This is the third tab."));
tabbedPane.addTab("Advanced", panel3);
frame.add(tabbedPane);
frame.setVisible(true);
}
}
Custom Tab Components
You can customize the appearance of tabs by setting a custom component for each tab using setTabComponentAt. This allows you to add buttons, icons, or other UI elements to the tabs.
// Add a close button to the first tab
JButton closeButton = new JButton("X");
closeButton.addActionListener(e -> tabbedPane.removeTabAt(0));
tabbedPane.setTabComponentAt(0, closeButton);
Using Tables
A JTable displays data in a two-dimensional grid. It does not store the data itself; instead, it uses a TableModel to access the data. This follows the Model-View-Controller (MVC) design pattern.
Here's a basic example of creating a table with a custom model:
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
public class SimpleTableDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Simple Table Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Define column names
String[] columnNames = {"ID", "Name", "Price"};
// Define data
Object[][] data = {
{1, "Product A", 19.99},
{2, "Product B", 29.50},
{3, "Product C", 9.99}
};
// Create a custom table model
JTable table = new JTable(new AbstractTableModel() {
@Override
public int getRowCount() {
return data.length;
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public Class> getColumnClass(int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}
});
// Add the table to a scroll pane
JScrollPane scrollPane = new JScrollPane(table);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
}
}
Custom Renderers and Editors
To customize how data is displayed or edited, you can provide custom TableCellRenderer and TableCellEditor implementations. For example, to render a Color value as a colored rectangle:
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
class ColorRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof Color) {
c.setBackground((Color) value);
setText(""); // Clear text for a color cell
}
return c;
}
}
You can then set this renderer for a specific column:
table.getColumnModel().getColumn(2).setCellRenderer(new ColorRenderer());