Paint with Android

Interface creation

1. Create an Android Project

Step 1

Start a new Android project in Android Studio, choosing application and package names. We are using a minimum API level of 15 for the code in this tutorial.

Drawing Project
Drawing Project

Let Android Studio create a blank main Activity and layout - you can use the default names.

Drawing Class and Layout

Step 2

Open your project Manifest file and switch to the XML editing tab. Your Activity and SDK levels should already be set. Add the following to your Activity element opening tag, forcing the app only to use portrait:

Step 3

Before we start building the interface, let's define some numbers we will use throughout the series. In your app's "res/values" folder, if Eclipse has not already created it, add the "dimens.xml" file - if it is already there you can simply add new values to it. The outline should be as follows:

If Eclipse created the file, there may be some values in it already. We are going to use three possible brush/eraser sizes: small, medium, and large. We need to define the size for each as both a dimension and an integer value so that we can use these measurements in both the XML layout and drawable resources and the Java code:

The values for dimension and integer at each size are the same, so that the UI indicates the brush size as it will function when the user draws with it.


2. Create a Custom View Class

Step 1

We are going to define a custom View class in which the drawing will take place. In your app's source package, create a new class. Name it "DrawingView" and select "android.view.View" as the Superclass. The new class should have the following outline:

Step 2

In your new View class you will need the following import statements in addition to the View import which Eclipse should have added for you:

Add a constructor method to the class:

We will add an instance of the custom View to the XML layout file. Add the specified helper method to the class:

We will implement this method in the next part of the series, as well as adding other methods to the class.


3. Design the Activity Layout

Step 1

Open your app's "res/values" strings XML file and add some text strings we will use in the layout:

Open the app's main layout file and switch to the XML tab to edit the code. The Activity screen content will be easiest to implement using Linear Layouts. Replace the content of the layout file with the following outline:

We set vertical orientation and a gray background color.

Step 2

Inside the main Linear Layout, add another to hold the UI buttons along the top of the screen:

This time the layout is horizontal with a set height and centering applied. Inside this layout we will add the buttons for starting a new drawing, selecting a brush, selecting an eraser, and saving a drawing. We are using the following images for these buttons, although you can create your own if you prefer:

New Button
Brush Button
Eraser Button
Save Button

Copy the images to your app's drawable folder(s) - if you are creating your own you can target particular densities. To complete the tutorial series without targeting particular screen densities you can simply create a folder named "drawable" and add all of your drawable resources to it.

Let's now add an ImageButton for each option in the Activity. Start with the button to create a new drawing inside the second Linear Layout:

We will use the ID to respond to button clicks in the Activity class. We specify the new button icon image as source for the ImageButton and add a content description string.

Next add the brush button:

Now add the eraser button:

On clicking either the brush or eraser button, the user will be prompted to select a size - we will implement this later. Next add the save button:

That's it for the top Linear Layout control buttons.

Step 3

After the top button Linear Layout, but still inside the outer Linear Layout in the file, let's now add an instance of the custom view class we created:

As you can see, you can add a custom View to your layouts in much the same way as the standard Android UI elements. We set general layout properties, a white background for drawing, and provide an ID for referencing the View in Java. By retrieving a reference to this instance of the custom View class, our Activity will be able to access the methods we define in the View class declaration we created.

Step 4

Now let's add the color palette. After the custom View element, add another Linear Layout for the palette buttons:

This element will contain two rows of buttons so enter two more Linear Layouts for these. Place the following inside the one you just added:

The first row has an ID because we are going to use it in Java when the app starts to set the first default color as selected so that the user can start drawing straight away. For each color, we are going to use the following ImageButton structure:

Don't add this to your layout file yet - we will do that shortly, for now look over the code. We use one of the dimension values we defined for the color button. Notice that the background and tag attributes are the same - the background is for the appearance of the button in the UI, while the tag is so that we can set the paint color according to what the user has clicked in the Activity Java code. The element includes a method to execute on clicks of the button - we will implement this in the Activity class next time. We also specify a drawable named "paint". Add this to the project now, creating a new file in the project drawable folder(s) and naming it "paint.xml". Enter the following code in the new drawable file:

This is less complex than it looks at first glance. To create a rounded button appearance, we use two layered Shape Drawables, one a rectangle outline and the other a rounded stroke. The strokes have a gray color, with transparency in the middle through which the background color for each button will be seen (the background color being the color represented by the button).

Back in the layout file, inside the top row layout in the color palette section, add the following ImageButton elements to represent the first six colors, using the structure we outlined above:

Each button is identical apart from the colors defined in the background and tag attributes. Add the next six in the bottom row layout:

Change the colors if you like, but make sure you use the same color value in the background and tag attributes for each button. You should now be able to see the layout in the Graphical Layout tab in Eclipse:

Drawing App Design

Touch interaction

1. Prepare for Drawing

Step 1

Last time we created a class named "DrawingView" which is a custom View for the drawing functions to take place in. We created the outline of the class declaration and a method named "setupDrawing" - we will implement this now. In your DrawingView class, add the following import statements:

Next add some instance variables at the top of the class:

When the user touches the screen and moves their finger to draw, we will use a Path to trace their drawing action on the canvas. Both the canvas and the drawing on top of it are represented by Paint objects. The initial paint color corresponds to the first color in the palette we created last time, which will be initially selected when the app launches. Finally we declare variables for the canvas and bitmap - the user paths drawn with drawPaint will be drawn onto the canvas, which is drawn with canvasPaint.

Step 2

In the setupDrawing method, let's instantiate some of these variables now to set the class up for drawing. First instantiate the drawing Path and Paint objects:

Next set the initial color:

Now set the initial path properties:

We will alter part of this code in the next tutorial when we implement the ability to choose brush sizes, for now we set an arbitrary brush size. Setting the anti-alias, stroke join and cap styles will make the user's drawings appear smoother.

Complete the setupDrawing method by instantiating the canvas Paint object:

This time we set dithering by passing a parameter to the constructor.

Step 3

We need to override a couple of methods to make the custom View function as a drawing View. First, still inside the DrawingView class, override the onSizeChanged method, which will be called when the custom View is assigned a size:

Inside this method, first call the superclass method:

Now instantiate the drawing canvas and bitmap using the width and height values:

Step 4

To allow the class to function as a custom drawing View, we also need to override the onDraw method, so add it to the class now:

Inside the method, draw the canvas and the drawing path:

We have not yet implemented the ability for the user to draw the Path using the drawing Paint, but once we do this will present it in the View. Each time the user draws using touch interaction, we will invalidate the View, causing the onDraw method to execute.


2. Facilitate Drawing

Step 1

When the drawing View is on the app screen, we want user touches on it to register as drawing operations. To do this we need to listen for touch events. In your drawingView class, add the following method:

Inside the method, retrieve the X and Y positions of the user touch:

Step 2

The MotionEvent parameter to the onTouchEvent method will let us respond to particular touch events. The actions we are interested in to implement drawing are down, move and up. Add a switch statement in the method to respond to each of these:

Take a moment to look over this code. When the user touches the View, we move to that position to start drawing. When they move their finger on the View, we draw the path along with their touch. When they lift their finger up off the View, we draw the Path and reset it for the next drawing operation.

Step 3

After the switch statement, complete the method by invalidating the View and returning a true value:

Calling invalidate will cause the onDraw method to execute.


3. Choosing Colors

Step 1

Let's now implement the ability for the user to choose colors from the palette. In the app's main Activity, add the following imports:

Add the following instance variable to the class:

This represents the instance of the custom View that we added to the layout. Inside onCreate, after the existing code, instantiate this variable by retrieving a reference to it from the layout:

We now have the View that is displayed in the Activity on which we can call the methods in the DrawingView class.

Step 2

