Java:每次使用 JTextField 数组返回 0 计算平均值



我正在尝试编写一个Java GUI程序来计算用户定义的课程数的平均值,但是,每当我尝试这样做时,数组似乎都是空的,例如:System.out.println(gTextFields[0]);,我无法弄清楚问题是什么,我希望有人可以提供帮助。

这是我的代码:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Swinggg extends JFrame {
double course1 = 90;
double course2 = 80;

private JLabel numOfCoursesLabel;
private JTextField numOfFallCourses;
private JButton clickButton;
private JButton calcAvg;
private JLabel label;
private JTextField textField;
private JLabel gLabel;
private JTextField gTextField;
public Swinggg() {
super("Title");
setLayout(null);
// Setting up the label
numOfCoursesLabel = new JLabel("Please enter the number of courses that you completed in Fall 17/18: ");
numOfCoursesLabel.setFont(new Font("Tahoma", Font.BOLD, 15));
Dimension labelSize = numOfCoursesLabel.getPreferredSize();
numOfCoursesLabel.setBounds(25, 20, labelSize.width, labelSize.height);
add(numOfCoursesLabel);
// Setting up the text field
numOfFallCourses = new JTextField(20);
Dimension textFieldSize = numOfFallCourses.getPreferredSize();
numOfFallCourses.setBounds(130, 60, textFieldSize.width, textFieldSize.height + 10);
numOfFallCourses.setFont(new Font("Tahoma", Font.BOLD, 18));
add(numOfFallCourses);
// Setting up the click button
clickButton = new JButton("Click");
Dimension buttonSize = clickButton.getPreferredSize();
clickButton.setBounds(360, 55, buttonSize.width + 20, buttonSize.height + 10);
clickButton.setFont(new Font("Tahoma", Font.BOLD, 16));
add(clickButton);
// Setting up the calculate average button
calcAvg = new JButton("Calculate Average");
Dimension calcAvgSize = calcAvg.getPreferredSize();
calcAvg.setBounds(180, 100, 200, 40);
calcAvg.setFont(new Font("Tahoma", Font.BOLD, 16));
add(calcAvg);
TheHandler handler = new TheHandler();
clickButton.addActionListener(handler);
calcAvg.addActionListener(handler);
numOfFallCourses.addActionListener(handler);
}

private class TheHandler implements ActionListener {
public void actionPerformed(ActionEvent event) {
int theNumOfCourses = 0;
int yDynamicPositioning = 120;
JTextField[] textFields = new JTextField[theNumOfCourses];
JTextField[] gTextFields = new JTextField[theNumOfCourses];
if (event.getSource() == clickButton) {
theNumOfCourses = Integer.parseInt(numOfFallCourses.getText());
JLabel[] labels = new JLabel[theNumOfCourses];
textFields = new JTextField[theNumOfCourses];
JLabel[] gLabels = new JLabel[theNumOfCourses];
gTextFields = new JTextField[theNumOfCourses];
for (int i = 0; i < labels.length; i++) {
yDynamicPositioning = yDynamicPositioning + 35;
// Course name labels creation
label = new JLabel("Course Name " + i);
Dimension labelSize = label.getPreferredSize();
label.setFont(new Font("Tahoma", Font.BOLD, 15));
label.setBounds(25, yDynamicPositioning, labelSize.width + 40, labelSize.height);
add(label);
// Course name text fields creation
textField = new JTextField(10);
textField.setFont(new Font("Tahoma", Font.BOLD, 15));
Dimension textFieldSize = textField.getPreferredSize();
textField.setBounds(160, yDynamicPositioning, textFieldSize.width, textFieldSize.height + 2);
textFields[i] = textField;
add(textField);
// Course grades labels creation
gLabel = new JLabel("Course Grade " + i);
Dimension gLabelSize = gLabel.getPreferredSize();
gLabel.setFont(new Font("Tahoma", Font.BOLD, 15));
gLabel.setBounds(300, yDynamicPositioning, gLabelSize.width + 40, gLabelSize.height);
add(gLabel);
// Course grades text fields creation
gTextField = new JTextField(10);
gTextField.setFont(new Font("Tahoma", Font.BOLD, 15));
Dimension gTextFieldSize = gTextField.getPreferredSize();
gTextField.setBounds(440, yDynamicPositioning, gTextFieldSize.width, gTextFieldSize.height + 2);
gTextFields[i] = gTextField;
add(gTextField);

}
calcAvg.setBounds(210, yDynamicPositioning + 50, 200, 40);
System.out.println(gTextFields[0].getText());
clickButton.setVisible(false);
setSize(650, 250 + (labels.length * 40) + 40);
validate();
revalidate();
repaint();
} else if (event.getSource() == calcAvg) {
int counter = 0;
double sum = 0;
try {
while (counter < gTextFields.length) {
sum = sum + Double.parseDouble(gTextFields[counter].getText());
System.out.println(gTextFields[0]);
counter++;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Exception occurred!");
}
double avg = ((course1 + course2) + sum) / (2 + gTextFields.length);
System.out.println(sum);
}
}
}
public static void main(String[] args) {
Swinggg objct = new Swinggg();
objct.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
objct.setSize(600, 250);
objct.setVisible(true);
}
}

每次调用侦听器时,它都会创建一个全新的JTextFields 数组(实际上是两个新数组),因此您尝试从空的 JTextFields 中提取数据。

总而言之,这就是你的听众正在做的事情:

public void actionPerformed(ActionEvent event) {
// create two arrays of JTextField, textFields and gTextFields
// this happens **every time the listener is called!**
if (event.getSource() == clickButton) {
// fill the array with objects and put them into the GUI
} else if (event.getSource() == calcAvg) {
// extract data from the JTextField arrays, but **not** the displayed array of fields
// rather of a new array that has never been added to the GUI
}
}

相反,将数组声明为类的字段,创建 JTextFields 并将它们放在构造函数中,而不是在侦听器中。使用侦听器从中提取数据。如果要在创建程序时推迟显示 JTextField 网格,仍应创建网格,将其放入代码构造函数中的 JPanel 中,然后在 ActionListener 中使用 CardLayout 将网格交换到视图中。


编辑

您声明:

我不认为我能做到这一点,因为数组大小取决于按下 clickButton 后的用户输入

然后将空数组声明为字段,并在侦听器中填充它们一次。 但不要在侦听器中多次创建数组。您需要注意执行此操作。您可以检查空值以防止这种情况发生。

例如,

//  fields declared in *class* and are null:
private JTextField[] textFields;
private JTextField[] gTextFields;
private class TheHandler implements ActionListener 
public void actionPerformed(ActionEvent event) {
if (event.getSource() == clickButton) {
// get user input
if (textFields == null && gTextFields == null) {
textFields = new JTextField[someInt];
gTextFields = new JTextField[someInt];
// use for loop to fill arrays and place into GUI
}
} else if (event.getSource() == calcAvg) {
// extract data from the JTextField arrays,
}
}
}

