Search code examples
javaswinglayout-managerjradiobuttontemperature

How do I put label under radio button within a panel with Java Swing/awt


enter image description here

There is 3 panels which I created as seen in the image. The first panel is the "From" panel, second is "To" panel, and third is the buttons panel. So the question is, how can I put a new line for the "Enter Temperature: [ ]" so that it will be under neath the radio buttons? I am very new to Java swing/awt please bear with me.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.*;

public class TemperatureConversion extends JFrame{

// component
JTextField txtFromTemp, txtToTemp;
JLabel lblFromTemp, lblToTemp;
JRadioButton radFromCelsius, radFromFahrenheit, radFromKelvin;
JRadioButton radToCelsius, radToFahrenheit, radToKelvin;    
JPanel pnlFromRadioButton, pnlToRadioButton, pnlFromTemp, pnlButton;
ButtonGroup bgFrom, bgTo;
JButton btnConvert, btnExit;

// constructor
public TemperatureConversion(){
    super("Temperature");

    // assign objects
    radFromCelsius = new JRadioButton("Celsius", true);
    radFromFahrenheit = new JRadioButton("Fahrenheit");
    radFromKelvin = new JRadioButton("Kelvin");
    lblFromTemp = new JLabel("Enter Temperature: ");
    pnlFromTemp = new JPanel();
    btnConvert = new JButton("Convert");
    btnExit = new JButton("Exit");
    pnlButton = new JPanel();
    txtFromTemp = new JTextField(3);
    lblToTemp = new JLabel("Comparable Temperature: ");
    txtToTemp = new JTextField(3);


    // register the button to a listener 
    btnExit.addActionListener(new MyButtonListener());
    btnConvert.addActionListener(new MyButtonListener());


    // make the multiple choice exclusive but not a container
    bgFrom = new ButtonGroup();
    bgFrom.add(radFromCelsius);
    bgFrom.add(radFromFahrenheit);
    bgFrom.add(radFromKelvin);

    // radio buttons
    radToCelsius = new JRadioButton("Celsius");
    radToFahrenheit = new JRadioButton("Fahrenheit", true);
    radToKelvin = new JRadioButton("Kelvin");

    // make the multiple choice exclusive 
    bgTo = new ButtonGroup();
    bgTo.add(radToCelsius);
    bgTo.add(radToFahrenheit);
    bgTo.add(radToKelvin);


    pnlFromRadioButton = new JPanel();
    pnlToRadioButton = new JPanel();

    // decorate the panel
    pnlFromRadioButton.setBorder(BorderFactory.createTitledBorder("From"));
    pnlToRadioButton.setBorder(BorderFactory.createTitledBorder("To"));

    // add radiobutton to panel
    pnlFromRadioButton.add(radFromCelsius);
    pnlFromRadioButton.add(radFromFahrenheit);
    pnlFromRadioButton.add(radFromKelvin);
    pnlToRadioButton.add(radToCelsius);
    pnlToRadioButton.add(radToFahrenheit);
    pnlToRadioButton.add(radToKelvin);

    // add button to panel
    pnlButton.add(btnConvert);
    pnlButton.add(btnExit);

    // add label and txt field to panel
    pnlFromRadioButton.add(lblFromTemp);
    pnlFromRadioButton.add(txtFromTemp);
    pnlToRadioButton.add(lblToTemp);
    txtToTemp.setEditable(false);
    pnlToRadioButton.add(txtToTemp);


    // add panels to the frame
    add(pnlFromRadioButton, BorderLayout.NORTH);
    add(pnlToRadioButton, BorderLayout.CENTER);
    add(pnlButton, BorderLayout.SOUTH);


    setVisible(true);
    setSize(400, 300);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack();
}


// private inner class to handle button event
private class MyButtonListener implements ActionListener {
    // must override actionPerformed method
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btnConvert) {
            if (radFromCelsius.isSelected())
             System.out.print("exit");
        } else if (e.getSource() == btnExit) {
            System.exit(0);
        }
    }
}


public static void main(String[] args) {
    new TemperatureConversion();
}

}

