I've written two Swing GUI: one with single column and one with two columns. Let me show you screen of the first app.
I also wrote an app with two columns mode. Here's screenshot:
Problem
I'd like to combine these two GUI into one applications. The second application is based on GroupLayout
; I find the code clean as there is virtually no hand-made positioning of components. However, many tutorials suggest that GroupLayout
is not the right layout without pointing which layout would be better, thought.
Here's the code of my first app:
public static void main(final String[] args) {
SwingUtilities.invokeLater(Mvp::createGui);
}
private static void createGui() {
final JFrame frame = new JFrame("Page labels app");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel panel = new JPanel(new GridLayout(2, 1));
final JPanel tablePanel = createTablePanel();
panel.add(tablePanel);
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static JPanel createTablePanel() {
final PageLabelsTable table = new PageLabelsTable();
final JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setPreferredSize(new Dimension(400, 120));
final JPanel tablePanel = new JPanel();
tablePanel.add(scrollPane);
tablePanel.setBorder(BorderFactory.createTitledBorder("Page styles"));
return tablePanel;
}
public class PageLabelsTable extends JTable {
private final DefaultTableModel tableModel;
public PageLabelsTable() {
tableModel = new DefaultTableModel();
tableModel.addColumn("Style");
tableModel.addColumn("Prefix");
tableModel.addColumn("From");
tableModel.addColumn("To");
tableModel.addRow(
new String[]{"Table A 00", "Table A 01", "Table A 02", "Table A 03"});
tableModel.addRow(
new String[]{"Table A 10", "Table A 11", "Table A 12", "Table A 13"});
setModel(tableModel);
Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
if (event.getID() == MouseEvent.MOUSE_CLICKED) {
final MouseEvent mouseEvent = (MouseEvent) event;
final int row = rowAtPoint(mouseEvent.getPoint());
if (row == -1) {
clearSelection();
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
getTableHeader().setReorderingAllowed(false);
setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
setDragEnabled(true);
}
}
And here's the code for the second app (heavily based on the code found here at SO):
public void createUI() {
initUI();
final JFrame frame = new JFrame(APP_NAME);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setResizable(false);
closeOnEscape(frame);
frame.getRootPane().setDefaultButton(fixButton);
frame.setContentPane(ui);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setMinimumSize(frame.getSize());
frame.setVisible(true);
}
private void closeOnEscape(final JFrame frame) {
// close on ESCAPE
frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel");
frame.getRootPane().getActionMap().put("Cancel", disposeFrame(frame));
}
private AbstractAction disposeFrame(final JFrame frame) {
return new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
frame.dispose();
}
};
}
private JComponent getTwoColumnLayout(final JLabel[] labels, final JComponent[] fields) {
if ( labels.length != fields.length ) {
throw new IllegalArgumentException(
"%d labels supplied for %d fields!".formatted(labels.length, fields.length));
}
final JComponent panel = new JPanel();
final GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
// Turn on automatically adding gaps between components
layout.setAutoCreateGaps(true);
// Create a sequential group for the horizontal axis.
final GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
final GroupLayout.Group yLabelGroup = layout.createParallelGroup(
GroupLayout.Alignment.TRAILING);
hGroup.addGroup(yLabelGroup);
final GroupLayout.Group yFieldGroup = layout.createParallelGroup();
hGroup.addGroup(yFieldGroup);
layout.setHorizontalGroup(hGroup);
// Create a sequential group for the vertical axis.
final GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
layout.setVerticalGroup(vGroup);
// add the components to the groups
for (JLabel label : labels) {
yLabelGroup.addComponent(label);
}
for (Component field : fields) {
yFieldGroup.addComponent(field, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE);
}
for (int i = 0; i < labels.length; i++) {
vGroup.addGroup(layout.createParallelGroup().addComponent(labels[i])
.addComponent(fields[i], GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE));
}
addMnemonics(labels, fields);
return panel;
}
private void addMnemonics(final JLabel[] labels, final JComponent[] fields) {
final Map<Character, Character> map = new HashMap<>();
for (int i = 0; i < labels.length; i++) {
labels[i].setLabelFor(fields[i]);
final String lowerCase = labels[i].getText().toLowerCase();
for (int j = 0; j < lowerCase.length(); j++) {
final char ch = lowerCase.charAt(j);
if ( Character.isLetterOrDigit(ch) && map.get(ch) == null ) {
map.put(ch, ch);
labels[i].setDisplayedMnemonic(ch);
break;
}
}
}
}
/**
* Provides a JPanel with two columns (labels & fields) laid out using GroupLayout. The arrays
* must be of equal size.
*
* @param labelStrings Strings that will be used for labels.
* @param fields The corresponding fields.
* @return JComponent A JPanel with two columns of the components provided.
*/
private JComponent getTwoColumnLayout(final String[] labelStrings, final JComponent[] fields) {
final JLabel[] labels = new JLabel[labelStrings.length];
for (int i = 0; i < labels.length; i++) {
labels[i] = new JLabel(labelStrings[i]);
}
return getTwoColumnLayout(labels, fields);
}
private void initUI() {
if ( ui != null ) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
// Here is our control. This puts a titled border around it,
// instead of using a label in the PAGE_START
final JPanel jPanel = new JPanel(new BorderLayout());
ui.add(jPanel);
final JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 15));
jPanel.add(actionPanel, BorderLayout.PAGE_END);
this.fixButton = new JButton("Fix");
this.resetButton = new JButton("Reset");
this.cancelButton = new JButton("Cancel");
Stream.of(fixButton, resetButton, cancelButton).forEach(actionPanel::add);
// Use GroupLayout for the label/cotrnl combos.
// make the arrays for the factory method
final String[] labels = { "Page label style", "Start page", "Remove bookmarks" };
final String[] ccString = LabelStyle.getAllStyles();
final JPanel checkBoxPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
checkBoxPanel.add(new JCheckBox("", false));
final JComponent[] controls = { new JComboBox<>(ccString), new JTextFieldNumeric(5),
checkBoxPanel };
jPanel.add(getTwoColumnLayout(labels, controls));
// throw in a few borders for white space
final Border insideBorder = new CompoundBorder(new EmptyBorder(10, 10, 5, 10),
new TitledBorder(APP_NAME));
final Border border = new CompoundBorder(insideBorder, new EmptyBorder(10, 10, 5, 10));
jPanel.setBorder(border);
}