Canvas

Android Canvas Example

Android provides a set of APIs for 2D-drawing that allow you to render your custom graphics on a canvas or modify the existing Views. When drawing 2D graphics, you have two choices to work with:

  1. Draw your graphics or animations into a View object from your layout. In this way, the drawing of your graphics is handled by the system’s normal View hierarchy drawing process and you simply define the graphics to go inside the View.
  2. Draw your graphics directly to a Canvas. This way, you personally call the appropriate class’s onDraw() method (passing it Canvas), or one of the Canvas draw…() methods. In doing so, you are also in control of any animation.

When we have an activity that uses 2d graphics, such a video game application, drawing to a Canvas, is the best way to work with, as our application will meet the need to regularly re-draw itself. For such an application, we can work our graphics either on the same thread, the UI Activity thread, and create custom Canvas View components in your layout, or work on a separate thread, meaning that we will work on a SurfaceView and perform draws to the canvas in a faster way.

In this example, we are going to see the use of Android Canvas on the UI Activity thread, and we are going to make a custom component, in order to achieve a small finger-drawing application. You can also read about Canvas and Drawables from the Android Developers Guide

For our example will use the following tools in a Windows 64-bit or an OS X platform:

  • JDK 1.7
  • Eclipse 4.2 Juno
  • Android SDK 4.4.2

Let’s take a closer look:

1. Create a New Android Application Project

Tip
You may skip project creation and jump directly to the beginning of the example below.

Open Eclipse IDE and go to File → New → Project → Android Application Project.

Figure 2. Create a new Android project
Figure 1. Create a new Android project

Specify the name of the application, the project and the package and then click Next.

Figure 2. Create a new Android project name
Figure 2. Create a new Android project name

In the next window, the “Create Activity” option should be checked. The new created activity will be the main activity of your project. Then press Next button.

Create Activity
Figure 3. Configure the project

In “Configure Launcher Icon” window you should choose the icon you want to have in your app. We will use the default icon of android, so click Next.

Configure Launcher Icon
Figure 4. Configure the launcher icon

Select the “Blank Activity” option and press Next.

Blank Activity
Figure 5. Create the activity and select its type

You have to specify a name for the new Activity and a name for the layout description of your app. The .xml file for the layout will automatically be created in the res/layout folder. It will also be created a fragment layout xml, that we are not going to use in this project and you can remove it if you want. Then press Finish.

Figure 6. Create a new blank activity
Figure 6. Create a new blank activity

Here you can see, how will the structure of the project become when finished:

Figure 7. The tree of the project
Figure 7. The tree of the project

2. Creating the layout of the main AndroidCanvasExample

We are going to make a very simple layout xml for the AndroidCanvasExample.class, that only consists of a FrameLayout that contains the custom CanvasView, the component from the custom class we are going to make in the lines below. He have chosen the FrameLayout for our external layout, because we want to add a “Clear Button” that will clear, invalidate and empty our canvas. This FrameLayout helped us add the Button above the canvas, like placing it on a seperate layout on top.

Open res/layout/activity_main.xml, go to the respective xml tab and paste the following:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    android:orientation="vertical" >

    <com.javacodegeeks.androidcanvasexample.CanvasView
        android:id="@+id/signature_canvas"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textColor="#FFFFFF" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:onClick="clearCanvas"
        android:text="Clear Canvas" />

</FrameLayout>

3. Creating the source code of the main AndroidCanvasExample Activity

Open src/com.javacodegeeks.androidcanvasexample/AndroidCanvasExample.java file and paste the code below.

AndroidCanvasExample.java

package com.javacodegeeks.androidcanvasexample;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;

public class AndroidCanvasExample extends Activity {

	private CanvasView customCanvas;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
	}

	public void clearCanvas(View v) {
		customCanvas.clearCanvas();
	}

}

This activity is just a simple helper activity, it only includes the layout in which we have added our custom CanvasView.

4. Creating the source code of the CanvasView

Open src/com.javacodegeeks.androidcanvasexample/CanvasView.java file and paste the code below.

CanvasView.java

