Colored Dots for App Inventor 2
This is an App Inventor 2 Tutorial (Need the App Inventor Classic version?)
Colored Dots: Creating Apps with Multiple Screens
This tutorial shows how to create apps that have multiple screens. In App Inventor, you can have a screen open a second screen. Later, the second screen can return to the screen that opened it. You can have as many screens as you like, but each screen closes by returning to the screen that opened it. The screens can share information by passing and returning values when they open and close. The screens also share the same TinyDB data, which they can use to store and share values.
Building an app with multiple screens is a lot like creating several individual apps. Every screen that you create has its own components in the Designer window. In the Blocks Editor, you will be able to see only the components of the screen currently selected in the Designer. Similarly, the blocks of code related to a screen cannot refer to blocks of code in another screen.
This program, ColoredDots, has two screens. ColoredDots is similar to PaintPot, but it uses a second screen to let the user create new colors by providing numerical values for the red, green, and blue color composition (RGB). In the PaintPot app, a user can only paint with one of the three predefined colors. Adding more colors to Paintpot would have required new buttons on the screen, reducing the amount of space available for painting. In ColoredDots, the new color is chosen on a second screen called Brush_Picker. Once a new color has been created in this second screen, its value is passed back to the first screen. You can also save and name the colors you create in the second screen, then use the saved colors later when painting. By using ListPicker to store the colors you create, it eliminates the need for the user to remember the name of the color they saved and spell it correctly in accessing it again. This makes the database of stored colors more accessible, making it easier to share the app among a group of people.
Here are the two screens for the ColoredDots app:
Objectives
By the end of this tutorial, you should have a understanding of:
- How to make an app with multiple screens.
- How to pass values from one screen to another via TinyDB.
- How to fill and use the ListPicker element.
Building the Main Screen (Screen1)
You start building a multiscreen app by creating a new project, just as with any other app. As usual, App Inventor automatically creates the main screen and names it Screen1 and you can add components. Here's the Designer and the Components panel when all the components for Screen1 have been added.
The components are:
Component Type | Palette Group | What you'll name it | Purpose of Component | Component Settings |
Label | User Interface | TitleLabel | Shows the title "Paint with Colored Dots" | Change Properties to FontBold and set FontSize to 16, TextAlignment to center, and Width to Fill parent |
HorizontalArrangement | Layout | HorizontalArrangement1 | Holds the following 4 labels: | Set Width to Fill Parent and AlignHorizontal to Center |
Label | User Interface | ColorLabel | Shows the text "Current Color" | |
Label | User Interface | ColorSample | Blank label whose background color is the current color which we initialize to be black | |
Label | User Interface | DotSizeLabel | Shows the text "Dot size" | |
Label | User Interface | DotSizeValue | Shows the current dot size. We will initialize this to 3. | |
Canvas | Drawing and Animation | Canvas1 | Shows dots at the places you touch | Set the width to Fill parent and the height to 300 pixels |
HorizontalArrangement | Layout | HorizontalArrangement2 | Holds the following 2 Buttons and ListPicker | Set Width to Fill Parent and AlignHorizontal to Center |
Button | User Interface | EraseButton | Clears the canvas | Set Text to "Erase" |
Button | User Interface | openBrushPicker | Launches the second screen | Set Text to "Customize Brush" |
ListPicker | User Interface | ListPicker1 | Loads the list of colors from the data base | Set Text to "Stored Colors" |
TinyDB | Storage | TinyDB1 | Stores color names and values |
We'll look at the blocks for Screen1 below. But first let's add the other screen.
Adding a second screen
To add a new screen to your app, click the Add Screen button in the top toolbar of the Designer window. A dialog window will appear, in which you can provide a name for the new screen.
Note: You should make the name of the new screen something meaningful when you add it. Do that now, because once a screen has been added, its name cannot be changed -- the Rename button for the screen component itself is disabled -- it's good to have meaningful names for the parts of your program. One limitation here is that Screen1 cannot be renamed. Remember also that the screen's title (shown in the title bar when the app is running) is different from its name. You can change the screen title in the Properties pane.
When you create a new screen, the Designer window switches to display it. Initially there will be no components, just like with a new app, and you add new components by dragging them from the Palette, just as with any screen. Here's the Designer window for the second screen, named Brush_Picker, after the components have been added. Notice that Brush_Picker is selected in the dropdown on the top bar outlined in red. This is because it is the current screen.
The components for the brush picker screen are:
Component Type | Palette Group | What you'll name it | Purpose of Component | Component Settings |
Label | User Interface | RedLabel | Shows the text "Red" | |
TextBox | User Interface | Red | For entering the amount of red in the color (0-255) | Set NumbersOnly |
HorizontalArrangement | Layout | RedHorizontalArrangement | Holds RedLabel and Red TextBox | |
Label | User Interface | BlueLabel | Shows the text "Blue" | |
TextBox | User Interface | Blue | For entering the amount of blue in the color (0-255) | Set NumbersOnly |
HorizontalArrangement | Layout | BlueHorizontalArrangement | Holds BlueLabel and Blue TextBox | |
Label | User Interface | GreenLabel | Shows the text "Green" | |
TextBox | User Interface | Green | For entering the amount of green in the color (0-255) | Set NumbersOnly |
HorizontalArrangement | Layout | GreenHorizontalArrangement | Holds GreenLabel and Green TextBox | |
VerticalArrangement | Layout | VerticalArrangement1 | Holds Color HorizontalArrangements | |
Canvas | Drawing and Animation | Canvas1 | Displays the size and color of the test color | Set Width and Height to Fill parent |
HorizontalArrangement | Layout | HorizontalArrangement1 | Holds VerticalArrangement1 and Canvas1 | Set Width to Fill parent |
Label | User Interface | DotRadius | Shows the text "Dot Size" | |
TextBox | User Interface | RadiusTextBox | Shows the current dot size | Set NumbersOnly |
HorizontalArrangement | Layout | RadiusHorizontalArrangement | Holds DotRadius and RadiusTextBox | |
Button | User Interface | TestColorButton | Press to create the new color and prompts the user to store this color | Set Text to "Test Color" |
Label | User Interface | LabelTestColorSample | Blank label whose background color is the new color | Set Width to Fill parent |
HorizontalArrangement | Layout | TestHorizontalArrangement | Holds TestColorButton and LabelTestColorSample | Set Width to Fill parent |
Button | User Interface | ResetColorButton | Resets all the values to the default color, black | Set Text to "Reset Brush Selector" |
Button | User Interface | ReturnToPainting | Returns to the main screen with the new color and dot size | Set Text to "Return to Painting" and FontSize to 16 |
VerticalArrangement | Layout | VerticalArrangement2 | Holds ResetColorButton and ReturnToPainting | Set Width to Fill Parent and AlignHorizontal to Center |
Notifier | User Interface | Notifier1 | Shows a dialog for saving the color and entering a color name | |
TinyDB | Storage | TinyDB1 | Stores color names and values |
Here and in general with multiple screen apps, only the components that belong to the current screen (in this case Brush_Picker) are shown in the Designer and the Components pane. You can switch between screens by toggling between Brush_Picker and Screen1 using the dropdown, and the view in the Designer will show the corresponding screen. The same is true for the Blocks Editor: When you're working on a screen you'll see only the blocks for that screen.
None of the components, variable definitions, and procedures that you define in one screen will be accessible from any other screen. We'll see below how to pass information between screens.
Blocks for the main screen (Screen1)
The Screen1 is essentially a drawing program. When you touch the Canvas, the app draws a dot of the current radius and color. The color is specified by the background of the ColorSample label. The erase button clears the canvas. When the screen opens, it initializes the radius to 3 and the color to black. Here are the corresponding blocks:
It is worth noting that the Initialize block uses the procedure TinyDB1.StoreValue. This action will be discussed in more detail later. Basically, when the screen starts, it stores the default color, Black.
Testing the behavior. Test to make sure the drawing behavior acts as you expect it to. You won't be able to add new colors yet, but see what happens when you try to draw dots of the page.
Communicating with the brush picker screen
The multiple screen aspect of the app involves getting the "new brush": the new color and the new dot radius. It's the job of Brush_Picker to create these values and return these to Screen1 as a list of three items: the color, the dotsize, and the total number of colors stored. The number of colors is necessary for creating the list that ListPicker will reference. We'll see how Brush_Picker does this below, but from the perspective of Screen1, all it needs to do is open Brush_Picker and get the result.
Here's how this happens: when the Customize Brush button is pressed, Screen1 uses open another screen with start value to open Brush_Picker. When the user finishes with Brush_Picker, it will signal the other screen closed event, which provides the name of the screen (here Brush_Picker) and the value returned (here the three-element list). The event handler for Screen1 extracts the three items from the list and sets the ColorSample background and the DotSizeValue text properties. Here are the blocks:
ColoredDots uses open another screen with value to open Brush_Picker with values passed to it from Screen1. The other screen then accesses these opening values using get start value blocks. In general, a screen opens another screen with open another screen and gets a result back through the when other screen closed event. In this case, the values are passed from Screen1 to Brush_Picker so the app can keep track of how many colors are stored and what the current values are.
Sharing data via TinyDB
Besides opening screens and returning values, the different screens in a multiple screen app can communicate through TinyDB. To do this, give every screen its individual TinyDB component. Even though these will be "different" TinyDB components, they will in fact all share the same keys and values: If one screen stores a value under a key, the other screen can get that value by using the same key.
ColoredDots uses TinyDB to let you name the colors you create and save them to later use. The saving and naming will be done in Brush_Picker, as shown in the blocks later. When you open ListPicker (labelled Stored Color) in Screen1, the app uses opens the list of colors that you have stored for you to choose one and then sets the drawing color to the result. The elements are added to this list by creating the procedure populateList. As shown in these blocks, populateList iterates through the all of the stored entries in the database up to the number of stored colors and adds them to the list tinyDBlist. It returns this list, which ListPicker1 points to.
Now that populateList has been created, add the following to Screen1.Initialize and Screen1.OtherScreenClosed :
When ListPicker is opened, you will see a list of all the colors you created. ListPicker1.AfterPicking sets the Color value stored for that name to be the current drawing color and then sets the current color swatch to that color, as shown here.
Blocks for the Brush_Picker screen
The main job of Brush_Picker is to create a color from the red-green-blue values entered in the text boxes and provide that color to Screen1. One thing the Brush_Picker needs to check is that it's using good values for colors and dot size. Each of the red, green, blue values should be a number between 0 and 255. But you could have entered anything in those text boxes. The checkColor procedure takes a value and limits its range to between 0 and 255. If it's less than 0 (or not a number at all) the result will be 0. If it's greater than 255, the result will be 255.
The checkColor procedure calls the limitRange procedure. This is a general procedure that takes an input, a lower limit, and an upper limit, and restricts the input to lie within that range (and it returns the lower limit if the input is not a number). The procedure is simple, but tricky: To restrict the range, take the maximum of the input and the lower limit, then take the minimum of the result and the upper limit. This programming "trick" has little to do with multiple screens or App Inventor. You might want to take a few moments to convince yourself that it really works.
Below are the blocks for checkColor and limitRange. Remember that procedures are mutator blocks and we can use the mutator button to add (and change the names of) parameters to our procedure. To review mutators, check out the mutators page.
Now, we will use those two procedures to create the rest of the blocks for Brush_Picker. When you click ReturnToPainting, the new color, the new dot size (taken from the RadiusTextBox), and the total number of colors are returned to Screen1 as a three-item list. As mentioned before, when Brush_Picker is open, Screen1 passes its values of the current color, the number of colors, and the current dot radius size. Brush_Picker uses get start value when it is initialized to access these values.
When you click the TestColorButton, the color is created for you as the background of LabelTestColorSample and the Canvas shows a circle of the appropriate size and color. To return the list to Screen1, Brush_Picker uses close screen with value, which takes as an argument the value to be returned (in this case, the list). When a screen closes, the app returns to the screen that opened it.
Here are the blocks that do this. Brush_Picker uses the procedures CheckColor and LimitRange to ensure that the values entered in the text boxes are valid numbers for colors and dot size.
When TestColorButton is clicked, is displays the created color. Notifier1 is then used to give you a choice to save the color or cancel using the Notifier1.ShowChooseDialog block. The response from this will trigger further Notifier events so that you can save the color.
The next blocks show how Brush_Picker associates a name with a color for use by Screen1. The Notifier1.ShowChooseDialog block triggers a Notifier1.AfterChoosing event. In these blocks, the ifelse block checks the response to whether or not the color should be saved. If the response is "OK," Notifier1.AfterChoosing calls Notifier1.ShowTextDialog which prompts you to store a name for this color.
Once you make your entry, the color is stored with two tags two ways in TinyDB1. First, it stores the name with the current number of colors as the key. This is used when iterating through the list of colors for ListPicker in Screen1 so that the names will be displayed for the user. Secondly, the color is stored in TinyDB, using the name you entered as a key. Screen1 can then retrieve the color using its own TinyDB component and access it via the stored name as chosen in ListPicker.
The Reset button allows the user to clear all of the entries in the red, green, and blue fields so they can start over easily. The blocks for this are shown here:
Note: you can use copy paste (ctl-v and ctl-c) after creating the first set Red.Text then you can use the dropdown to change from red to green to blue.
Comments on multiple screens
You can have many screens in an App Inventor app, but a screen always returns to the screen that opened it. On the other hand, you can get the effect of screens switching to arbitrary other screen by setting up a "manager screen" that is used for opening all the other screens. When a screen wants to switch, it returns to the manager with a value saying which screen to open next.
Testing the behavior. App Inventor 2 allows multi-screen app testing during live development, however it is not fully functional when passing values between screens. REMEMBER, you actually have to package the app to your phone or emulator to test the multi-screen switching behavior.
Further exploration
Now that you are able to create your own colors and access them for drawing, try some variations!
- Make Brush_Picker warn the user if they are trying to store a color with the same name as something that has already been stored (e.g. don't let them make two colors named "red").
- Have the starting color be the last one that the user was using in the app instead of always starting with black.
Summary
In this tutorial you have learned:
- How to make an app with multiple screens
- How to share data between those two screens using TinyDB
- How to use a counter to add all of the values in TinyDB to add to a list.
- How to use ListPicker to select from a list of items
This tutorial is based on work by Eni Mustafaraj of Wellesley College. Done with Colored Dots? Return to the other tutorials here.
Tutorial Version:
Tutorial Difficulty:
- Advanced
Tutorial Type:
- Drawing Canvas
- Multiple Screens
- Data Storage