更好的是:使用List<JTextField> textFields = new ArrayList<>();。这样,您无需提前知道数组的大小。

关于以下方面的旁注:setLayout(null);

虽然空布局和setBounds()在 Swing 新手看来似乎是创建复杂 GUI 的最简单和最好的方法,但您创建的 Swing GUI 越多,您在使用它们时会遇到的困难就越严重。当 GUI 调整大小时,它们不会调整组件的大小,它们是增强或维护的皇家女巫,它们在滚动窗格中放置时完全失败,在所有平台上查看时看起来很糟糕或屏幕分辨率与原始平台不同。


编辑

请注意,如果这是我的程序,我会尝试使用 JTable 来保存我的课程名称/课程成绩数据,并为我进行平均计算,从而进一步简化它。例如:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
@SuppressWarnings("serial")
public class Swinggg2 extends JPanel {
// default table model holds no data, but displays the column header names, course/grade
private MyTableModel tableModel = new MyTableModel(0);
private JTable myTable = new JTable(tableModel);
// get the number of courses in a JSpinner, from 1 to 100
private JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
private JTextField averageField = new JTextField(10);
public Swinggg2() {
// create our GUI
// first the top JPanel to get the number of courses
JPanel courseNumberPanel = new JPanel();
courseNumberPanel.add(new JLabel("Please enter the number of courses that you completed in Fall 17/18:"));
courseNumberPanel.add(spinner);
courseNumberPanel.add(new JButton(new AbstractAction("Show Courses") {
@Override
public void actionPerformed(ActionEvent e) {
int rowCount = (int) spinner.getValue();
tableModel = new MyTableModel(rowCount);
myTable.setModel(tableModel);
}
}));
// then the JScrollPane to hold the JTable
JScrollPane scrollPane = new JScrollPane(myTable);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// then create the bottom JPanel to calculate and display the average grade
averageField.setEditable(false);
JPanel calcAveragePanel = new JPanel();
calcAveragePanel.add(new JLabel("Calculated Average Grade:"));
calcAveragePanel.add(averageField);
calcAveragePanel.add(new JButton(new AbstractAction("Calculate Average") {
@Override
public void actionPerformed(ActionEvent e) {
// if user is still editing a cell, STOP editing
if (myTable.isEditing()) {
myTable.getCellEditor().stopCellEditing();
}
// extract the average value from the table model and display it
double average = tableModel.getAverageGrade();
String averageText = String.format("%.3f", average);
averageField.setText(averageText);
}
}));
// give the JPanel a layout and add components to it, top, center, bottom
int gap = 3;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(courseNumberPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
add(calcAveragePanel, BorderLayout.PAGE_END);
}
// create and show the GUI
private static void createAndShowGui() {
Swinggg2 mainPanel = new Swinggg2();
JFrame frame = new JFrame("Course Grade Average");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
// do everything on the Swing event thread
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

// each row of our JTable holds a Course object's data
class Course {
private String name;
private double grade;
public Course(String name, double grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public double getGrade() {
return grade;
}
// hashcode and equals based on name (or courseNumber)
}

// table model to hold and display our course data
@SuppressWarnings("serial")
class MyTableModel extends DefaultTableModel {
// Table's column header Strings
public static final String[] COL_NAMES = {"Course Name", "Course Grade"};
public MyTableModel(int rowCount) {
super(COL_NAMES, rowCount);
}
@Override
public int getColumnCount() {
return COL_NAMES.length;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 1) {
return Double.class;
}
return super.getColumnClass(columnIndex);
}
public void addRow(Course course) {
Object[] rowData = {course.getName(), course.getGrade()};
super.addRow(rowData);
}
public Course getRow(int rowIndex) {
//get data, handling nulls carefully
String name = (String) getValueAt(rowIndex, 0);
name = name == null? "" : name;
Object colObject = getValueAt(rowIndex, 1);
double grade = colObject == null ? 0.0 : (double) colObject;
return new Course(name, grade);
}

public double getAverageGrade() {
// if no rows, either return 0.0 
if (getRowCount() == 0) {
return 0.0;  // ? or throw an exception ?
}
// summation variable
double total = 0.0;
for (int i = 0; i < getRowCount(); i++) {
// iterate through the table extracting grades and adding to total
total += getRow(i).getGrade();
}
// calculate and return the average
return total / getRowCount();
}
}

最新更新