package com.javacodegeeks.androidcanvasexample;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class CanvasView extends View {

	public int width;
	public int height;
	private Bitmap mBitmap;
	private Canvas mCanvas;
	private Path mPath;
	Context context;
	private Paint mPaint;
	private float mX, mY;
	private static final float TOLERANCE = 5;

	public CanvasView(Context c, AttributeSet attrs) {
		super(c, attrs);
		context = c;

		// we set a new Path
		mPath = new Path();

		// and we set a new Paint with the desired attributes
		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Paint.Style.STROKE);
		mPaint.setStrokeJoin(Paint.Join.ROUND);
		mPaint.setStrokeWidth(4f);
	}

	// override onSizeChanged
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);

		// your Canvas will draw onto the defined Bitmap
		mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		mCanvas = new Canvas(mBitmap);
	}

	// override onDraw
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// draw the mPath with the mPaint on the canvas when onDraw
		canvas.drawPath(mPath, mPaint);
	}

	// when ACTION_DOWN start touch according to the x,y values
	private void startTouch(float x, float y) {
		mPath.moveTo(x, y);
		mX = x;
		mY = y;
	}

	// when ACTION_MOVE move touch according to the x,y values
	private void moveTouch(float x, float y) {
		float dx = Math.abs(x - mX);
		float dy = Math.abs(y - mY);
		if (dx >= TOLERANCE || dy >= TOLERANCE) {
			mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
			mX = x;
			mY = y;
		}
	}

	public void clearCanvas() {
		mPath.reset();
		invalidate();
	}

	// when ACTION_UP stop touch
	private void upTouch() {
		mPath.lineTo(mX, mY);
	}

	//override the onTouchEvent
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			startTouch(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_MOVE:
			moveTouch(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			upTouch();
			invalidate();
			break;
		}
		return true;
	}
}

Lets see the code above in detail.

In this code snippet, we set up a new Canvas. This canvas will draw onto the defined Bitmap.

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
		mCanvas = new Canvas(mBitmap);
	}

Here, we get the x and y event coordinates in order to make our path moves.

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX();
		float y = event.getY();

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			startTouch(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_MOVE:
			moveTouch(x, y);
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			upTouch();
			invalidate();
			break;
		}
		return true;
	}

We transform the x,y event coordinates into path moves.

	private void moveTouch(float x, float y) {
		float dx = Math.abs(x - mX);
		float dy = Math.abs(y - mY);
		if (dx >= TOLERANCE || dy >= TOLERANCE) {
			mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
			mX = x;
			mY = y;
		}
	}

And by overriding the onDraw event, we draw our path onto the canvas.

	// override onDraw
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawPath(mPath, mPaint);
	}

5. Build, compile and run

When we build, compile and run our project, the main AndroidCanvasExample should look like this:

Figure 8. Figure This is how the main Activity looks like
Figure 8. Figure This is how the main Activity looks like.

Download the Eclipse Project

This was an example of Android AndroidCanvasExample.

Download
You can download the full source code of this example here: AndroidCanvasExample.zip

Chryssa Aliferi

Chryssa is a Computer Science graduate from Athens University of Economic and Business. During her studies, Chryssa carried out a great variety of projects ranging from networking to software engineering. She is very keen on front end development especially on mobile technologies and web applications. She has worked as a junior Software Engineer in the telecommunications area and currently works as an Android Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Pedro
Pedro
6 years ago

Hi! Could you please help me understand something? Why did you create a new Bitmap and Canvas on the onSizeChanged() callback? Nothing seems to be using mCanvas… onDraw() is drawing straight to the preexisting view canvas, isn’t it?

Thanks!

Trieu Vu
Trieu Vu
6 years ago
Reply to  Pedro

“In order to properly draw your custom view, you need to know what size it is…onSizeChanged() is called when your view is first assigned a size, and again if the size of your view changes for any reason. Calculate positions, dimensions, and any other values related to your view’s size in onSizeChanged(), instead of recalculating them every time you draw”

Link: https://developer.android.com/training/custom-views/custom-drawing.html. Handle Layout Events Section.

Gamal Defianur
Gamal Defianur
5 years ago

Thanks a lot, this is work 100%

Yanis
Yanis
5 years ago

Hi, tnx for the explaining.
May be you can answer me, how can i use in my “class extend View”, ImageView from MainActivity

Yanis
Yanis
5 years ago
Reply to  Yanis

Wrong question: I override the onSizeChange method, but when i try to draw for example a rectangle, i do not see it on my screen, appreciate for help

rob
rob
4 years ago

my god this is why I don’t prefer Java all I wanted was a simple code example to be able to draw a line on a canvas good lord ooooooooffffffff. I m glad it works for everyone else but geeez . Can we get simple ?

TropicalCoder
4 years ago

Works as advertised. Thanks!

Back to top button