Android Canvas

Filed Under: Android

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.

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.

Since canvas is drawn on the screen, views that are drawn need to be measured in terms of pixels (px).

Let’s look at how to draw basic stuff on Canvas through our sample android studio project.

Android Canvas Example Project Structure

Android Canvas Basics Project

Android Canvas Example Project

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:

Android Canvas Coordinate System

Android Canvas Coordinate System

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="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://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.

Why is Anti Alias flag needed in Paint?
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:

Android Canvas Basics Output 1

Android Canvas Basics Output 1

Android Canvas Basics Output 2

Android Canvas Basics Output 2

That brings an end to this tutorial. You can download the project from the link below:

Comments

  1. Ashutosh says:

    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?

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages