Android 003: Drawing App
This will help you:
Create a drawing app for Android devices.
Time: 1-2 hours / Level: B2
You should already:
Have an Android device to test on (optional)
Have completed the Android Startup activity
Part 1: The Layout
The app will have a canvas with controls and options below it. In this example, there will be a vertical stack of controls which can be scrolled through. At the top, there will be buttons for opening, saving, and other options. These buttons will scroll side-to-side. Below the buttons, there will be sliders for pen color and size. Make sure you are in the "Designer" view, not the "Blocks" view, to edit the appearance and layout.
Step 1: Components
From the "Layout" panel in the component list, add a vertical arrangement to the screen. This will hold the canvas and all controls. Then, from the "Drawing and Animation" panel, add a canvas component inside the vertical arrangement. This will be the drawing area. For the controls, add a vertical scrolling arrangement from the "Layout" panel, and position it under the canvas, inside the first vertical arrangement.
So far, we have a vertical arrangement with the canvas and the options arrangement inside. Into the options arrangement, add a horizontal scrolling arrangement to hold the buttons. You can add buttons to it from the "User Interface" tab, for clearing the canvas, saving it, and whatever else you want.
Below the scrolling row of buttons, add 4 sliders: 3 for color choosing (in R, G, B format), and 1 for pen size.
Lastly, add a notifier component from the "User Interface" panel. This is a non-visible component, and it will be used to save drawings.
Your "Components" tree should look something like this:
Step 2: Appearance
While you can customize the appearance of your components however you want, here are some steps to make the user interface a little easier to use for drawing:
For the screen, uncheck "ShowStatusBar" and "TitleVisible" to increase screen space. I set the BackgroundColor to a different color, just to make the edge of the canvas apparent.
For the main vertical arrangement, set the horizontal alignment to "Center" and the vertical alignment to "Top". Set the width to "Fill parent", to give the canvas more room.
For the canvas, set the height to 65-75% and the width to "Fill parent".
For the vertical scrolling arrangement of options, set the horizontal alignment to "Center".
Make sure any buttons you add have labels and component names that tell what they're for.
For the 3 color sliders, make sure they have component names which clearly distinguish them. Set the "ColorLeft" value to the color they represent. Set the width of each one to 90%, the MaxValue to 255, and the MinValue to 255. Make sure "ThumbEnabled" is checked.
For the size slider, maybe set "ColorLeft" to gray so it is clearly not a color slider. Set the width to 90%, the MaxValue to about 30, and the MinValue to 1. Make sure "ThumbEnabled" is checked, and "ThumbPosition" (the initial value for the slider) is at least 1.
Part 2: The Code
Now that you have the interface set up, it's time to make the app work!
There will be a few things to keep track of:
Update the color value or pen size when sliders are moved.
Draw on the canvas when it's touched.
Clear the canvas when the clear/erase button is clicked.
Save the drawing when the save button is pressed.
Click on the "Blocks" button in the upper-right area of your screen to edit the code.
Step 3: Color & Size Changing
To set the pen color, we're going to need something like this:
when a slider is moved:
make a color out of that slider position and the other slider positions
set the paint color to that result
From one of the color sliders' block menus, get a block for when [thisslider].PositionChanged and add it to the work area. From the canvas' block menu, get a set [canvas].PaintColor block and put it into the slider change block. Now, we need to give a color as an argument to that block. From the "Colors" block menu, get a make color (make a list) block and "plug" it into the paint color block.
Now, we need 3 arguments to give to the color list: a red value, green value, and blue value, in that order from the top. For the color whose slider you're working with, you can use the current slider position (thumb position). Hover over "thumbPosition" near the top of the when [thisslider].PositionChanged block, and click and drag a get thumbPosition block into the appropriate color slot.
For the other 2 color arguments, go to the other 2 sliders' block menus and get a [thisslider].ThumbPosition block for each of them. Alternatively, you could duplicate one (right click -> duplicate) and change the slider selected. Put these blocks into the make a list block along with the get ThumbPosition block, and make sure they are in R, G, B order.
Your code should look something like this:
Right-click and duplicate the whole when [thisslider].PositionChanged stack of blocks. Change the drop downs in this copy so that it is triggered by another slider, and it uses the slider's thumb position for a different color position. You will have to switch some of the blocks around to get different color values from different sliders. Make sure the first value is affected by the red slider, the second by the green slider, and the third by the blue slider. Do this again for the remaining color slider.
You should have something like this:
The size changing is much simpler. It works like this:
when size slider is moved:
set the line width to the thumb position
Get another when [thisslider].PositionChanged block from the size slider's block menu. From the canvas' block menu, get a set [canvas].LineWidth to block. You need a number as an argument to give to this block, which will be the position of the slider. So, hover over thumbPosition at the top of the PositionChanged block and drag out a get ThumbPosition block. Plug it into the LineWidth block, and it should look like this:
Nice work! Now, on to actually drawing!
Step 4: Drawing
It might seem unclear how to program drawing on the canvas, but luckily, the canvas object has a lot of ready-made methods for working with it, so it's pretty simple. From the canvas' block menu, get a when [canvas].Dragged block. This block has a bunch of useful variables, mostly relating to the x and y position that the user is touching. The "start" variables represent where the user put their finger down first. The "prev" variables represent where their finger was at the beginning of this drag motion. The "current" variables represent the current position of the user's finger. The difference between "start" and "prev" is confusing, but if you use "start" instead of "prev", you'll quickly understand.
Anyway, inside the when [canvas].Dragged block, we're going to put a call [canvas].DrawLine block, which is also in the canvas' block menu. This block takes 4 arguments: x1, y1, x2, and y2, where the 1's are the first point in the line, and the 2's are the endpoint of the line. Hover over the variables in the when [canvas].Dragged block and put the right "get" blocks into these argument slots.
Now is a good time to test your app. Seriously, do it now. Look for a few things in particular:
Are any of your components or controls invisible or inaccessible?
When you change the sliders, do the size and color change as you would expect? Try to draw a rainbow, to make sure that all your sliders are changing the right colors.
If you got the arguments for DrawLine wrong, you'll notice.
Go back and fix your code as needed. The drawing part should look like this:
Congratulations, you're able to draw in your app! Now go learn how to erase your bad drawings and save your nice ones.
Step 5: Clearing & Saving
Clearing your image will be pretty simple. Get a when [button].Click block for the clear button. In the canvas' block menu, get a call [canvas].Clear block and put it inside the button click block. That's it. That's your eraser. It should look like this:
Now, the save button. There's going to be two parts to this here, because we want to give the user a chance to name their drawing. There will be two events: when the save button is clicked, and when the user inputs a name. In pseudocode, this is what's going to happen:
when save button is clicked:
open the save notifier, asking for a name to save the image
when the save notifier gets a name input:
save the canvas under the filename [input response] + ".png"
From the save button's block menu, get a when [button].click block. Inside that, put a call [notifier].ShowTextDialog block, from the save notifier's block menu. This has 3 arguments: a text string for message, a text string for title, and a boolean (true or false) value for cancelable. Put text blocks (from the "Text" block menu) into the first 2 slots. The message and title can be whatever you want, even empty (but there has to be a text block anyway). They should ask the user to enter a name for saving.
From the "Logic" block menu, get a true block and plug it into the cancelable argument, so that users can change their mind about saving.
The block stack should look something like this:
However, that's just the first event handler. We now need to save the image once a name is typed.
From the save notifier's block menu, get a when [notifier].AfterTextInput block. Inside it, you'll want to put a call [canvas].SaveAs block, from the canvas' block menu. But... it doesn't fit. It has a puzzle piece tab, and you just want a regular block. That's because the SaveAs block returns a value, so it could be used as an input (or argument) for some other block. So, we need an evaluate but ignore result block to do the step, but not use the output (result). This can be found in the "Control" block menu.
So, putting these all together, you have the call [canvas].SaveAs block, plugged into the evaluate but ignore result block, inside the when [notifier].AfterTextInput block. Finally, the SaveAs block needs a text string for the fileName argument. You could just use the response from the notifier, but let's make sure there's a valid file extension on the name. From the "Text" block menu, get a join block and a text block. Plug the join block in for the fileName argument, then plug get response in for the first argument of join, and the text string '.png' for the second argument.
It should look something like this:
Congratulations, you've completed a drawing app! Share a picture you drew via the submission survey, or develop your app further and share what you did.
Step 6: Extensions
Here are some ideas for going further with your app:
Create a "Mirror" button, which will create a mirrored drawing from the user's pen strokes. Can you do a 2-way, 4-way, or rotating mirror?
Add an option to send the image to someone via text, email, or social media.
Use the camera component to take a picture to use as the drawing background.