Sportsday using Netbeans Tutorial - tstorrnetnz/teaching2022 GitHub Wiki

How to think like a computer scientist (HTTLACS)

Otago Workbook L2

Otago Workbook L3

Files for this project

Files for this project in Dr Java

Learning Aim: Be able to make a more complex GUI using Netbeans IDE

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.

classes

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.

classes2

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.

INSTRUCTIONS

  1. Create a new project - lets call it SportsDay
  2. 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.

img21

Here’s the JPanel before any changes with Panel and Tabbed Pane visible in the top right.

img24

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

img5

Now drag a Panel (above the Tabbed Pane) into the GUI. Tab 1 should appear.

img17

img44

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.

img36

Do the same for tabs 2 and 3.

img41

Now add: Jlabel, JButton, JCombobox and a ScrollPane (from the containers, top right).

img7

The ScrollPane needs it’s preferred size setting to 600, 800

img43

Drag a JText area into the scrollpane and set the dimensions to 164, 800. A vertical scrollbar should appear.

img16

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).

img2

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.

img6

Change the values to:

U11 U12 U13 U14 U15 U16 U17 U18 U19

And click ok.

img3

Set the values for the gender combobox to Boys, Girls.

img45

...and the Time/Distance as well.

The writing in the Jtextfield should be removed - Right click on it > edit and delete it.

img20

The GUI should now look as above. Save your work.


Let’s move on to Tab3 Add Event Results.

img13

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.

img30

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.

img12

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.

img29

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:

img32

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.

img22

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.

img40

In the pop up window put:

resultField1 = new SpinnerNumberModel(spinnerValue,spinnerMin,spinnerMax,spinnerInterval);

(This is one line!)

img19

Next, right click on the spinner itself and select customize code.

img34

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

img23

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;

img10

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!


img26

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).

img38

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.

img15

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.

img8

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;

img46

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).

img39

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!

img25

Create a new Event Class by R clicking on the SportsDay package > new > java Class. Name the class Event.

img31

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

    {
    }

img28

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!)

img4

At the first red ! add an import for the actionlistener.

img 27

At the second red ! and an import for action event.

img11

Where Jframes are used you can add an import for Jframe, similarly for Joptionpane.

img14

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.

img1 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 …

img18

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

img37

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!

The complete code listing for SportsDayGUI is here.

⚠️ **GitHub.com Fallback** ⚠️