一段时间以来一直在研究一台BMR计算器。创建了一个界面,并通过不同的方式存储用户输入,不同的方法计算BMR/TDEE等。
我为计算创建了另一个名为User的类(如某人所建议的)。通过这种方式,我可以将用户的数据与使用这些数据的任何方法一起打包。
任何批评都是值得赞赏的。谢谢
/**
* @author "Faizan Tahir"
* @title "BMR Calculator"
* A program to calculate a users BMR (Basal metabolic rate) and TDEE (Total Daily Energy Expenditure).
* Allows user to select how they would like to input their information (e.g. weight in kg or cm),
* Calculates users TDEE given an activity level. Basic and straightforward to use. User does not need to convert
* to their standard unit of measure, this calculator does that for them.
* Future updates: Allow user to save their information so as to track fitness progress
**/
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.util.HashMap;
public class BmrMain extends JFrame {
// Declarations for gender/weight/height and other variables
HashMap<String, Double> activityMap;
String[] activityLevels = {"Sedentary", "Lightly Active", "Moderately Active", "Very Active", "Extra Active"};
int userAge;
String userGender;
double userHeight;
double userWeight;
double userActivityLevel;
public BmrMain(String title) {
// Main JFrame
setTitle(title);
JPanel mainPanel = new JPanel();
// All JPanel declarations
JMenuBar menuBar = new JMenuBar();
JPanel imgPanel = new JPanel();
JPanel agePanel = new JPanel();
JPanel genderPanel = new JPanel();
JPanel heightPanel = new JPanel();
JPanel weightPanel = new JPanel();
JPanel weightHeightPanel = new JPanel(new BorderLayout());// combines genderPanel, agePanel, heightPanel, weightPanel - this
// is a BorderLayout, whereas gender/agePanel are FlowLayouts.
JPanel combinedGAHWpanel = new JPanel(new BorderLayout()); // Combines weight and height panel into out flowlayout panel, which is then combined into the above borderlayout panel
JPanel tdeeBMRPanel = new JPanel(new BorderLayout());
JPanel tdeePanel = new JPanel();
JPanel activityLevelPanel = new JPanel();
JPanel bmrTDEEValuesPanel = new JPanel(new BorderLayout());
// Image panel declaration
JLabel imgLabel = new JLabel(new ImageIcon(getClass().getResource("/mainlogo2.png")));
JLabel activityLevelHelp = new JLabel(new ImageIcon(getClass().getResource("/question-mark.png")));
imgPanel.add(imgLabel);
// Activity level map
initializeActivityLevelMap();
// JPanel layout managers
agePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
genderPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
// Menu JComponents
JMenu saveMenu = new JMenu("Save");
JMenu optionMenu = new JMenu("Options");
JMenu helpMenu = new JMenu("Help");
menuBar.add(saveMenu);
menuBar.add(optionMenu);
menuBar.add(helpMenu);
// Age JComponents
JLabel ageLabel = new JLabel("Age:");
JLabel yearsLabel = new JLabel("<html><i>years</i><html>");
JTextField ageField = new JTextField(4);
agePanel.add(ageLabel);
agePanel.add(ageField);
agePanel.add(yearsLabel);
// Gender JComponents
JLabel genderLabel = new JLabel("Gender:");
JRadioButton genderMale = new JRadioButton("Male", true);
JRadioButton genderFemale = new JRadioButton("Female");
genderPanel.add(genderLabel);
genderPanel.add(genderMale);
genderPanel.add(genderFemale);
ButtonGroup genderGroup = new ButtonGroup(); // groups male and female radio buttons together so that only one can be selected
genderGroup.add(genderMale);
genderGroup.add(genderFemale);
// Height JComponents
JLabel heightLabel = new JLabel("Height:");
JTextField height1Field = new JTextField(3);
JTextField height2Field = new JTextField(3);
JLabel height1Label = new JLabel("m");
JLabel height2Label = new JLabel("cm ");
JToggleButton cmButton = new JToggleButton("cm", true);
JToggleButton feetButton = new JToggleButton("feet");
heightPanel.add(heightLabel);
ButtonGroup heightGroup = new ButtonGroup();
heightGroup.add(cmButton);
heightGroup.add(feetButton);
heightPanel.add(height1Field);
heightPanel.add(height1Label);
heightPanel.add(height2Field);
heightPanel.add(height2Label);
heightPanel.add(cmButton);
heightPanel.add(feetButton);
cmButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {;
height1Label.setText("m");
height2Label.setText("cm ");
}
});
feetButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
height1Label.setText("ft");
height2Label.setText("inch");
}
});
// Weight JComponents
JLabel weightLabel = new JLabel("Weight:");
JTextField weightField = new JTextField(4);
JToggleButton kgButton = new JToggleButton("kg", true);
JToggleButton lbButton = new JToggleButton("lbs");
weightPanel.add(weightLabel);
weightPanel.add(weightField);
weightPanel.add(kgButton);
weightPanel.add(lbButton);
ButtonGroup weightGroup = new ButtonGroup();
weightGroup.add(kgButton);
weightGroup.add(lbButton);
// tdee JComponents
JLabel tdeeQuestionLabel = new JLabel("Calculate TDEE Also?");
JRadioButton tdeeYes = new JRadioButton("Yes");
JRadioButton tdeeNo = new JRadioButton("No", true);
ButtonGroup tdeeButton = new ButtonGroup();
tdeeButton.add(tdeeYes);
tdeeButton.add(tdeeNo);
tdeePanel.add(tdeeQuestionLabel);
tdeePanel.add(tdeeYes);
tdeePanel.add(tdeeNo);
// activitylevel JComponents
JLabel activityLevelLabel = new JLabel("Activity Level: ");
JComboBox activityLevelBox = new JComboBox(activityLevels);
activityLevelBox.setSelectedIndex(0);
activityLevelPanel.add(activityLevelLabel);
activityLevelPanel.add(activityLevelBox);
activityLevelPanel.add(activityLevelHelp);
activityLevelBox.setEnabled(false);
activityLevelHelp
.setToolTipText("<html><b>Sedentary:</b> little or no exercise, deskjob<<br /><b>Lightly Active:</b> little exercise/sports 1-3 days/week<br /><b>Moderately active:</b> moderate exercise/sports 3-5 days/week<br /><b>Very active:</b> hard exercise or sports 6-7 days/week<br /><b>Extra active:</b> hard daily exercise or sports & physical labor job </html>");
ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE);
tdeeYes.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
activityLevelBox.setEnabled(true);
}
});
tdeeNo.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
activityLevelBox.setEnabled(false);
}
});
// tdee and BMR value components
JLabel bmrLabel = new JLabel();
JLabel tdeeLabel = new JLabel();
JButton calculate = new JButton("Calculate");
JPanel calcPane = new JPanel();
calcPane.add(calculate);
bmrTDEEValuesPanel.add(calcPane, BorderLayout.NORTH);
bmrTDEEValuesPanel.add(bmrLabel, BorderLayout.CENTER);
bmrTDEEValuesPanel.add(tdeeLabel, BorderLayout.SOUTH);
// After the user presses the calculate button, error checking is done to ensure correct input is given (using regular expressions).
// If error checking passes, user input is stored in variables which is then passed to the calcBMR method.
// The output from calcBMR is then passed into calcTDEE (if user selects this option)
calculate.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (!ageField.getText().matches("[0-9]+")) {
JOptionPane.showMessageDialog(null, "Integer values only please.", "INCORRECT AGE INPUT", JOptionPane.WARNING_MESSAGE);
} else if (!weightField.getText().matches("[0-9]+(.[0-9]+)?")) {
JOptionPane.showMessageDialog(null, "Decimal/integer values only please.", "INCORRECT WEIGHT INPUT", JOptionPane.WARNING_MESSAGE);
} else if (cmButton.isSelected() && (!height1Field.getText().matches("[0-9]") || !height2Field.getText().matches("[0-9]([0-9]?)"))) {
JOptionPane.showMessageDialog(null, "Integer value for metre and cm field only please. Enter a value between 0 and 99 for cm field", "INCORRECT HEIGHT INPUT", JOptionPane.WARNING_MESSAGE);
} else if (feetButton.isSelected() && (!height1Field.getText().matches("[0-9]") || !height2Field.getText().matches("[0-9]|10|11"))) {
JOptionPane.showMessageDialog(null, "Integer value for feet and inch field only please. Enter a value between 0-11 for inches field", "INCORRECT HEIGHT INPUT", JOptionPane.WARNING_MESSAGE);
} else {
userAge = Integer.parseInt(ageField.getText());
// conversions done from lb to kg and feet/inches to cm below.
userWeight = kgButton.isSelected() ? Double.parseDouble(weightField.getText()) : Double.parseDouble(weightField.getText()) * 0.453592;;
userHeight = cmButton.isSelected() ? Double.parseDouble(height1Field.getText())*100 + Double.parseDouble(height2Field.getText()) :
(Double.parseDouble(height1Field.getText())*12 + Double.parseDouble(height2Field.getText())) * 2.54;
userGender = genderMale.isSelected() ? "M" : "F";
}
userActivityLevel = tdeeYes.isSelected() ? activityMap.get(activityLevelBox.getSelectedItem()) : 1.0; // Looks up selected item from combo box, which is the KEY. Then looks up the value to this key from the map - this value is the TDEE multiplier.
User user1 = new User(userAge, userGender, userHeight, userWeight, userActivityLevel); // An object of type Calculations needs to be created to access instance methods in Calculations class - objectReference.methodName(); syntax is being used
bmrLabel.setText("<html><br /><font size=4>You have a <i><font color=#ce0000>BMR</font></i> of: " + "<font color=#59AF0E>" + user1.getBMR() + "</font></html>");
if (tdeeYes.isSelected()) {
tdeeLabel.setText("<html><br /><font size=4>You have a <i><font color=#ce0000>TDEE</font></i> of: " + "<font color=#59AF0E>" + user1.getTDEE() + "</font></html>");
} else {
tdeeLabel.setText(""); // Used to clear text if user chooses to calculate TDEE first time but chooses not to calculate second time and thereafter
}
User user2 = new User(20, "M", 140.0, 70.5, 1.725);
System.out.println("############### AGE ###############");
System.out.println(user1.getAge());
System.out.println(user2.getAge());
System.out.println("############### WEIGHT ###############");
System.out.println(user1.getWeight());
System.out.println(user2.getWeight());
System.out.println("############### HEIGHT ###############");
System.out.println(user1.getHeight());
System.out.println(user2.getHeight());
System.out.println("############### GENDER ###############");
System.out.println(user1.getGender());
System.out.println(user2.getGender());
System.out.println("############### MULT ###############");
System.out.println(user1.getActivityMultiplier());
System.out.println(user2.getActivityMultiplier());
System.out.println("############### BMR ###############");
System.out.println(user1.getBMR());
System.out.println(user2.getBMR());
System.out.println("############### TDEE ###############");
System.out.println(user1.getTDEE());
System.out.println(user2.getTDEE());
}
});
// Adding sub JPanels to main JPanel
mainPanel.add(imgPanel);
combinedGAHWpanel.add(agePanel, BorderLayout.NORTH); // Combine genderPanel and agePanel (which are both flowLayouts) into a
// single BorderLayout panel where agePanel is given the Northern spot and
// genderPanel is given the center spot in the panel
weightHeightPanel.add(weightPanel, BorderLayout.NORTH); // Nested borderlayouts, the weightHeightPanel is another borderLayout which is nested
// into the southern position of the combinedGAHW border layout.
weightHeightPanel.add(heightPanel, BorderLayout.CENTER);
weightHeightPanel.add(tdeeBMRPanel, BorderLayout.SOUTH);
combinedGAHWpanel.add(genderPanel, BorderLayout.CENTER);
combinedGAHWpanel.add(weightHeightPanel, BorderLayout.SOUTH);
mainPanel.add(combinedGAHWpanel);
// adding to tdeeBMRPanel
tdeeBMRPanel.add(tdeePanel, BorderLayout.NORTH);
tdeeBMRPanel.add(activityLevelPanel, BorderLayout.CENTER);
tdeeBMRPanel.add(bmrTDEEValuesPanel, BorderLayout.SOUTH);
// Adding main JPanel and menubar to JFrame
setJMenuBar(menuBar);
add(mainPanel);
}
public void initializeActivityLevelMap() {
activityMap = new HashMap<String, Double>();
activityMap.put("Sedentary", 1.2);
activityMap.put("Lightly Active", 1.375);
activityMap.put("Moderately Active", 1.55);
activityMap.put("Very Active", 1.725);
activityMap.put("Extra Active", 1.9);
}
public static void main(String[] args) {
BmrMain gui = new BmrMain("BMR/TDEE Calculator");
/*invokes the constructor above with the specified title parameter, the constructor creates the main frame with the setTitle(title)
line which calls the setTitle method from the JFrame superclass (as we used "extends JFrame" when creating this class)
i.e, we create a bmrCalc object called gui using the non-default constructor (as we pass a title parameter, specifically "BMR/TDEE Calculator",
and this constructor also creates the main frame we are working in.
*/
gui.setVisible(true);
gui.setSize(330, 465);
gui.setResizable(false);
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}以及将用户信息和计算方法组合在一起的类:
public class User {
private int age;
private String gender; // todo: use an Enum
private double height; // height stored in cm, weight in kg (so if user enters in feet/lbs, conversions are done to cm/kg and *THEN* passed through to constructor below)
private double weight;
private double activityMultiplier; // todo: use an Enum (possibly)
private int bmr;
private int tdee;
static final int MALEOFFSET = 5; // Miffin St-Jeor equation is: (10 * weight (kg)) + (6.25 * height (cm)) + (5 * age) + OFFSET, where offset is 5 for males, -161 for females.
static final int FEMALEOFFSET = -161;
// This constructor is called everytime the program is run, used to create a User object to pack all user information together.
//This information comes from the UI which is coded in BMRMain
public User(int age, String gender, double height, double weight, double activityMultiplier) {
this.age = age;
this.gender = gender;
this.height = height;
this.weight = weight;
this.activityMultiplier = activityMultiplier;
bmr = calcBMR();
tdee = calcTDEE(bmr); // Calculates and stores tdee regardless of user selecting the option, but does not display if user did not select the option
}
/**
* If user input is correct, this method will calculate the BMR value of the user given their input and measurement choices.
*
* @param None
* @return BMR Value
*/
private final int calcBMR() {
int offset = gender.equals("M") ? MALEOFFSET : FEMALEOFFSET;
// This is the body of the calculations - different offset used depending on gender. Conversions to kg and cm done earlier so no conversions needed here.
// The formula for male and female is similar - only the offset is different.
return (int) (Math.round((10 * weight) + (6.25 * height) - (5 * age) + offset)); // This is the Miffin St-Jeor formula, calculations done in cm/kg
}
/**
* If the user selects the TDEE option, this method will be executed after the calcBMR() method.
* A value from the calcBMR() method will be passed down to this method, and is multiplied
* by the activity level parameter passed into this method.
*
* @param bmr (output from calcBMR() method
* @return TDEE Value
*/
private final int calcTDEE(int bmr) {
return (int) Math.round(bmr * activityMultiplier);
}
// calcBMR() and calcTDEE() can both be private as they are only called within this
// class (constructor), and we then use getBMR() and getTDEE() to access the returned values.
public int getAge() {
return age;
}
public String getGender() {
return gender;
}
public double getHeight() {
return height;
}
public double getWeight() {
return weight;
}
public double getActivityMultiplier() {
return activityMultiplier;
}
public int getBMR() {
return bmr;
}
public int getTDEE() {
return tdee;
}
}发布于 2015-09-03 02:56:00
JFrame mainFrame;
此变量从未使用,可以删除。
JPanel mainPanel;
您不需要这个字段是一个对象字段(有时称为实例字段)。它足以成为一个局部变量。也就是说。
mainPanel = new JPanel();
可能是
JPanel mainPanel = new JPanel();这对于大多数对象字段都是正确的。
将其保持在局部变量中的优点是它减少了范围和可见性。注意,如果有人将另一个类添加到同一个包中,他们将能够看到并使用原始定义更改mainPanel。这可能会引起混淆,这就是为什么我们有范围和可见性限制。
作为经验法则,您应该尽可能地限制范围。
我会在声明中初始化变量,除非有什么理由需要将变量初始化为不同的值。例如,不同的构造函数或基于方法参数的构造函数。请注意,如果要初始化集合变量(例如Map),则在许多情况下必须分别添加元素。
private JTextField ageField;
这有点不同。因为您在内部方法中使用它,所以您必须做一些事情来使它超出局部变量的范围。让它成为一个对象字段就可以做到这一点。另一种选择是
ageField = new JTextField(4);
变成最后一个变量。
final JTextField ageField = new JTextField(4);如果您确实将其保留为对象字段,则private是正确的可见性。
bmrValuePanel = new JPanel(); tdeeValuePanel = new JPanel();
这些也是从来没有使用和可以删除。
// Activity level map activityMap = new HashMap<String, Double>(); activityMap.put("Sedentary", 1.2); activityMap.put("Lightly Active", 1.375); activityMap.put("Moderately Active", 1.55); activityMap.put("Very Active", 1.725); activityMap.put("Extra Active", 1.9);
与其使用注释来命名它,不如考虑创建一个方法
private void initializeActivityValues() {
activityValues = new HashMap<String, Double>();
activityValues.put("Sedentary", 1.2);
activityValues.put("Lightly Active", 1.375);
activityValues.put("Moderately Active", 1.55);
activityValues.put("Very Active", 1.725);
activityValues.put("Extra Active", 1.9);
}这样做的好处还在于缩短了原始方法,特别是当您对其他部分做同样的事情时。
我还改了名字。你也许能做得更好。我没想弄清楚这些数字是什么意思。
https://codereview.stackexchange.com/questions/102557
复制相似问题