Solution

  • Nest more JPanels and use layout managers

    For instance, in the JPanel where you want two lines, give it a BoxLayout oriented along the BoxLayout.PAGE_AXIS, and then add two more JPanels to this BoxLayout-using, a top JPanel with the radio buttons and bottom JPanel with the JLabel and JTextField (or whatever else you want in it).

    Side note: this would be a great place to use an enum one called TempScale that had three values: CELSIUS, FAHRENHEIT, KELVIN. You could even give the enum the formulas for conversion to and from Kelvin.

    For example:

    import java.awt.Component;
    import java.awt.event.*;
    import java.util.HashMap;
    import java.util.Map;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class TempConversion2 extends JPanel {
        private ToFromPanel fromPanel = new ToFromPanel("From", true);
        private ToFromPanel toPanel = new ToFromPanel("To", false);
        private ButtonPanel buttonPanel = new ButtonPanel(fromPanel, toPanel);
    
        public TempConversion2() {
            setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
            add(fromPanel);
            add(toPanel);
            add(buttonPanel);
        }
    
        private static void createAndShowGui() {
            TempConversion2 mainPanel = new TempConversion2();
    
            JFrame frame = new JFrame("Temp Convert");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    
    @SuppressWarnings("serial")
    class ButtonPanel extends JPanel {
    
        public ButtonPanel(ToFromPanel fromPanel, ToFromPanel toPanel) {
            add(new JButton(new ConvertAction("Convert", fromPanel, toPanel)));
            add(new JButton(new ExitAction("Exit", KeyEvent.VK_X)));
        }
    
    }
    
    @SuppressWarnings("serial")
    class ConvertAction extends AbstractAction {
        private ToFromPanel fromPanel;
        private ToFromPanel toPanel;
    
        public ConvertAction(String name, ToFromPanel fromPanel, ToFromPanel toPanel) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
            this.fromPanel = fromPanel;
            this.toPanel = toPanel;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            String text = fromPanel.getText();
            try {
                double fromTemp = Double.parseDouble(text.trim());
                TempScale fromScale = fromPanel.getTempScalesPanel().getSelectedTempScale();
                double kelvinValue = fromScale.convertToKelvin(fromTemp);
    
                TempScale toScale = toPanel.getTempScalesPanel().getSelectedTempScale();
                double toValue = toScale.convertFromKelvin(kelvinValue);
    
                String toValueString = String.format("%.2f", toValue);
                toPanel.setText(toValueString);
            } catch (NumberFormatException e1) {
                Component parentComponent = fromPanel;
                String message = "Text must be a valid number: " + text;
                String title = "Invalid Text Entered";
                int messageType = JOptionPane.ERROR_MESSAGE;
                JOptionPane.showMessageDialog(parentComponent, message, title, messageType);
                fromPanel.setText("");
            }
        }
    }
    
    @SuppressWarnings("serial")
    class ExitAction extends AbstractAction {
        public ExitAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }
    
    @SuppressWarnings("serial")
    class ToFromPanel extends JPanel {
        private String title;
        private TempScalesPanel tempScalesPanel = new TempScalesPanel();
        private JTextField tempTextField = new JTextField(3);
    
        public ToFromPanel(String title, boolean textFieldEnabled) {
            this.title = title;
            tempTextField.setFocusable(textFieldEnabled);
    
            JPanel bottomPanel = new JPanel();
            bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
            bottomPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
            bottomPanel.add(new JLabel("Temperature:"));
            bottomPanel.add(Box.createHorizontalStrut(8));
            bottomPanel.add(tempTextField);
    
            setBorder(BorderFactory.createTitledBorder(title));
            setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
            add(tempScalesPanel);
            add(bottomPanel);
        }
    
        public String getTitle() {
            return title;
        }
    
        public TempScalesPanel getTempScalesPanel() {
            return tempScalesPanel;
        }
    
        public String getText() {
            return tempTextField.getText();
        }
    
        public void setText(String text) {
            tempTextField.setText(text);
        }
    }
    
    @SuppressWarnings("serial")
    class TempScalesPanel extends JPanel {
        private ButtonGroup buttonGroup = new ButtonGroup();
        private Map<ButtonModel, TempScale> buttonTempMap = new HashMap<>();
    
        public TempScalesPanel() {
            for (TempScale tempScale : TempScale.values()) {
                JRadioButton radioButton = new JRadioButton(tempScale.getName());
                add(radioButton);
                buttonGroup.add(radioButton);
                buttonTempMap.put(radioButton.getModel(), tempScale);
                // set first button as selected by default
                if (buttonGroup.getSelection() == null) {
                    buttonGroup.setSelected(radioButton.getModel(), true);
                }
            }
        }
    
        public TempScale getSelectedTempScale() {
            ButtonModel model = buttonGroup.getSelection();
            return buttonTempMap.get(model);
        }
    }
    

    This is the enum that I was talking about. Note that if you change the enum, and for instance add another temperature scale element, the program will automatically include it in the GUI and in the calculations. God I love Java and OOP.

    public enum TempScale {
        CELSIUS("Celsius", 1.0, -273.15), 
        FAHRENHEIT("Fahrenheit", 5.0 / 9.0, -459.67), 
        KELVIN("Kelvin", 1.0, 0.0);
    
        private TempScale(String name, double ratioToKelvin, double absZero) {
            this.name = name;
            this.ratioToKelvin = ratioToKelvin;
            this.absZero = absZero;
        }
    
        private String name;
        private double ratioToKelvin;
        private double absZero;
    
        public String getName() {
            return name;
        }
    
        public double getRatioToKelvin() {
            return ratioToKelvin;
        }
    
        public double getAbsZero() {
            return absZero;
        }
    
        public double convertToKelvin(double value) {
            return (value - absZero) * ratioToKelvin;
        }
    
        public double convertFromKelvin(double kelvinValue) {
            return (kelvinValue / ratioToKelvin) + absZero;
        }
    
    }