In this series, we will create 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:
- Interface Creation
- Touch Interaction
- Essential Functionality (Pending Publication)
Final Preview
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.