14: Input and Output

What We Will Cover


Illuminations

Questions from last class or the Reading?

Homework Questions?

Final Boss Event Approaching

  • Final Examinations Schedule
  • Final exams must be taken at the scheduled time
  • If you need the proctoring center, schedule it two weeks early

14.1: Worlds and Files

Learner Outcomes

At the end of the lesson the student will be able to:

  • Read text files with a loop
  • Read and display a map file in a tile world

14.1.1: Review of Files and Streams

  • Sometimes we want to save data after our scenario finishes
  • For instances, we could store a high score list of players
  • Sometimes we want to use stored data so it is easier to make changes to scenarios
  • For example, we can read information from files to create tile maps
  • Reading a tile map from a file would let us change maps without recompiling
  • In addition, multiple tile map files to allow us to more easily create multiple levels in a scenario

Types of Files

  • All data in a file is ultimately just zeros and ones (bits) grouped into 8-bit bytes
  • We classify files based on how we store data into two broad categories: text and binary
  • For text files, the bytes in the file represent printable characters
  • The byte for a character is defined by a standard such as ASCII (ASCII table) or UTF-8
  • Programs like text editors read text files and display the bytes as textual characters
  • Every line of text is delimited by end-of-line characters:
    • Linux/Unix/OS-X: "\n"
    • Windows: "\r\n"
  • Data other than text is usually referred to as binary data
  • Each bit represents some type of encoded information like program instructions or integer data
  • Binary files are easily read by the computer but not by humans

Streams

  • To read and write files, we use a stream
  • A file stream is used to connect a file to a program
  • Input stream: accepts bytes from a source and delivers them to a program

    Input stream

  • Output stream: accepts bytes from a program and delivers the data to a destination

    Output stream

  • We use character streams to read and write text files:

Layering Streams

  • Usually, any one stream does have not all the functionality we want
  • Instead, it is common to "layer" two or more streams
  • We add buffers to our streams to make reading and writing faster

Buffer

  • When writing, we add a stream to convert from binary to character data like PrintWriter
  • When reading, we add an object to convert the character data into other types like int, double and String using a Scanner
  • The following instructions include the layers normally used to write and read text files

Step by Step Procedure for Writing Text Files

  1. Import the API libraries for file output.
    import java.io.*;
  2. Create the layered output stream.
    String fileName = "data.txt";
    PrintWriter out = new PrintWriter( // encode data as characters
                      new BufferedWriter( // buffer data for speed
                      new FileWriter(fileName))); // write bytes
    
  3. Write the data like printing to a terminal window.
    out.println("This is a string");
    out.println('c');
    out.println(1234);
    out.println(1.234);
    for (int i = 0; i <= 10; i++) {
        out.print(i + ",");
    }
    out.println();
    
  4. Close the file.
    out.close(); // flush buffer and free up resources
    

Try It: Writing a File (3m)

  1. If you have not done so already, download the following scenario file and save it to a convenient location like the Desktop.

    Scenario file: fileio.gfar.

  2. Open the scenario by double-clicking the fileio-gfar icon, which creates a scenario folder.

    If the scenario folder already exists, then double-click the project.greenfoot file inside the folder.

  3. Open the FileWorld class and verify the API libraries for file I/O are imported:
    import java.io.*;
    
  4. Add the following method to the FileWorld class:
    public void writeScores(String filename) throws IOException
    {
        String[] scores = {"Ed      1000", "MstrChf  500"};
        PrintWriter out = new PrintWriter(
                          new BufferedWriter(
                          new FileWriter(filename)));
        for (String line: scores)
        {
            out.println(line);
        }
        out.close();
    }
    

    Compile the class after copying in the code and verify there are no errors. Resolve any errors you find, getting help from a guild mate or the instructor as needed.

  5. Call the new method to verify it works by right-clicking on the background and selecting the method. Save a file with the name of "scores.txt".

    Remember to put double-quote marks (") around the name of the file.

  6. Look inside the main folder of your scenario and verify the file scores.txt exists. Then examine the contents of the file by double-clicking the file, which should open it inside a text editor.

    If you have problems, ask a guild mate or the instructor for help as needed.

Step by Step Procedure for Reading Text Files

  1. Import the API libraries for file input.
    import java.io.*;
    import java.util.Scanner;
    
  2. Create the input stream.
    String fileName = "data.txt";
    Scanner in = new Scanner( // parse characters into data types
                 new BufferedReader( // buffer data for speed
                 new InputStreamReader( // read bytes as characters
                 getClass().getResourceAsStream(fileName)))); //JAR
    
  3. Read the data.
    String str = in.nextLine();
    char ch = in.nextLine().charAt(0);
    int x = in.nextInt();
    double d = in.nextDouble();
    while (in.hasNextInt()) {
        x = in.nextInt();
    }
    
  4. Close the file.
    in.close();  // flush buffer and free up resources
    

Try It: Reading a File (3m)

  1. Open your fileio scenario from the last exercise: Try It: Writing a File.
  2. Open the FileWorld class and verify the API libraries for file reading are imported:
    import java.io.*;
    import java.util.Scanner;
    
  3. Add the following method to the FileWorld class:
    public void readScores(String filename) throws IOException
    {
        InputStream istr = getClass().getResourceAsStream(filename);
        Scanner in = new Scanner(
                     new BufferedReader(
                     new InputStreamReader(istr)));
        while (in.hasNext()) {
            String line = in.nextLine();
            System.out.println(line);
        }
        in.close();
    }
    

    Compile the class after copying in the code and verify there are no errors. Resolve any errors you find, getting help from a guild mate or the instructor as needed.

  4. Call the new method readScores() by right-clicking on the scenario background and selecting the method. Type in the file name "scores.txt".

    Remember to put double-quote marks (") around the name of the file.

  5. Verify the read operation by looking at the Terminal window seeing the contents of the file displayed.

    If you have problems, ask a guild mate or the instructor for help as needed.

Check Yourself

  1. To store information, the computer saves the data in a __________.
  2. True or false: data in a file is restricted to characters only.
  3. When we want to write binary data to a file as character, we add the "layer" named ________.
  4. When we want to read character data and convert it to data types like int, double and String , we add a(n) ________.

14.1.2: Using Loops to Read a File

  • Sometimes we do not know how many data items are in a file
  • To solve this, the typical approach is to use a loop to read the file data
  • Within the loop we check whether or not more data is available from the input stream
  • The Scanner object has methods like hasNext() to see if there is any more input in the stream
  • These methods signal our program when it reaches the end of a file
  • The following table shows some methods of the Scanner class for detecting if more data is available
  • After the table is an example of a method that read an indefinite number of lines from a file

Some Test Methods of a Scanner Object

Method Description
hasNext() Returns true if this scanner has another token in its input.
hasNextLine() Returns true if there is another line in the input of this scanner.
hasNextDouble() Returns true if the next token can be interpreted as a double value.
hasNextInt() Returns true if the next token can be interpreted as an int value.

Method to Read a File with a Loop

public void readMap(String fileName)
{
    Scanner in = new Scanner(
                 new BufferedReader(
                 new InputStreamReader(
                 getClass().getResourceAsStream(fileName))));
    while (in.hasNext())
    {
        String line = in.nextLine();
        System.out.println(line);
    }
    in.close();
}

Try It: Read a File with a Loop (4m)

  1. Download the following scenario file, save it to a convenient location like the Desktop and open the scenario.

    Scenario file: platformer6.gfar

    If using the zip version, extract the file and double-click the project.greenfoot file in the extracted folder

  2. Open the editor for the GameManager class and import the API libraries for file input by adding the following at the top of the file.
    import java.io.*;
    import java.util.Scanner;
    
  3. Next in the GameManager class, add the readMap() method listed above just before these instructions, and then compile the file.
  4. To test the method, right-click on the scenario background and select the readMap() method, entering an argument of: "maps/map1.txt" (with the quotes).

    Remember to include the double-quote marks (") because the entry is a string. You should see the map file in the terminal window. If you have problems, ask a guild mate or the instructor for help as needed.

  5. Save your updated scenario as we will be adding to it as the lesson continues.
  6. When finished, please help those around you.
  7. Be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. When the number of file data items is unknown use a ________ statement to read all the data.
  2. Of the following, the answer that is NOT a method of the Scanner class for testing the input stream is ________.
    1. hasNext()
    2. hasNextLine()
    3. hasNextInt()
    4. hasNoInput()
  3. When typing the file name, double-quotes are required to indicate a data type of ________.

14.1.3: Loading Files with ArrayList

  • We can read data from a file but we still need to store the data into the map array
  • We could load each line of the file directly into an array like:
    int i = 0;
    while (in.hasNext()) {
        String line = in.nextLine();
        map[i] = line;
        i++;
    }
    
  • However, an array must be set to the correct size before we read from the file
  • Unless we know the number of lines in the map ahead of time, we need a list type that can grow as needed
  • One list type that can grow as needed is an ArrayList

Array Lists

  • An ArrayList is a class that organizes data into a List
  • We discussed the List type in lesson 8.1.4: Interacting with Lists of Actors
  • The ArrayList class is part of the standard library classes of Java
  • To make use of an ArrayList, we must import the Java class library
    import java.util.ArrayList;
  • The general syntax for declaring an ArrayList is:
    ArrayList<elementType> objectName;
    
  • Where:
    • elementType: the type of data stored in the list
    • objectName: the name you make up for the ArrayList
  • For example, to store a list of String objects we declare:
    ArrayList<String> list;
  • After declaring an ArrayList, we construct one using the new operator
  • For example, to declare and construct an ArrayList to store strings:
    ArrayList<String> list = new ArrayList<String>();
    
  • The above declaration creates an ArrayList with zero elements
  • To add items to the list, we call the method add():
    list.add(element);
    
    Where element is a variable or literal of the correct type.
  • Each time we call the add() method, the ArrayList adds a new element to the end of the list

Some Commonly Used Constructors and Methods of the ArrayList<type> Class

Constructor/Method Description
ArrayList() Creates an empty list.
add(object) Adds the specified object to the end of the list.
get(index) Returns the object at the specified index in the list.
set(index, element) Returns the number of elements in the list.
size() Replaces the element at index with the new element.
toArray(array) Returns an array containing all of the elements in proper sequence.

Converting an ArrayList to an Array

  • Notice that the last method above returns an array from the ArrayList
  • After we have loaded all the lines from the file into an ArrayList, we call the toArray() method like:
    map = list.toArray(new String[0]);
    
  • By using an ArrayList our program can read any number of lines from a file
  • After reading the lines, we then convert the lines to an array
  • We will update readMap() to complete this task in the next exercise

Updated Method readMap()

public void readMap(String fileName)
{
    removeObjects(getObjects(null)); // remove all actors
    ArrayList<String> list = new ArrayList();  // construct ArrayList
    Scanner in = new Scanner(
            new BufferedReader(
                new InputStreamReader(
                    getClass().getResourceAsStream(fileName))));
    while (in.hasNext()) { // while not at end of file
        String line = in.nextLine();
        list.add(line); // add lines to list
        //System.out.println(line);
    }
    in.close();
    map = list.toArray(new String[0]); // convert to array
    createPlatforms(map); // add platforms from map
}

Check Yourself

  1. Of the following statements, the one that constructs a new ArrayList that stores String data is ________.
    1. ArrayList<GreenfootImage> images = new ArrayList<GreenfootImage>();
    2. ArrayList<String> strList = new ArrayList();
    3. ArrayList<String> strList = new ArrayList<String>;
    4. ArrayList<String> strList = new ArrayList<String>();
  2. To add an item to an ArrayList, call the method ________.
  3. To convert an ArrayList to an array, call the method ________.

Exercise 14.1: Reading a Map File (5m)

In this exercise, we load map data from a file and display the tile map in a scenario using a file chooser.

Specifications

  1. Start Greenfoot and open the scenario from the last Try It exercise.

    If you did not keep the scenario, then complete Try It: Read a File with a Loop now and then continue with these instructions.

  2. Open the editor for the GameManager class and import the API library for the ArrayList at the top of the file with the other import statements.
    import java.util.ArrayList;
    
  3. Next add the following instance variable inside the GameManager class:
    private String[] map;
    
  4. In the GameManager class, replace the readMap() method with the one shown below.
    public void readMap(String fileName)
    {
        removeObjects(getObjects(null)); // remove all actors
        ArrayList<String> list = new ArrayList();  // construct ArrayList
        Scanner in = new Scanner(
                new BufferedReader(
                    new InputStreamReader(
                        getClass().getResourceAsStream(fileName))));
        while (in.hasNext()) { // while not at end of file
            String line = in.nextLine();
            list.add(line); // add lines to list
            //System.out.println(line);
        }
        in.close();
        map = list.toArray(new String[0]); // convert to array
        createPlatforms(map); // add platforms from map
        addObject(george, getWidth() / 2, 0); // add player
    }
    
  5. In the constructor of GameManager, replace the method call to createPlatforms(map) with a call to readMap().
    public GameManager()
    {
        super(800, 600, 1, false);
        leftX = TILE_WIDTH / 2 - TILE_WIDTH;
        topY = TILE_HEIGHT - getHeight() % TILE_HEIGHT;
        // createPlatforms(map);  // comment or remove
        readMap("maps/map1.txt"); // call instead of createPlatforms()
        addObject(new Sign(), TILE_WIDTH * 33, getHeight() - TILE_HEIGHT * 2);
    }
    
  6. Replace all the uses of the MAP variable with map.

    Hint: use the search and replace function in the Tools menu: Tools : Replace.... Be sure to check the Match Case box.

  7. Now remove the entire String[] MAP variable from the scenario by commenting or deleting:
    // private static final String[] MAP =
        // {
            // "BBBBBBBBBBBB                       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB                       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB                       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB    LMMMMR    LMR      BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       P       P      LBBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       P       P       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       P     LMMMR     BBBBBBBBBBBBB",
            // "BBBBBBBBBBBBMMR    P       P       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       PMR     P     LMBBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       P       P       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB       P       P       BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB     LMMMMMMMMMMMR     BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB           P           BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB           P           BBBBBBBBBBBBB",
            // "BBBBBBBBBBBBMMMR       P        LMMBBBBBBBBBBBBB",
            // "BBBBBBBBBBBB           P           BBBBBBBBBBBBB",
            // "BBBBBBBBBBBB           P                        ",
            // "BBBBBBBBBBBB           P                        ",
            // "MMMMMMMMMMMMMMMMMMMMMMMMMMR   LMMMMMMMMMMMMMMMMM"
        // };
    
  8. Compile the GameManager class to verify you implemented the variable changes correctly.
  9. After compiling, run your updated scenario and verify the map is still displayed.

    Move a tile and an NPC to verify there are no duplicate's behind them. If so, you probably need to remove the call to createPlatforms() in the constructor (step 5). Ask a guild mate or the instructor for help as needed.

  10. We can now load other maps by changing the line:
    readMap("maps/map1.txt");
    
    to something like:
    readMap("maps/map2.txt");
    
  11. Save your updated scenario as we will be adding to it as the lesson continues and eventually turning it in as part of lab 14.

As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 14.1.4: Review.

Image of portal
portal.gif

14.1.4: Adding a Portal

  • With multiple worlds available, we need some mechanism to change between them
  • One commonly used technique is to add a portal to a game
  • A portal is a doorway or other entrance to another place, in this case another world
  • To add a portal we create a new Actor subclass named Portal

Try It: Add a Portal (5m)

  1. Start Greenfoot and open the scenario from the last exercise: Exercise 14.1: Reading a Map File.
  2. Create a new Actor subclass named Portal with "portal.gif" as the image.
  3. Open the editor for the Portal class and add the following instance variable inside the Portal class:
    private String world;
    
  4. Add a constructor to the Portal class with a single String parameter. Assign the parameter to the world instance variable.
    public Portal(String map)
    {
        world = map;
    }
    
  5. Update the act() method to call the readMap() method as follows.
    public void act()
    {
        if (isTouching(Player.class))
        {
            GameManager w = (GameManager)getWorld();
            w.readMap(world);
        }
    }
    
  6. Test the Portal class by adding a new Portal object to the world using "maps/map2.txt" (with the quotes) as an argument to the constructor and then running the scenario.

    Remember to include the double-quote marks (") because the entry is a string. When the player touches the portal, a new map should appear. If you have problems, ask a guild mate or the instructor for help as needed.

  7. Save your updated scenario as we will be adding to it as the lesson continues.
  8. When finished, please help those around you.

14.1.5: Adding a Portal to the Map

  • With portals available we want to add them to our world map
  • However, each portal needs different arguments to specify which world to visit
  • One way to accomplish this goal is to have a different symbol for each world portal
  • For example:
    • 1: "maps/map1.txt"
    • 2: "maps/map2.txt"
  • To implement these symbols we update our makeMapRow() method in GameManager like:
    else if (tileType == '1')
    {
        Portal port = new Portal("maps/map1.txt");
        GreenfootImage img = port.getImage(); // adjust y-position
        int adjustY = TILE_HEIGHT / 2 - img.getHeight() / 2;
        addObject(port, tileX, tileY + adjustY);
    }
    

Try It: Add Portals to the Map (5m)

  1. Start Greenfoot and open the scenario from the last exercise: Try It: Add a Portal.
  2. Open the GameManager, locate the makeMapRow() method and add the following to makeMapRow() before the else if (tileType != ' ') option.
    else if (tileType == '1')
    {
        Portal port = new Portal("maps/map1.txt");
        GreenfootImage img = port.getImage(); // adjust y-position
        int adjustY = TILE_HEIGHT / 2 - img.getHeight() / 2;
        addObject(port, tileX, tileY + adjustY);
    }
    
  3. Similarly, add another entry to makeMapRow() to recognize a '2' as a portal for "maps/map2.txt".
  4. Open the map1.txt and map2.txt files in a text editor and add portal entries. For example, map1.txt may look like:
    BBBBBBBBBBBB                       BBBBBBBBBBBBB
    BBBBBBBBBBBB                     F BBBBBBBBBBBBB
    BBBBBBBBBBBB                       BBBBBBBBBBBBB
    BBBBBBBBBBBB    LMMMMR    LMR      BBBBBBBBBBBBB
    BBBBBBBBBBBB       P       P      LBBBBBBBBBBBBB
    BBBBBBBBBBBB       P       P       BBBBBBBBBBBBB
    BBBBBBBBBBBB       P     LMMMR     BBBBBBBBBBBBB
    BBBBBBBBBBBBMMR    P       P       BBBBBBBBBBBBB
    BBBBBBBBBBBB       PMR     P     LMBBBBBBBBBBBBB
    BBBBBBBBBBBB     F P       P     F BBBBBBBBBBBBB
    BBBBBBBBBBBB       P     G P       BBBBBBBBBBBBB
    BBBBBBBBBBBB     LMMMMMMMMMMMR     BBBBBBBBBBBBB
    BBBBBBBBBBBB           P           BBBBBBBBBBBBB
    BBBBBBBBBBBB           P           BBBBBBBBBBBBB
    BBBBBBBBBBBBMMMR       P        LMMBBBBBBBBBBBBB
    BBBBBBBBBBBB           P           BBBBBBBBBBBBB
    BBBBBBBBBBBB           P
    BBBBBBBBBBBB         G P                     2
    MMMMMMMMMMMMMMMMMMMMMMMMMMR   LMMMMMMMMMMMMMMMMM
    
  5. Test the Portal class by by running the scenario, finding a portal, and touching the portal with the player.

    When the player touches the portal, a new map should appear. If you have problems, ask a guild mate or the instructor for help as needed.

  6. Save your updated scenario as we will be adding to it as the lesson continues.
  7. When finished, please help those around you.

14.1.6: Changing Worlds

  • Unfortunately, we can no longer read files on the Greenfoot site
  • However, we can work around this problem by creating mutiple worlds with different maps
  • Greenfoot allows a scenario to have multiple world subclasses
  • After creating multiple worlds, we may switch between them using code like:
    World world = new MyWorld();
    Greenfoot.setWorld(world);
    
  • The setWorld() method is part of the Greenfoot class

    public static void setWorld(World world)

Creating Maps

  • One way to create new world maps is as a subclass of GameManager

    map as world subclasses

  • The subclasses contain a map array and a call to createPlatforms()
  • Then in the GameManager constructor we remove the calls to creating or reading maps
    //createPlatforms(map);
    //readMap("maps/map1.txt");
    

Example Map Classes

Updating Portal

  • The next step is to change the Portal class act() method like:
    public void act()
    {
        if (isTouching(Player.class))
        {
            //GameManager w = (GameManager)getWorld();
            //w.readMap(world);
            if(world.equals("maps/map1.txt"))
            {
                Greenfoot.setWorld(new Map1());
            }
            else if (world.equals("maps/map2.txt"))
            {
                Greenfoot.setWorld(new Map2());
            }
        }
    }
    
  • Finally, right-click the Map1 class and select new Map1().
  • This last step sets the starting world of a Greenfoot scenario

Try It: Changing Worlds (3m)

  1. Start with your existing platform code unless you had problems.

    If you had problems then download the following scenario file, save it to a convenient location like the Desktop, and double-click to open the gfar file.

    Scenario file: platformer7.gfar

  2. Create two new World subclasses using the following code with "sky.jpg" as the image.
  3. Update the GameManager constructor to remove any call to create or read maps
    //createPlatforms(map);
    //readMap("maps/map1.txt");
    
  4. Change the Portal class act() method as follows:
    public void act()
    {
        if (isTouching(Player.class))
        {
            if (world.contains("map1"))
            {
                Greenfoot.setWorld(new Map1());
            }
            else if (world.contains("map2"))
            {
                Greenfoot.setWorld(new Map2());
            }
        }
    }
    
  5. Right-click the Map1 class and select new Map1().

14.1.7: Review

Answer these questions to check your understanding. You can find more information by following the links after the question.

  1. What is the difference between a text file and a binary file? (14.1.1)
  2. What is an input stream? (14.1.1)
  3. What is an output stream? (14.1.1)
  4. What are three streams we typically layer for program output? (14.1.1)
  5. What are the steps for writing files? (14.1.1)
  6. What are the steps for reading files? (14.1.1)
  7. What technique can we use to read all the data from a file, if we do not know how much data is in the file? (14.1.2)
  8. Which of the following statements constructs a new ArrayList that stores String data? (14.1.3)
    1. ArrayList<GreenfootImage> images = new ArrayList<GreenfootImage>();
    2. ArrayList<String> strList = new ArrayList();
    3. ArrayList<String> strList = new ArrayList<String>;
    4. ArrayList<String> strList = new ArrayList<String>();
  9. Which of the following statements adds a new string to the end of the list? (14.1.3)
    1. add("foo");
    2. strList.add("foo");
    3. strList.addToEnd("foo");
    4. strList.addString("foo");
  10. A portal is a doorway to another ________. (14.1.4)
  11. We must use a different symbol for each portal because the constructor ________ is different. (14.1.5)
  12. The Greenfoot method to change worlds is ________. (14.1.6)

14.2: Buttons, Labels and TextFields

Learner Outcomes

At the end of the lesson the student will be able to:

  • Write code to add buttons to scenarios
  • Write code to add labels to scenarios
  • Write code to add text fields to scenarios
  • Write code that responds to action events

14.2.1: Adding GUI Components to Scenarios

  • A Graphical User Interface (GUI) allows users to interact with a program through graphical icons and other visual indicators
  • GUI components, like buttons and text fields, are typical in a GUI system
  • Java provides GUI components through libraries for buttons and text fields
  • However, we cannot directly display these GUI components in a Greenfoot scenario window
  • Instead, we would have to create a separate GUI window which is cumbersome
  • To get around this limitation, your instructor created a GUI library that runs in Greenfoot

Introducing GreenGUI

  • Let us look at the GreenGUI scenario:

    Scenario file: GreenGUI.gfar

  • Save the file in a convenient location like the Desktop and launch the archive
  • The Green GUI scenario shows how to fully utilize buttons and labels
  • Notice the rollover and pressed effects on the buttons
  • Also, notice how we can ask the user for information

GUI Organization

  • Each component is in its own class:
    • Button: Constructs and handles buttons
    • Label: Constructs and handles text labels
  • The user input text field is from the ask() method of the Greenfoot class
  • The button and label components allows many options like changing font sizes, foreground and background colors, and changing the text
  • The GUIWorld class has many examples of how to install the components
  • The ButtonChecker class shows examples of how to use the various features
  • We will look at how to use individual components in the following sections

Try It: Install the GUI Components (3m)

  1. Download the Green GUI scenario and save it to a convenient location like the Desktop and then double-click to open the gfar file.

    Scenario file: GreenGUI.gfar

  2. Use your existing platform code unless you had problems.

    If you had problems then download the following scenario file, save it to a convenient location like the Desktop, and double-click to open the gfar file.

    Scenario file: platformer7.gfar

  3. Copy the images from the Green GUI scenario to the Platformer scenario's images folder.

    You may skip copying the person.png image file.

  4. Next copy the following source code files from the GreenGUI scenario to the Platformer scenario:
    • Button.java
    • Label.java

    If you have problems, ask a classmate or the instructor for help as needed.

  5. If the Platformer scenario is running, then exit the scenario.
  6. Start (or restart) the Platformer scenario and compile it to verify you copied the component classes correctly.

    If you have problems, ask a classmate or the instructor for help as needed.

  7. When finished please help those around you.
  8. Be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. GUI stands for G________ U________ I________.
  2. True or false: we cannot add Java's GUI components to a scenario's window.
  3. Of the following, ________ is not a component provided by GUI World.
    1. Button
    2. Dialog
    3. Label
    4. TextField
  4. True or false: to use a GUI component (like Button) we must include all the component files (like Label) in our scenario.

14.2.2: Creating a Label Component

  • A Label is an Actor with an image containing text that we may add to a scenario
  • To construct a label we must include the Label class in our scenario

Constructing a Label

  • To create a label, we write code like the following:
    Label myLabel = new Label("Some text");
    
  • To add the label to a scenario, we use the addObject() method of World
  • For example:
    addObject(myLabel, 115, 50);
    
  • As we can see below, Label has several constructors and methods that let us choose how the label looks
  • For instance, if we want a label with larger blue text we write:
    Label colorful = new Label("Some text", 24, Color.BLUE);
    addObject(label, x, y);
    
  • We can see the list of constructors in the following table

Constructors and Methods of the Label Class

Constructor Description
Label() Constructs a label with a default message and settings.
Label(textString) Constructs a label with the specified text using default settings.
Label(textString, textSize) Constructs a label with black text of the specified text size on a clear background.
Label(textString, textSize, colorObj) Constructs a label of the specified size and text color on a clear background.
Label(textString, textSize, textColorObj, bgColorObj) Constructs a label with text of the specified size and color on a background of the specified color.
String getText() Returns the text for this label.
void setText(textString) Sets the text to be displayed.
void setBackgroundColor(colorObj) Sets a new background color for this button.

Getting and Setting Text

  • We may construct a Label with specified text like
    Label label = new Label("Label 24", 24);
    
  • We can set new text using code like:
    label.setText(answer);
    
  • We can retrieve the current text of a label using getText() like:
    String text = label2.getText();
    System.out.println(text);
    
  • Using getText() may be useful when trying to find a specific label:
    String answer = "42";
    List<Label> labels = getWorld().getObjects(Label.class);
    for (Label label : labels)
    {
        if (label.getText().equals("Label"))
        {
            label.setText(answer);
        }
    }
    

Setting a Background Color

  • The default background color for a Label object is clear (transparent)
  • We can change the background color to a new color by calling setBackgroundColor() like:
    label.setBackgroundColor(Color.GREEN);
    

Mutliple-Line Labels

  • We can specify labels with multiple lines of text
  • To manually break lines, we use the "\n" character
  • For example, this code creates a three-line label:
    Label label = new Label("Lines\nwrapped\nwith \\n");
    
    That looks like this:

    Multiple line label

  • Notice how we are printing a "\n" character by escaping the "\n" with an extra "\"
  • The backslash is known as an escape character

Try It: Make a Label (5m)

  1. Start Greenfoot and open the platformer scenario from the last Try It exercise.

    If you did not keep the scenario, then complete Try It: Install the GUI Components now and then continue with these instructions.

  2. Open the editor for the starting world (like Map1).
  3. Add the following Label object to the world at the end of the constructor.
    Label label = new Label("Exit \u2192", 18, Color.WHITE);
    addObject(label, 400, 50);
    

    Note that \u2192 is a right arrow in unicode.

  4. Compile the scenario to see the label.

    Experiment with different label possibilities by making changes to the text, text size, and colors.

  5. Save your updated scenario as we will be adding to it as the lesson continues.
  6. When finished please help those around you.
  7. Be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. Enter the code to construct a Label object with the text, "Java rules!" in a red color.

    (an answer)

  2. To add a label object to a scenario, call the ________ method of the World class.
  3. True or false: we can construct a text image with GreenfootImage, but we always need a Label or other Actor object to add the image to a scenario.
  4. To cause a new line in the text of a label use the character sequence ________.

14.2.3: Creating a Button Component

  • A Button is a GUI component that responds to mouse clicks
  • To construct a button we must include the Button class in our scenario

Buttons States

  • A button has three different images it presents depending on the mouse
  • The following are the images for the default Style.RECTANGLE:

    Button up Button hover Button down
    Button up Button hover Button down

  • To make use of these images, we place them in our scenario's images folder

Constructing a Button

  • To create a Button, we write code like:
    Button button = new Button("Click me");
    
  • To add the button to a scenario, we use the addObject() method of World
  • For example:
    addObject(button, 350, 50);
    
  • As we can see below, Button has several constructors and methods that let us choose how the button looks
  • For instance, if we want a button with larger blue text we write:
    Button btn = new Button("Click me", 24, Color.BLUE);
    addObject(btn, x, y);
    
  • We can see the list of constructors and methods in the following table

Constructors of the Button Class

Constructor Description
Button() Constructs a button with no set text or image.
Button(textString) Constructs a button with the specified text using default settings.
Button(textString, textSize) Constructs a button with black text of the specified text size.
Button(textString, textSize, textColorObj) Constructs a button of the specified size and text color.
Button(textString, textSize, textColorObj, style) Constructs a button with text of the specified size and color on a button object of the specified style.

Some Commonly Used Methods of the Button Class

Method Description
String getText() Returns the text for this button.
void setText(textString) Sets the text to be displayed.
void setBackgroundColor(colorObj) Sets a new background color for this button.
boolean isPressed() Returns true if this button is currently down (pressed), otherwise returns false.
int getID() Gets the identifier number of the component.
void setID(idNumber) Sets the identifier number of the component.

Button Methods

  • The Button class methods work like the Label methods of the same name
  • However, the button has a few extra methods that the label does not
  • For example, isPressed() tests if the button is currently down (pressed)
  • We call the isPressed() method to see if we need to respond to a button press
    if (button.isPressed())
    {
        // do something
    }
    
  • We may use the getID() and setID() methods to identify a particular button in the scenario
  • By calling setID(), we can set a unique id number
    button.setID(42);
    
  • Later, we may test the id number to see if it is the button we are looking for
    if (button.getID() == 42)
    {
        // do something with this button
    }
    

Button Styles

  • The current styles supported by the Button class are: RECTANGLE, ROUNDED, CIRCLE
  • Thus there are nine images needed to use the Button class
  • To select a style write: Style.X, where X is one of the styles
  • For example, to select the ROUNDED style write the code:
    Style.ROUNDED

Try It: Make a Button (5m)

  1. Continue with the scenario from the last Try It exercise.

    If you did not keep the scenario, then complete Try It: Make a Label now and then continue with these instructions.

  2. Open the editor for the starting world (like Map1) and add the following instance variables to the class:
    private Button button;
    
  3. Locate the constructor and add the following code at the end of the constructor:
    button = new Button("Choose level");
    addObject(button, 85, 28);
    
  4. Compile and run the scenario to see the button.

    Notice how the button changes when the mouse moves over it and when pressed.

  5. Experiment with different button options by making changes to the style, text and colors.
  6. Add the following act() method to the starting world (like Map1).
    public void act()
    {
        if (button.isPressed())
        {
            System.out.println(button);
        }
    }
    
  7. Test your act() method by running the scenario and pressing the button.
  8. Save your updated scenario as we will be adding to it as the lesson continues.
  9. When finished please help those around you.
  10. Be prepared to answer the following Check Yourself questions when called upon.

Check Yourself

  1. True or false: we can adjust the way a button looks using the same options as a label.
  2. Enter the code to create a button with the text, "Click for Java".

    (an answer)

14.2.4: Collecting Player Input as Text

  • The Greenfoot class has a method that collects player input:
    String ask(String prompt)
    
  • The method freezes the scenario and asks the user for a value
  • For example if we wrote the following code in an act() method:
    String map = Greenfoot.ask("Enter the name of the map:");
    
    we would see at the bottom of the world:

    ask method call

  • Once the user types in a value and presses the Enter key, the scenario resumes
  • We will explore how to use this technique in the following exercise

Exercise 14.2: Adding GUI Components (8m)

In this exercise, we add world selection to the platformer scenario.

Specifications

  1. Start with your existing platform code unless you had problems.

    If you had problems then download the following scenario file, save it to a convenient location like the Desktop, and double-click to open the gfar file.

    Scenario file: platformer7.gfar

  2. Make sure you have already completed the following Try It exercises from this lesson:
  3. Open the editor for the starting world (like Map1) and locate the act() method added in the last exercise
    public void act()
    {
        if (button.isPressed())
        {
            System.out.println(button);
        }
    }
    
  4. Replace System.out.println(button); with the following statements shown in bold.
    public void act()
    {
        if (button.isPressed())
        {
            String map = Greenfoot.ask("Enter the name of the map:");
            System.out.println(map);
        }
    }
    
  5. Test your code by running the scenario, pressing the button, typing in text and pressing the Enter key.

    Notice that the information typed by the user in the text box is displayed in the Terminal window.

  6. We now want to let the user enter a map to read from a file. Replace System.out.println(map); with the following statements shown in bold.
    public void act()
    {
        if (button.isPressed())
        {
            String map = Greenfoot.ask("Enter the name of the map:");
            if (map.contains("map1"))
            {
                Greenfoot.setWorld(new Map1());
            }
            else if (map.contains("map2"))
            {
                Greenfoot.setWorld(new Map2());
            }
        }
    }
    
  7. Test your code by running the scenario, pressing the button, typing in the other map file: map2.txt

    You should now see a new map loaded. If you have problems, please ask a classmate or the instructor for help.

  8. Save your platformer7 scenario to submit to Canvas as part of the Final Lab.

As time permits, read the following sections and be prepared to answer the Check Yourself questions in the section: 14.2.5: Review.

14.2.5: Review

  • A Graphical User Interface (GUI) allows users to interact with a program through graphical icons and other visual indicators
  • GUI components, like buttons and text fields, are typical in a GUI system
  • Java provides GUI components through libraries for buttons and text fields
  • However, we cannot directly display these GUI components in a Greenfoot scenario window
  • To add a GUI, your instructor created a GUI library for Greenfoot
  • Each component has its own class:
    • Button: Constructs and handles buttons
    • Label: Constructs and handles text labels
  • To use any component, we must include the class for the component
  • The GUIWorld class has many examples of how to use the various features

    Scenario file: GreenGUI.gfar.

  • For instructions on installing GUI Components into a scenario, see Try It: Install the GUI Components

Constructing Label's

  • A label is an image with text that we can add to a scenario
  • To create a label, we write code like the following:
    Label myLabel = new Label("Some text");
    
  • To add the label to a scenario, we use the addObject() method of World like:
    addObject(myLabel, x, y);
    
  • We can construct labels with various settings as described in section 14.2.2: Creating a Label Component

Creating Button's

  • A button is a GUI component that responds to mouse clicks
  • To create a Button, we write code like:
    Button btn = new Button("Click me");
    
  • To add the button to a scenario, we use the addObject() method of World like:
    addObject(btn, x, y);
    
  • We can construct buttons with various settings as described in section 14.2.3: Creating a Button Component
  • To detect button presses we call the isPressed() method of the Button class.

Creating TextField's

  • A text field is a GUI component that allows the user to enter a single line of text
  • Text input is provided by Greenfoot.ask()

Answer these questions to check your understanding. You can find more information by following the links after the question.

  1. GUI stands for G___________ U_________ I___________. (14.2.1)
  2. True or false: we cannot add Java's GUI components to a scenario's window. (14.2.1)
  3. What are two GUI components provided by GUI World? (14.2.1)
  4. True or false: to use a GUI component (like Button) we must include all the component files (like Label) in our scenario. (14.2.1)
  5. Write a statement to create a label with the text, "Java rules!" in a red color. (14.2.2)
  6. Write the code to construct a label with the text, "Java rules!", in a large blue font color on a yellow background. (14.2.2)
  7. What special character sequence will cause a new line in the text of a label? (14.2.2)
  8. True or false: we can adjust the way a button looks using the same options as a label. (14.2.3)
  9. Write code to create a button with the text, "Click for Java". (14.2.3)
  10. Write the code to ask the player for their name. (14.2.4)

14.3: User Testing

Learner Outcomes

At the end of the lesson the student will be able to:

  • Provide feedback on other student' scenarios
  • Review feedback on your own scenario

14.3.1: About User Testing

  • The user-testable prototype is a complete and useable scenario
  • Some final touches, like opening screens, may be absent
  • However, the scenario is playable and testable
  • During testing, we have the opportunity to provide the developers with important feedback
  • With the feedback, they can improve the quality of the scenario before submitting the final project

User-Testable Prototype Developer Instructions

  1. Load your guild's project prototype onto at least three classroom computers
  2. Be prepared to let other students come and test your project
  3. Place written instructions next to each computer
  4. Open a browser with a link to the online form (see below)
  5. After the testing session is complete, you can view the reports online (see below)

Testing the Scenario

  • Do not discuss the game with a developer until after your play testing is complete.
  • Read the instructions for the game and then play the game through at least once.
  • Fill out the online form to record your observations (see below).
  • Make certain to correctly spell both your avatar name and the guild name to ensure credit.
  • Also make sure the instructor knows your avatar name

Instructor Evaluation of Project

  • During user testing, the instructor will also be evaluating projects
  • Because time is tight, please come and demo your project if your name is called
  • Instructor feedback will be reported in Canvas

14.3.2: Steer Clear of the Dark Path

  • Even if you enjoy talking trash with friends, do not indulge during the review
  • Avoid ridicule or dismissive language
    • NO: "The feeling was Meh. The ball of light was uninspiring."
    • NO: "I can't see around that ridiculous ball of puke-colored light."
    • YES: "The concept is good but the light around this character obstructs the view and is an unpleasant shade of yellow."
  • Do not use irony and sarcasm
    • NO: "The art for that makes me want to gouge out my eyes."
    • NO: "I'm so amazed by that effect, I just want to quit my job and worship the developer."
    • NO: "The fire effect is super impressive. It's the most amazing fire I've ever seen. Really."
    • YES: "The fire effect is good but would be more impressive if it was larger and if it shimmered more."
  • Do not make it personal
    • NO: "If you can't see how this isn't fun, then you're stupid."
    • YES: "The scenario would be more enjoyable if I did not die every time I started."
    • NO: "Whoever coded this [particular feature] deserves to fail."
    • YES: "The player takes off like a rocket when falling and then dies."
  • The NO comments like those above may see funny, but hurting others is never funny
  • Treat scenario creators with courtesy and respect
  • We will explore how to give constructive feedback in the next section

14.3.3: How to Give Constructive Scenario Feedback

  • The goal of reviewing a student's scenario is to help them improve
  • While they may want to improve, people are generally sensitive to criticism
  • We want to phrase our feedback so that it is valued by the recipient
  • Here are some suggestions on how to give constructive scenario feedback

Review as you want to be reviewed

  • Most people want the same thing: to have people enjoy their scenarios
  • Make sure your feedback is courteous and thoughtful
  • Review other scenarios as you would want other people to review your scenario
  • Avoid antisocial comments

Bundle suggestions with compliments

  • Overall, give at least as much positive feedback as you do negative
  • Start with a positive observation before offering a criticism
  • Make sure you follow criticism with a suggested solution
  • After the solution, offer a positive observation again
  • The courtesy of placing some praise before and after a suggestion for improvement makes receiving the criticism easier to take

Be specific

  • Describe specifically what you have observed
  • Where in the scenario did you observe something and what were you doing?
  • For example, "I was playing _____ and I tried _____ when ____ happened."
  • Describe how you reacted to what you observed

Put the feedback in perspective

  • Describe whether your criticism or suggestion is major or minor
  • When you are sending a message, make sure the tone of your words is appropriate

Starter Phrases for Giving Feedback

  • "I thought you did a good job with/of _____."
  • "One area I thought could be improved was _____."
  • "The core mechanic was _____. It made it fun to _____."
  • "The visual design was _____ and it made me think/feel _____."
  • "I was playing _____ and I tried _____ when ____ happened."

14.3.4: Forms and Feedback Results

Reading Feedback

  • Determine whether the comment is legitimate. Don't waste time if the comment is just to bully or offend you.
  • Pick out useful information. If someone is alerting you to a problem with your scenario that you were not aware of, then read it carefully.
  • Decide which points you agree with. Think about whether your scenario would be better if you followed the reviewer's advice. Your scenario may be better left unchanged.
  • Keep track of aspects you want to fix.
  • Think about what you are doing right.
  • Take note of highly praised concepts or techniques that should be extended or repeated.

Wrap Up

Due Next:
Q13: User-Testable Prototype (11/29/18)
Lab 14: Final Lab (12/4/18)
Q14: Final Project (12/6/18)
  • When class is over, please shut down your computer
  • You may complete unfinished lesson exercises at any time before the due date.
Last Updated: November 26 2018 @15:04:28