Sportsday using Netbeans Tutorial - tstorrnetnz/teaching2022 GitHub Wiki
How to think like a computer scientist (HTTLACS)
Files for this project in Dr Java
This programme is a good attempt at answering this problem:
.. A programme to record results at a school sports day. It needs to be able to add events, add results and display the results…..
This sort of problem is something similar to an assessment type problem. The answer/solution here is a very good grade. It uses multiple classes, is robust and has arrays. The comments are helpful. The code has extensive comments (using a style that is good for your assessment).
The structure of the programme is summarised in the picture below.
There are 3 Class files SportsDay, Event and SportsDayGUI. The details are above. Data is stored in an arraylist of events (in sportsday). Each event has an arraylist of people for the results. Another way of looking at the overall design is shown in this diagram.
MAKE SURE YOU UNDERSTAND ROUGHLY HOW THE PROGRAM WORK BEFORE PROCEEDING.
The programme will be able to add events, add results and then display them.
It would also have been very useful to incorporate using text files to store the data in (and read/write from/to).
You will need Netbeans open. You should have done the Celcius converter Netbeans task beforehand. THIS IS ESSENTIAL _ DO THIS NOW IF REQUIRED.
- Create a new project - lets call it SportsDay
- Add a new JPanel form to the project and call it SportsDayGUI
Now lets try making the GUI
We are making a 3 tabbed layout. This is made of a tabbed panel with 3 panes.
We will use the Swing containers Panel and Tabbed Pane to do this.
Here’s the JPanel before any changes with Panel and Tabbed Pane visible in the top right.
Drag a Tabbed pane into the JPanel and resize it so that it is larger - to about 800 by 600. This can be done by dragging or in the Jpanel Properties box
Now drag a Panel (above the Tabbed Pane) into the GUI. Tab 1 should appear.
Select the tabbed pane by clicking in the area to the right of tab1. The properties box should say JTabbedPane1, NOT JPanel 1. Then drag another panel ...Tab 2, should appear. Do the same for tab 3 Save your work.
We can now build the GUI further. Tab1 can be renamed by Right clicking on it > edit text.
Do the same for tabs 2 and 3.
Now add: Jlabel, JButton, JCombobox and a ScrollPane (from the containers, top right).
The ScrollPane needs it’s preferred size setting to 600, 800
Drag a JText area into the scrollpane and set the dimensions to 164, 800. A vertical scrollbar should appear.
Now got to tab 2 - Add New Event. We need to add: labels. Comboboxes, Textfield and button (note yet to add the text field on the screenshot above).
The age,gender and Time/Distance fields of the combobox can be set using the properties box, properties tab and clicking on the 3 dots to the right of the section called model (which should have item 1, item 2 etc in there.
Change the values to:
U11 U12 U13 U14 U15 U16 U17 U18 U19
And click ok.
Set the values for the gender combobox to Boys, Girls.
...and the Time/Distance as well.
The writing in the Jtextfield should be removed - Right click on it > edit and delete it.
The GUI should now look as above. Save your work.
Let’s move on to Tab3 Add Event Results.
The top row is made of labels, a combobox and 2 jtextfields. Remove the text from both textfields, and Item 1, Item 2 etc from the combobox using the same techniques as for tab 2.
Add labels as above. We add extra labels for the time & distance variables and toggle the visibility depending on the type of event chosen. This whole way of doing things required a lot of thinking through and was probably the most difficult part of the project.
Now add JSpinners for the 3 fields. JSpinners are useful because it is easy to set min/max numbers on them - and they only accept Numbers. This means that input validation (preventing unacceptable data being entered is easy and lets you get a better grade. Hint - if you need a number as input JSpinners are great!) JSpinners can be found in the Swing Controls window.
Don’t forget to add the save result button
Save your work.
We will now set the max and minimum in the Jspinners.
Here is the documentation for spinners - quite complicated. Spinners can have several ‘models’ applied to them - dates, lists and numbers. We need the number model. Look at this example - scroll down a little to see the three models. The numbermodel documentation tells use that we can set min, max and intervals.
A SpinnerModel for sequences of numbers. The upper and lower bounds of the sequence are defined by properties called minimum and maximum. The size of the increase or decrease computed by the nextValue and previousValue methods is defined by a property called stepSize. The minimum and maximum properties can be null to indicate that the sequence has no lower or upper limit. All of the properties in this class are defined in terms of two generic types: Number and Comparable, so that all Java numeric types may be accommodated. Internally, there's only support for values whose type is one of the primitive Number types:Double, Float, Long, Integer, Short, or Byte.
To create a SpinnerNumberModel for the integer range zero to one hundred, with fifty as the initial value, one could write:
Integer value = new Integer(50);
Integer min = new Integer(0);
Integer max = new Integer(100);
Integer step = new Integer(1);
SpinnerNumberModel model = new SpinnerNumberModel(value, min, max, step);
int fifty = model.getNumber().intValue();
Spinners for integers and doubles are common, so special constructors for these cases are provided. For example to create the model in the previous example, one could also write:
SpinnerNumberModel model = new SpinnerNumberModel(50, 0, 100, 1);
Lets think about this in terms of valid, reasonable inputs.
Spinner 1 | Spinner 2 | Spinner 3 | |
---|---|---|---|
Time | Minutes 0 - 59 inclusive Interval of 1 (minute) | Seconds 0 - 59 inclusive Interval of 1 | Milliseconds 0 - 999 inclusive Interval of 1 |
Distance | Metres 50 Interval of 1.This seems reasonable for a javelin throw - could set more though | Cm 99 - 100 would be a metre. Interval of 1 | Mm 9 - 10 would be a cm Interval of 1 |
Therefore the maximum and minimum will depend on if time or distance is selected. One way of controlling this is to have variables for the spinner max/min/interval and change the variables value when time/distance is selected. By pointing the Jspinners max/min/interval to the relevant variables we can make this work!
Add some variables to hold the min/max intervals. These go just after the variable that you can’t edit. Switch to source view. Just after the variables -the very end of your code, add a few extra lines inside the final } and add:
Notice how the min is always 0, interval 1, value 0 and the max is not yet set.
To make the spinners get the values from these values we need to add some more code. This is done back in the GUI editor/design view.
Select the first spinner and in the properties window for that spinner, choose the code tab. Select the 3 dots … to the right of the pre-creation code field.
In the pop up window put:
resultField1 = new SpinnerNumberModel(spinnerValue,spinnerMin,spinnerMax,spinnerInterval);
(This is one line!)
Next, right click on the spinner itself and select customize code.
Edit the parameter of the spinner so that it holds resultsField1. You will need select custom creation from the drop down on the left.
jSpinner1 = new javax.swing.JSpinner(resultField1);
Finally change the spinner variable name from jSpinner1 to spinner1 in the code properties tab. We do this so that the variable is named similarly to the BlueJ example. Complete all these changes for the other two spinners, spinner 2 and spinner 3. Then add the resultsField1 2 and 3 to the variables.
private SpinnerNumberModel resultField1;
private SpinnerNumberModel resultField2;
private SpinnerNumberModel resultField3;
The drop-down covers resultField2 and 3. The red exclamation mark shows an error. Left clicking on it gives the drop-down. The error is because we don’t have an import for javax.swing.SpinnerNumberModel. We can either add the import (click on it) or prepend (put before) the statement .ie.
private javax.swing.SpinnerNumberModel resultField1;
If you look in the auto-generated code you will see loads of the javax.swing….. This is another way of saying - get from javax.swing…..
The import is easiest.
private Integer spinnerMax;
private Integer spinnerMin = 0;
private Integer spinnerInterval = 1;
private Integer spinnerValue = 0;
private SpinnerNumberModel resultField1;
private SpinnerNumberModel resultField2;
private SpinnerNumberModel resultField3;
Save your work!
You can preview the GUI using the eye icon. Check the comboboxes on tab2. Of course the GUI does not actually respond to any events yet. That’s our next (rather large and complicated step).
Firstly, let’s get the GUI working for real - not preview mode. We are going to create the GUI by creating it with a call from the SportsDay Class (the GUI should have been created in the SportsDayGUI class).
To get to the SportsDay class, go window > projects in the top bar, and double click on the SportsDay file. This will open up the editor for that class.
Copy the main method below and paste it over the main method in the SportsDay class.
// Main method to get things started
public static void main( String args[] )
{
// Create an instance of the gui application
javax.swing.JFrame window = new javax.swing.JFrame("Sportsday");
window.getContentPane().add(new SportsDayGUI());`
window.setVisible(true);
window.pack();
window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
}
What this does: creates a new Jframe window called “Sportsday”, adds an instance of the SportsDayGUI to it ( .add(new SportsDayGUI() ), sets the visibility and sets the close operation.
If all has gone well, clicking the green run arrow will compile the project and run it, firing up your GUI window - for real. The spinners should not be able to go below zero. No maximum is yet set.
Now change the gui variable names so that they make sense - for convenience the same as the variable names used in Bluej. Do this by selecting the button/field/box etc and using the variable name field in the code tab of the properties window to set it correctly. It is essential that they are named precisely. Only the labels that are changed during the time/distance set up are required to be renamed.
Code: tabs that the variables are on are Home, Add New Event (ANE), Add Event Results (AER)
private JButton saveEventButton; ANE
private JButton saveResultButton; AER
private JButton viewResultsButton; Home
private JComboBox ages; ANE
private JComboBox gender; ANE
private JTextField eventTitle; ANE
private JComboBox unit; ANE
private JTextArea textArea; Home
private SportsDay sportsDay = new SportsDay();**//work out what this does**
private JComboBox selectEvent; AER
private JComboBox selectResultsEvent; Home
private JTextField competitorFirstName; AER
private JTextField competitorLastName; AER
private SpinnerNumberModel resultField1; AER //These should have been set previously
private SpinnerNumberModel resultField2; AER //These should have been set previously
private SpinnerNumberModel resultField3; AER //These should have been set previously
private Integer max;
private JLabel metresLabel; AER
private JLabel cmLabel; AER
private JLabel mmLabel; AER
private JLabel minsLabel; AER
private JLabel secsLabel; AER
private JLabel msecsLabel; AER
private Integer spinnerMax;
private Integer spinnerMin = 0;
private Integer spinnerInterval = 1;
private Integer spinnerValue = 0;
Your variables (seen in the code view) should look something like this.
// Variables declaration - do not modify
private javax.swing.JComboBox<String> ages;
private javax.swing.JLabel cmLabel;
private javax.swing.JTextField competitorFirstName;
private javax.swing.JTextField competitorLastName;
private javax.swing.JTextField eventTitle;
private javax.swing.JComboBox<String> gender;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JLabel jLabel7;
private javax.swing.JLabel jLabel8;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JPanel jPanel3;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTabbedPane jTabbedPane1;
private javax.swing.JLabel metresLabel;
private javax.swing.JLabel minsLabel;
private javax.swing.JLabel mmLabel;
private javax.swing.JLabel msecsLabel;
private javax.swing.JButton saveEventButton;
private javax.swing.JButton saveResultButton;
private javax.swing.JLabel secsLabel;
private javax.swing.JComboBox<String> selectEvent;
private javax.swing.JComboBox<String> selectResultsEvent;
private javax.swing.JSpinner spinner1;
private javax.swing.JSpinner spinner2;
private javax.swing.JSpinner spinner3;
private javax.swing.JTextArea textArea;
private javax.swing.JComboBox<String> unit;
private javax.swing.JButton viewResultsButton;
// End of variables declaration
//My variables
private Integer spinnerMax;
private Integer spinnerMin = 0;
private Integer spinnerInterval = 1;
private Integer spinnerValue = 0;
private SpinnerNumberModel resultField1;
private SpinnerNumberModel resultField2;
private SpinnerNumberModel resultField3;
We will now set the visibility of the Metres/cm/sec labels to default to off on start up. Go to tab3, and Right click on the Metres label. Choose customize code and add this code:
metresLabel.setVisible(false);
Just pasting it in the space provided should add the post adding label. Repeat this for the cm and mm labels. Check this has worked by running the program (green arrow).
Tab 3 should have the M/cm/mm labels missing.
We will now try to get the code working for adding an event.
To do this we need to create an event class, have an array of events and methods to handle them in the SportsDay class, have event handlers and listeners in the SportsDayGUI class. Quite a lot to do!
Create a new Event Class by R clicking on the SportsDay package > new > java Class. Name the class Event.
From this existing event class, copy all the code, including the comment and import statements and paste it over the constructor. The package sportsday; line will be the only original code. Save your work. The Event class contains a private inner person class and an arraylist for person objects. Each instance of an event (eg boys 100m contains an arraylist of person results. This means results from one event cannot be mixed up with results from another event. The methods for getting the results are also included in the event class e.g. getResults(), addCompetitor() etc.
We will now get the right code in our SportsDay Class. At the moment, it has just one method, main() that calls the SportsDayGUI. From this example of a Sportsday class, copy the following methods into our netbeans version:
public String getEventType(String eventName)
public void addResult(String eventName, String fName, String lName, Double result)
public void createNewEvent(String eventName, String eventUnit)
public Event getEvent(String name)
And, just beneath public class SportsDay { (line 12) add the arraylist variable and constructor.
private ArrayList<Event> sportsDayEvents = new ArrayList<Event>();
public SportsDay()
{
}
Add the import for the arraylist by clicking on the red ! and selecting import. Your code should be the same as in this listing. Check it.
Save your work.
We now have a gui, an Event class and a SportsDay class with an arraylist for events, with methods to handle the events. We now need to make the gui work. We will need to add EventHandlers. Listeners and logic. We will do this for adding an event.
In this version of SportsdayGUI.java, the event handling is largely done by an inner event handling class. This goe from about lines 383 - 584 including comments (It is essential to read these comments!). It stops one } before the end of the code. Copy this and paste it into the same place in your Netbeans SportsDay GUI, again 1 } before the end of the code.
You may need to do some tidying up! (if these don't appear, lucky you!)
At the first red ! add an import for the actionlistener.
At the second red ! and an import for action event.
Where Jframes are used you can add an import for Jframe, similarly for Joptionpane.
Copy the check string length method from this version and paste it just above the inner event handling class (so it is just beneath your variables.) This should reduce the number of red ! a bit as this method is used in the logic a fair bit.
//String length method from below: /** * Method to validate length of a string to see if it fits between the min and max lengths. * @PARAM String inputString The actual string that is being tested. * @PARAM Integer minLength The minimum length of the string that will pass the test. * @PARAM Integer maxLength The maximum length of the string that will pass the test. * @RETURN Boolean True if the input string is greater than or equal to minLength and less than or equal to maxLength, otherwise, false. * * This method checks a strings length and returns boolean true or false if it passes the min/max length test. It is used for validating name and event string * length. */
public Boolean checkStringLength( String inputString, Integer minLength, Integer maxLength )
{
int stringLength = inputString.length();
if (stringLength >= minLength && stringLength <= maxLength){
System.out.println("pass");
return true;
}
else{
System.out.println("fail");
return false;
}
}
At the moment, events for each button/action have their own methods associated with them. For example find these: private void viewResultsButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}
private void saveEventButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}
private void eventTitleActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}
private void competitorFirstNameActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
}
private void saveResultButtonActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
We will remove these and add our own event handling.
Using design mode go to tab3 and click on the save result button. It’s variable name should be saveResultButton. In the events tab of the properties window, click on the 3 …
and do remove > OK to get rid of the handler method. Repeat for the others (use the list above, your may be slightly different - but ensure you have no methods left.).
We now need to add event listeners to each button.
This is done in the constructor, which at the moment, (in Netbeans) should look like (line 23 or 24 ish)
public SportsDayGUI() {
initComponents();
}
We add the listeners and listener code into there. Make you SportsDayGUI the same as below.
public SportsDayGUI() {
initComponents();
EventListener listener = new EventListener();
EventListener listenera = new EventListener();
EventListener listenerb = new EventListener();
saveEventButton.addActionListener(listener);
selectResultsEvent.addActionListener(listenerb);
viewResultsButton.addActionListener(listenerb);
selectEvent.addActionListener(listenera);
saveResultButton.addActionListener(listenera);
}
This creates 3 event listeners -1 for each tab - and attaches them to the buttons.
As the SportsDay Class object is actually created/called from SportsDayGUI, we need to add SportsDay as variable:
private SportsDay sportsDay = new SportsDay();
Your variables should now look like above.
This code should now work - try saving and running the programme - adding events and results and displaying them.
Notice how the Jspinner min/max is working and how the labels change from time/distance correctly!