We set the initial paint color in the drawing View class, let's now set the user interface up to reflect and manage that. In the main Activity class, add another instance variable to represent the paint color button in the palette:

Inside onCreate, we now want to retrieve the first paint color button in the palette area, which is initially going to be selected. First retrieve the Linear Layout it is contained within:

Get the first button and store it as the instance variable:

We will use a different drawable image on the button to show that it is currently selected:

Add this file to your app's drawables now, giving it the name "paint_pressed.xml" and entering the following shape:

This is very similar to the "paint.xml" drawable we created last time, but with a darker color around the paint.

Step 3

Now we can let the user choose colors. When we created the layout last time, we listed an onClick attribute for the color palette buttons - add the method to your main Activity class now:

Inside this method, first check that the user has clicked a paint color that is not the currently selected one:

Inside the if block, retrieve the tag we set for each button in the layout, representing the chosen color:

We need to use the custom View class to set the color. Move to the DrawingView class now and add the following method:

Inside the method, start by invalidating the View:

Next parse and set the color for drawing:

Back in your main Activity, in the paintClicked method after retrieving the color tag, call the new method on the custom drawing View object:

Now update the UI to reflect the new chosen paint and set the previous one back to normal:

Drawing Class and Layout

Essencial functionality

1. Choosing Brush Sizes

Step 1

Last time we implemented drawing on the canvas, now we can let the user choose a brush size. The brush size options will appear when the user presses the brush button we added to the interface. To respond to this, extend the opening line of your main Activity class declaration to implement the OnClickListener interface:

You will need the following import statements added to the class for this tutorial:

Add the following instance variables to the class to store the three dimension values we defined last time:

Instantiate them in onCreate:

We will use these later. You should already have an ImageButton instance variable in the main class named "currPaint" - extend that line to add another now for the drawing button:

In onCreate retrieve a reference to the button from the layout:

Set the class up as a click listener for the button:

Step 2

Add an onClick method to the class:

Inside the method, check for clicks on the drawing button:

We will be adding conditional blocks to the onClick method for the other buttons later.

Step 3

When the user clicks the button, we will display a dialog presenting them with the three button sizes. Inside the if block, create a Dialog and set the title:

Let's define the Dialog layout in XML - add a new file in your app's "res/layout" folder, naming it "brush_chooser.xml" and entering the following outline:

Inside the Linear Layout, add a button for each size:

Each button has an ID for identification in the Activity Java code. You will notice that each one also has a content description attribute - add the specified strings to your "res/values" strings XML file:

As you can see in the layout file, each button also has a drawable file listed as its source attribute. Create new files for each of these in your "res/drawables" folder(s) now, starting with "small.xml" and entering the following content:

Notice that we use the dimension values we defined. Next add "medium.xml" to the drawables folder, entering the following shape:

Finally add "large.xml" with the following content:

Back in your main Activity class onClick method, after creating the Dialog and setting its title, you can now set the layout:

Step 4

Before we continue with the Dialog, let's alter the custom drawing View class to use different brush sizes. In your DrawingView class, add the following import statements for this tutorial:

Add two instance variables to the class:

We will use the first variable for the brush size and the second to keep track of the last brush size used when the user switches to the eraser, so that we can revert back to the correct size when they decide to switch back to drawing. In the setupDrawing method, before the code already in there, add the following to instantiate these variables:

We use the dimension value for the medium sized brush to begin with. You can now update the line in the method where you set the stroke width with a hard-coded value to use this variable value instead:

Add the following method to the class to set the brush size:

Inside the method, update the brush size with the passed value:

We will be passing the value from the dimensions file when we call this method, so we have to calculate its dimension value. We update the variable and the Paint object to use the new size. Now add methods to get and set the other size variable we created:

We will call these methods from the main Activity class.

Step 5

Back in your main Activity class, let's complete the Dialog code in the onClick method. After setting the content view on the Dialog object, listen for clicks on the three size buttons, starting with the small one:

