Advertisement

Android SDK: Create a Drawing App – Touch Interaction

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

In this series, we will create a finger-painting app for Android using touch interaction. The user will be able to select from a color palette, choose a brush size, erase, create a new drawing, or save their existing drawing to the device gallery.


Series Format

This series on Creating a Drawing App will be in three parts:


Final Preview

Drawing App

In the first part of the series we created the user interface. In this second part we will implement drawing on the canvas and choosing colors. In the final part of the series we will introduce the ability to erase, to create new drawings and to save a drawing to the gallery on the user device. We will look at options you can use to enhance this app in future tutorials, including pattern fills, opacity and interaction other than touchscreen.


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:

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;

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

//drawing path
private Path drawPath;
//drawing and canvas paint
private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;

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:

drawPath = new Path();
drawPaint = new Paint();

Next set the initial color:

drawPaint.setColor(paintColor);

Now set the initial path properties:

drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(20);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);

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:

canvasPaint = new Paint(Paint.DITHER_FLAG);

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:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//view given size
}

Inside this method, first call the superclass method:

super.onSizeChanged(w, h, oldw, oldh);

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

canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);

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:

@Override
protected void onDraw(Canvas canvas) {
//draw view
}

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

canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);

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:

@Override
public boolean onTouchEvent(MotionEvent event) {
//detect user touch		
}

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

float touchX = event.getX();
float touchY = event.getY();

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:

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
	drawPath.moveTo(touchX, touchY);
	break;
case MotionEvent.ACTION_MOVE:
	drawPath.lineTo(touchX, touchY);
	break;
case MotionEvent.ACTION_UP:
	drawCanvas.drawPath(drawPath, drawPaint);
	drawPath.reset();
	break;
default:
	return false;
}

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:

invalidate();
return true;

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:

import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;

Add the following instance variable to the class:

private DrawingView drawView;

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:

drawView = (DrawingView)findViewById(R.id.drawing);

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:

private ImageButton currPaint;

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:

LinearLayout paintLayout = (LinearLayout)findViewById(R.id.paint_colors);

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

currPaint = (ImageButton)paintLayout.getChildAt(0);

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

currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));

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

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
	<item>
		<shape android:shape="rectangle" >
			<stroke
				android:width="4dp"
				android:color="#FF333333" />

			<solid android:color="#00000000" />

			<padding
				android:bottom="0dp"
				android:left="0dp"
				android:right="0dp"
				android:top="0dp" />
		</shape>
	</item>
	<item>
		<shape xmlns:android="http://schemas.android.com/apk/res/android" >
			<stroke
				android:width="4dp"
				android:color="#FF333333" />

			<solid android:color="#00000000" />

			<corners android:radius="10dp" />
		</shape>
	</item>
</layer-list>

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:

public void paintClicked(View view){
	//use chosen color
}

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

if(view!=currPaint){
//update color
}

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

ImageButton imgView = (ImageButton)view;
String color = view.getTag().toString();

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

public void setColor(String newColor){
//set color		
}

Inside the method, start by invalidating the View:

invalidate();

Next parse and set the color for drawing:

paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);

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

drawView.setColor(color);

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

imgView.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint));
currPaint=(ImageButton)view;

Conclusion

You can now run the app and draw on the canvas, choosing colors to draw with. You should see the color palette buttons reflect the currently chosen color. In this tutorial we have worked through the essential features of any touch-drawing app for Android, so you should now have the basic skills to implement your own drawing functions in other apps. In the final part of the series we will implement erasing, choosing brush and eraser sizes, saving drawings and starting new drawings.

Advertisement