如何将标签放在带有 Java Swing/awt 的面板中的单选按钮下

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

如图所示,我创建了 3 个面板。第一个面板是 "From" 面板,第二个是 "To" 面板,第三个是按钮面板。所以问题是,如何为 "Enter Temperature: [ ]" 换行,使其位于单选按钮下方?我是新手 Java swing/awt 请多多包涵

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();
}

}

嵌套更多 JPanel 并使用布局管理器

例如,在您想要两条线的 JPanel 中,给它一个沿 BoxLayout.PAGE_AXIS 方向的 BoxLayout,然后再向此 BoxLayout 添加两个 JPanel,一个带有单选按钮的顶部 JPanel 和带有 JLabel 和 JTextField(或您想要的任何其他内容)的底部 JPanel。

旁注:这是使用名为 TempScale 的枚举的好地方,它具有三个值:CELSIUS、FAHRENHEIT、KELVIN。您甚至可以为枚举提供转换开尔文和从开尔文转换的公式。

例如:

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);
    }
}

这就是我所说的枚举。请注意,如果您更改枚举,例如添加另一个温标元素,程序将自动将其包含在 GUI 和计算中。上帝,我爱 Java 和 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;
    }

}

始终考虑发布 MCVE。 例如你的布局可以简化和演示:

import java.awt.BorderLayout;
import java.awt.Label;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TemperatureConversion extends JFrame{

    JPanel pnlFromRadioButton, pnlToRadioButton, pnlFromTemp, pnlButton;

    // constructor
    public TemperatureConversion(){

        pnlFromRadioButton = new JPanel();
        pnlFromRadioButton.add(new Label("From Panel"));

        pnlToRadioButton = new JPanel();
        pnlToRadioButton.add(new Label("To Panel"));

        pnlButton = new JPanel();
        pnlButton.add(new Label("Buttons Panel"));

        // 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);
    }

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

假设您想要一个 "Enter Temperature: [ ]" 标签显示在不同的 "line" 下 From 按钮下,您的构造函数将更改为:

    public TemperatureConversion(){

        //set a layout manger. You could use grid layout
        //GridLayout gridLayout = new GridLayout(4, 1);
        //Or BoxLayout
        BoxLayout boxLayout = new BoxLayout(getContentPane(), BoxLayout.Y_AXIS); // top to bottom
        setLayout(boxLayout);
        pnlFromRadioButton = new JPanel();
        pnlFromRadioButton.add(new Label("From Panel"));

        //create a panel to hold the desired label
        pnlFromTemp = new JPanel();
        pnlFromTemp.add(new JLabel("Enter Temperature: [ ]"));//add label

        pnlToRadioButton = new JPanel();
        pnlToRadioButton.add(new Label("To Panel"));

        pnlButton = new JPanel();
        pnlButton.add(new Label("Buttons Panel"));

        // add panels to the frame
        //the panel will show in the order added
        add(pnlFromRadioButton);
        add(pnlFromTemp);
        add(pnlToRadioButton);
        add(pnlButton);

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

抱歉,我是论坛的新手,不熟悉代码@c0der 的发帖礼仪。但是使用 GridLayout 解决了我的问题,我想向您展示我所做的,这是一团糟,但就在这里。这是我奇怪的代码,但不知道如何进一步减少它。这就是我想要的样子,因为现在我明白你所说的 "Nest more JPanels and use layout managers" @Hovercraft Full of Eels 的意思了: my temperature program

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, lblToTempbox;
  JRadioButton radFromCelsius, radFromFahrenheit, radFromKelvin;
  JRadioButton radToCelsius, radToFahrenheit, radToKelvin;
  JPanel pnlFromRadioButton, pnlToRadioButton, pnlFrom, pnlTo, pnlButton;
  JPanel pnlEnterTemp, pnlComparableTemp;
  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: ");
    pnlFrom = 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);
    pnlTo = new JPanel();
    pnlEnterTemp = new JPanel();
    pnlComparableTemp = new JPanel();
    pnlFromRadioButton = new JPanel();
    pnlToRadioButton = new JPanel();

    // 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);

    pnlFrom.setLayout(new GridLayout(2, 1));
    pnlFrom.add(pnlFromRadioButton);
    pnlFrom.add(pnlEnterTemp);

    pnlTo.setLayout(new GridLayout(2, 1));
    pnlTo.add(pnlToRadioButton);
    pnlTo.add(pnlComparableTemp);


    // decorate the panel
    pnlFrom.setBorder(BorderFactory.createTitledBorder("From"));
    pnlTo.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
    pnlEnterTemp.add(lblFromTemp);
    pnlEnterTemp.add(txtFromTemp);
    pnlComparableTemp.add(lblToTemp);
    txtToTemp.setEditable(false);
    pnlComparableTemp.add(txtToTemp);

    // add panels to the frame
    add(pnlFrom, BorderLayout.NORTH);
    add(pnlTo, 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() && radToFahrenheit.isSelected()) {
          int strInput = Integer.parseInt(txtFromTemp.getText());
          int celsius = strInput * 9 / 5 + 32;
          txtToTemp.setText(Integer.toString(celsius));
        } else if (radFromCelsius.isSelected() && radToCelsius.isSelected() ||
            radFromFahrenheit.isSelected() && radToFahrenheit.isSelected() ||
            radFromKelvin.isSelected() && radToKelvin.isSelected()) {
         txtToTemp.setText(txtFromTemp.getText());
        } else if (radToCelsius.isSelected() && radFromFahrenheit.isSelected()) {
          int strInput = Integer.parseInt(txtFromTemp.getText());
          int fahrenheit = (strInput - 32) * 5 / 9;
          txtToTemp.setText(Integer.toString(fahrenheit));
        } else if (radFromKelvin.isSelected() && radToCelsius.isSelected()) {
          double strInput = Integer.parseInt(txtFromTemp.getText());
          double celsius = strInput - 273.15;
          txtToTemp.setText(Double.toString(celsius));
        } else if (radFromKelvin.isSelected() && radToFahrenheit.isSelected()) {
          double strInput = Integer.parseInt(txtFromTemp.getText());
          double fahrenheit = strInput - 459.67;
          txtToTemp.setText(Double.toString(fahrenheit));
        } else if (radFromCelsius.isSelected() && radToKelvin.isSelected()) {
          double strInput = Integer.parseInt(txtFromTemp.getText());
          double celsius = strInput + 273.15;
          txtToTemp.setText(Double.toString(celsius));
        } else if (radFromFahrenheit.isSelected() && radToKelvin.isSelected()) {
          double strInput = Integer.parseInt(txtFromTemp.getText());
          double fahrenheit = strInput + 255.37;
          txtToTemp.setText(Double.toString(fahrenheit));
        }
      } else if (e.getSource() == btnExit) {
        System.exit(0);
      }
    }
  }

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

顺便说一下@Hovercraft Full Of Eels,你的解决方案比我的思维水平更高效、更先进。我是一般编程的新手,所以请耐心等待我凌乱的代码和组织大声笑。我几乎没有涉足 OOP。我确实了解您如何将枚举用于 TempScale,感谢您的建议。我会把这些记在笔记里作为参考。