In this tutorial, we’ll be discussing a very important part of Android i.e. Canvas. It’s a territory which every developer prefers to stay away from. The reason for this tutorial is to make you more aware and at ease with the Android Canvas.
Table of Contents
Android Canvas
Many times you end up in a scenario where you need to implement a custom View and/or animate it. It can happen that the requirements are so specific that you need to implement it from scratch. Canvas plays a vital role in building such Custom Views.
A Canvas is a 2D drawing framework that provides us with methods to draw on the underlying Bitmap. A Bitmap acts as the surface over which the canvas is placed. The Paint class is used to provide colors and styles.
Before we dig deep into Canvas, let’s look at the lifecycle of a Custom View.
A Custom View consists of the following commonly used methods:
onMeasure()
onLayout()
onDraw()
Inside onMeasure
we determine the size of the view and its children.
Inside onLayout
the size is assigned to the view.
The onDraw
method is where the canvas is drawn.
A Canvas Object basically comes as a parameter inside the onDraw
method.
The invalidate()
method is used to redraw the view. It calls the onDraw method again. Typically, this is used when the text or color or view needs to be updated based on certain events.
The Canvas class contains methods to draw certain shapes just as line, arc, circle, over. Moreover, we can draw complex geometry too using Paths
.
We can also draw text or just simply Paint color on the canvas too.
Let’s look at how to draw basic stuff on Canvas through our sample android studio project.
Android Canvas Example Project Structure
To create a Path, two methods are important: moveTo()
and lineTo()
.
moveTo
takes you to the coordinates you specify on the screen.
lineTo
draws a line from the current position to the one specified.
close()
is used to close a path.
How does the Canvas Coordinate System work?
In the canvas 0,0 is the top left point. As you move horizontally right the x coordinate increases.
As you move vertically down the y coordinate increases in pixels.
Following diagram illustrates the same:
Rect vs RectF
Rect object is used to create a rectangle in which each side is specified in integer value.
RectF does the same but in float values.
In the following example, we’ll see how to draw the various shapes and also use Paint to style them.
Android Canvas Example Code
The code for the activity_main.xml
layout is a basic root layout in which we’ll add our Canvas:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" />
The code for the MainActivity.java
is given below:
package com.journaldev.androidcanvasbasics;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class MainActivity extends AppCompatActivity {
LinearLayout linearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
linearLayout = findViewById(R.id.linearLayout);
MyView myView = new MyView(this);
linearLayout.addView(myView);
}
}
The MyView class is where we create our Custom View. Let’s finally draw on the Canvas!
package com.journaldev.androidcanvasbasics;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.DisplayMetrics;
import android.view.View;
public class MyView extends View {
Paint mPaint, otherPaint, outerPaint, mTextPaint;
RectF mRectF;
int mPadding;
float arcLeft, arcTop, arcRight, arcBottom;
Path mPath;
public MyView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(5);
mTextPaint = new Paint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(pxFromDp(context, 24));
otherPaint = new Paint();
outerPaint = new Paint();
outerPaint.setStyle(Paint.Style.FILL);
outerPaint.setColor(Color.YELLOW);
mPadding = 100;
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager()
.getDefaultDisplay()
.getMetrics(displayMetrics);
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
arcLeft = pxFromDp(context, 20);
arcTop = pxFromDp(context, 20);
arcRight = pxFromDp(context, 100);
arcBottom = pxFromDp(context, 100);
Point p1 = new Point((int) pxFromDp(context, 80) + (screenWidth / 2), (int) pxFromDp(context, 40));
Point p2 = new Point((int) pxFromDp(context, 40) + (screenWidth / 2), (int) pxFromDp(context, 80));
Point p3 = new Point((int) pxFromDp(context, 120) + (screenWidth / 2), (int) pxFromDp(context, 80));
mPath = new Path();
mPath.moveTo(p1.x, p1.y);
mPath.lineTo(p2.x, p2.y);
mPath.lineTo(p3.x, p3.y);
mPath.close();
mRectF = new RectF(screenWidth / 4, screenHeight / 3, screenWidth / 6, screenHeight / 2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRoundRect(mRectF, 10, 10, otherPaint);
canvas.clipRect(mRectF, Region.Op.DIFFERENCE);
canvas.drawPaint(outerPaint);
canvas.drawLine(250, 250, 400, 400, mPaint);
canvas.drawRect(mPadding, mPadding, getWidth() - mPadding, getHeight() - mPadding, mPaint);
canvas.drawArc(arcLeft, arcTop, arcRight, arcBottom, 75, 45, true, mPaint);
otherPaint.setColor(Color.GREEN);
otherPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(
getLeft() + (getRight() - getLeft()) / 3,
getTop() + (getBottom() - getTop()) / 3,
getRight() - (getRight() - getLeft()) / 3,
getBottom() - (getBottom() - getTop()) / 3, otherPaint);
canvas.drawPath(mPath, mPaint);
otherPaint.setColor(Color.BLACK);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, arcLeft, otherPaint);
canvas.drawText("Canvas basics", (float) (getWidth() * 0.3), (float) (getHeight() * 0.8), mTextPaint);
}
public static float pxFromDp(final Context context, final float dp) {
return dp * context.getResources().getDisplayMetrics().density;
}
}
pxFromDp
method is used to get the pixel equivalent of the dp value passed.
canvas.drawLine(250, 250, 400, 400, mPaint);
simply draws a line from x1 y1 to x2 y2.
The following code is used to draw a rectangle at the center of the screen.
canvas.drawRect(
getLeft() + (getRight() - getLeft()) / 3,
getTop() + (getBottom() - getTop()) / 3,
getRight() - (getRight() - getLeft()) / 3,
getBottom() - (getBottom() - getTop()) / 3, otherPaint);
Inside the drawCircle
method, the first two parameters passed are the coordinates of the center of the circle. The third parameter sets the radius for the circle.
clipRect
clips the canvas with the rectangle. The last parameter sets the clipping region style.
Region.Op.DIFFERENCE
sets the Paint on the region between the canvas and the rectangle specified in the clipRect method.
Inside the drawText
method the two float values set the origin position of the text. We’ve set it at 30 percent from the left and 80 % from the top of the screen.
An Anti alias flag ensures that the shape is smooth.
How are the different shapes drawn on the canvas positioned relative to each other?
Canvas Z-Order
Elements are arranged on top of each other in the order in which they are drawn. Similar to a frame layout based on the z-order.
The output of the above application is given below:
That brings an end to this tutorial. You can download the project from the link below:
1.I am trying to implement a Drawing Application in Android. Where the user should be able to select and move the drawn shapes. Currently i have statically drawn some rects and text on my Drawing Canvas. 2.I want to select (draw borders on the selected shape) and move the drawn Shapes in onTouch events of the drawing canvas.Can some one please guide me about this, any help is Highly Appreciated.
Hey, I’m an Android Intern student, right now i am working on a real-time project which is used to book open plots(Land Ventures). How to use design controls to map exactly in the same way as the open plots layout looks and implement click on every land/plot area. Can you help me with this..?
Hey! One doubt though.. Why did you use DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
to get the screenWidth and screenHeight?
you could easily get from getWidth() and getHeight()? why so long?