We set the size using the methods we added to the custom View class as soon as the user clicks a brush size button, then immediately dismiss the Dialog. Next do the same for the medium and large buttons:

Complete the draw button section of onClick by displaying the Dialog:

The Dialog will display until the user makes a selection or goes back to the Activity.

Brush Dialog

Step 6

Use the new method to set the initial brush size in onCreate:


2. Erasing

Step 1

Now let's add erasing to the app. In the custom drawing View class, add a boolean instance variable to act as a flag for whether the user is currently erasing or not:

Initially we will assume that the user is drawing, not erasing. Add the following method to the class:

Inside the method, first update the flag variable:

Now alter the Paint object to erase or switch back to drawing:

If you're looking for an advanced topic to explore, have a look at the PorterDuff.Mode options.

Step 2

Back in the main Activity class, add another ImageButton to the list of instance variables:

In onCreate, retrieve a reference to the button and set the class up to listen for clicks:

Add a conditional statement for the button in onClick after the conditional for the draw button:

As with the draw button, we will let the user choose an eraser size from a Dialog. Inside the conditional block for the erase button, create and prepare the Dialog as before:

We use the same layout as the draw button Dialog. Setup click listeners for the size buttons as before, this time calling the new erase method we added to the View class:

We call the method to set the brush size as with the draw button, this time first setting the erase flag to true. Finally, display the Dialog:

Erase Dialog

The user will be able to erase using touch interaction as with drawing:

Drawing Erased

Step 3

When the user clicks the draw button and chooses a brush size, we need to set back to drawing in case they have previously been erasing. In the three click listeners you added for the small, medium and large buttons in the draw button section of onClick, call the erase method with a false parameter - add this in each onClick before calling dismiss on the "brushDialog" object:

When the user has been erasing and clicks a paint color button, we will assume that they want to switch back to drawing. In the paintClicked method, before the existing code, call the erase method, passing false:

Still inside paintClicked, set the brush size back to the last one used when drawing rather than erasing:

This type of processing is motivated by assumptions about what the user wants to do based on their actions - you could potentially enhance this app along these lines, so bear that in mind if you want to carry on working on the app later.


3. New Drawings

Step 1

We added a button for the user to start a new drawing, so let's implement that now. Add another to the list of ImageButton instance variables in your main Activity class:

Instantiate it with a reference to the button listed in the layout, in onCreate, also listening for clicks:

Step 2

In onClick, add another conditional block for the new button:

In your custom drawing View class, add a method to start a new drawing:

The method simply clears the canvas and updates the display.

Back in the main Activity class conditional block for the new button in onCreate, let's verify that the user definitely wants to start a new drawing:

The Dialog lets the user change their mind, calling the new method if they decide to go ahead and start a new drawing, in which case the current drawing is cleared.

New Drawing

4. Save Drawings

Step 1

The one remaining part of the app functionality is the ability to save drawings to the device. Add the save button as the last in the sequence of ImageButton instance variables in the main Activity class:

Instantiate it and listen for clicks in onCreate:

Add a conditional for it in onClick:

Let's use a similar algorithm to the one we used for creating new drawings, to check that the user wants to go ahead and save:

Save Dialog

If the user chooses to go ahead and save, we need to output the currently displayed View as an image. In the onClick method for the save Dialog, start by enabling the drawing cache on the custom View:

Now attempt to write the image to a file:

Take a moment to look over this code. We use the insertImage method to attempt to write the image to the media store for images on the device, which should save it to the user gallery. We pass the content resolver, drawing cache for the displayed View, a randomly generated UUID string for the filename with PNG extension and a short description. The method returns the URL of the image created, or null if the operation was unsuccessful - this lets us give user feedback:

Destroy the drawing cache so that any future drawings saved won't use the existing cache:

Finally, add the following permission to your project manifest file:

Drawing Saved

On browsing to the device gallery, the user should now be able to see their drawing image:

Drawing In Gallery

Conclution

Finally we have a app Paint ready to work.