11.1A: Creating a simple Canvas object
Contents:
- What you should already KNOW
- What you will LEARN
- What you will DO
- App overview
- Task 1. Create a canvas and draw on it
- Solution code
- Summary
- Related concept
- Learn more
When you want to create your own custom 2D drawings for Android, you can do so in the following ways.
- Draw your graphics or animations on a
View
object in your layout. By using this option, the system's rendering pipeline handles your graphics—it's your responsibility to define the graphics inside the view. - Draw your graphics in a
Canvas
object. To use this option, you pass yourCanvas
to the appropriate class'onDraw(Canvas)
method. You can also use the drawing methods inCanvas
. This option also puts you in control of any animation.
Drawing to a view is a good choice when you want to draw simple graphics that don't need to change dynamically, and when your graphics aren't part of a performance-intensive app such as a game. For example, you should draw your graphics into a view when you want to display a static graphic or predefined animation, within an otherwise static app. For more information, read Drawables
.
Drawing to a canvas is better when your app needs to regularly redraw itself. Apps, such as video games, should draw to the canvas on their own. This practical shows you how to create a canvas, associate it with a bitmap, and associate the bitmap with an ImageView
for display.
When you want to draw shapes or text into a view on Android, you need:
- A
Canvas
object. Very simplified, aCanvas
is a logical 2D drawing surface that provides methods for drawing onto a bitmap. - An instance of the
Bitmap
class which represents the physical drawing surface and gets pushed to the display by the GPU. - A
View
instance associated with the bitmap. - A
Paint
object that holds the style and color information about how to draw geometries, text, and on bitmap. - The
Canvas
class also provides methods for clipping views. Clipping is the action of defining geometrically what portion of the canvas the user sees in the view. This visible portion is called the viewport in graphics terminology.
The figure below shows all the pieces required to draw to a canvas.
You do not need a custom view to draw, as you learn in this practical. Typically you draw by overriding the onDraw()
method of a View
, as shown in the next practicals.
See the Graphics Architecture series of articles for an in-depth explanation of how the Android framework draws to the screen.
What you should already KNOW
You should be able to:
- Create apps with Android Studio and run them on a physical or virtual mobile device.
- Add a click event handler to a
View
. - Create and display a custom
View
.
What you will LEARN
You will learn how to:
- Create a
Canvas
object, associate it with aBitmap
object, and display the bitmap in anImageView
. - Style drawing properties with a
Paint
object. - Draw on a canvas in response to a click event.
What you will DO
- Create an app that draws on the screen in response to touch events.
App overview
As you build the SimpleCanvas app, you learn how to create a canvas, associate it with a bitmap, and associate the bitmap with an ImageView
for display.
When the user clicks in the app, a rectangle appears. As the user continues to click, the app draws increasingly smaller rectangles onto the canvas.
When you start the app, you see a white surface, the default background for the ImageView
.
Tap the screen, and it fills with orange color, and the underlined text "Keep tapping" is drawn. For the next four taps, four differently colored inset rectangles are drawn. On the final tap, a circle with centered text tells you that you are "Done!", as shown in the screenshot below.
If the device is rotated, the drawing is reset, because the app does not save state. In this case, this behavior is "by design," to give you a quick way of clearing the canvas.
Task 1. Create a canvas and draw on it
You can associate a Canvas
with an ImageView
and draw on it in response to user actions. This basic implementation of drawing does not require a custom View
. You create an app with a layout that includes an ImageView
that has a click handler. You implement the click handler in MainActivity
to draw on and display the Canvas
.
1.1 Create the SimpleCanvas project and layout
- Create the SimpleCanvas project with the Empty Activity template.
- In
activity_main.xml
, replace theTextView
with anImageView
that fills the parent. Add an
onClick
property to theImageView
and create a stub for the click handler calleddrawSomething()
. Your XML code should look similar to this.<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.simplecanvas.MainActivity"> <ImageView android:id="@+id/myimageview" android:layout_width="match_parent" android:layout_height="match_parent" android:onClick="drawSomething"/> </android.support.constraint.ConstraintLayout>
- Add the following color resources to the
colors.xml
file.<color name="colorRectangle">#455A64</color> <color name="colorBackground">#FFFFD600</color>
- Add the following string resources to the
strings.xml
file.<string name="keep_tapping">Keep tapping.</string> <string name="done">Done!</string>
1.2 Create the SimpleCanvas member variables and constants
In MainActivity.java
:
Create a
Canvas
member variablemCanvas
.The
Canvas
object stores information on what to draw onto its associated bitmap. For example, lines, circles, text, and custom paths.private Canvas mCanvas;
Create a
Paint
member variablemPaint
and initialize it with default values.The
Paint
objects store how to draw. For example, what color, style, line thickness, or text size.Paint
offers a rich set of coloring, drawing, and styling options. You customize them below.private Paint mPaint = new Paint();
- Create a
Paint
object for underlined text.Paint
offers a full complement of typographical styling methods. You can supply these styling flags when you initialize the object or set them later.private Paint mPaintText = new Paint(Paint.UNDERLINE_TEXT_FLAG);
Create a
Bitmap
member variablemBitmap
.The
Bitmap
represents the pixels that are shown on the display.private Bitmap mBitmap;
Create a member variable for the
ImageView
,mImageView
.A view, in this example an
ImageView
, is the container for the bitmap. Layout on the screen and all user interaction is through the view.private ImageView mImageView;
- Create two
Rect
variables,mRect
andmBounds
and initialize them to rectangles.private Rect mRect = new Rect(); private Rect mBounds = new Rect();
- Create a constant
OFFSET
initialized to 120, and initialize a member variablemOffset
with the constant. This offset is the distance of a rectangle you draw from the edge of the canvas.private static final int OFFSET = 120; private int mOffset = OFFSET;
- Create a
MULTIPLIER
constant initialized to 100. You will need this constant later, for generating random colors.private static final int MULTIPLIER = 100;
- Add the following private member variables for colors.
private int mColorBackground; private int mColorRectangle; private int mColorAccent;
1.3 Fix the onCreate method and customize the mPaint member variable
In MainActivity.java:
- Verify that
onCreate()
looks like the code below.@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
- In
onCreate()
, get color resources and assign them to the color member variables.mColorBackground = ResourcesCompat.getColor(getResources(), R.color.colorBackground, null); mColorRectangle = ResourcesCompat.getColor(getResources(), R.color.colorRectangle, null); mColorAccent = ResourcesCompat.getColor(getResources(), R.color.colorAccent, null);
- In
onCreate()
, set the color ofmPaint
tomColorBackground
.mPaint.setColor(mColorBackground);
- In
onCreate()
, set the color formPaintText
to the theme colorcolorPrimaryDark
, and set the text size to 70. Depending on the screen size of your device, you may need to adjust the text size.mPaintText.setColor( ResourcesCompat.getColor(getResources(), R.color.colorPrimaryDark, null) ); mPaintText.setTextSize(70);
- Get a reference to the image view.
mImageView = (ImageView) findViewById(R.id.myimageview);
Important: You cannot create theCanvas
inonCreate()
, because the views have not been laid out, so their final size is not available. When you create a custom view in a later lesson, you learn different ways to initialize your drawing surface.
1.4 Implement the drawSomething() click handler method
The drawSomething()
method is where all the interaction with the user and drawing on the canvas are implemented.
The drawSomething()
click handler responds to user taps by drawing an increasingly smaller rectangle until it runs out of room. Then it draws a circle with the text "Done!" to demonstrate basics of drawing on canvas.
You always need to do at least the following:
- Create
Bitmap
. - Associate
Bitmap
withView
. - Create
Canvas
withBitmap
. - Draw on
Canvas
. - Call
invalidate()
on theView
to force redraw.
Inside the drawSomething()
method, add code as follows.
- Create or verify the signature for the
drawSomething()
method.public void drawSomething(View view) {}
- Get the width and height of the view and create convenience variables for half the width and height. You must do this step every time the method is called, because the size of the view can change (for example, when the device is rotated).
int vWidth = view.getWidth(); int vHeight = view.getHeight(); int halfWidth = vWidth / 2; int halfHeight = vHeight / 2;
Add an if-else statement for
(mOffset == OFFSET)
.When
drawSomething()
is called, the app is in one of three states:mOffset == OFFSET
. The app is only in this state the first time the user taps. Create theBitmap
, associate it with theView
, create theCanvas
, fill the background, and draw some text. Increase the offset.mOffset != OFFSET
and the offset is smaller than half the screen width and height. Draw a rectangle with a computed color and increase the offset.mOffset != OFFSET
and the offset is equal to or larger than half the screen width and height. Draw a circle with the text "Done!".if (mOffset == OFFSET) { } else { if (mOffset < halfWidth && mOffset < halfHeight) { } else { } }
Inside the outer
if
statement(mOffset == OFFSET)
, create aBitmap
.Supply the width and height for the bitmap, which are going to be the same as the width and height of the view.
- Pass in a
Bitmap.config
configuration object. A bitmap configuration describes how pixels are stored. How pixels are stored affects the quality (color depth) as well as the ability to display transparent/translucent colors. TheARGB_8888
format supportsAlpha
, Red, Green, and Blue channels for each pixel. Each color is encoded in 8 bits, for a total of 4 bytes per pixel.mBitmap = Bitmap.createBitmap(vWidth, vHeight, Bitmap.Config.ARGB_8888);
- Associate the bitmap with the
ImageView
.mImageView.setImageBitmap(mBitmap);
- Create a
Canvas
and associate it withmBitmap
, so that drawing on the canvas draws on the bitmap.mCanvas = new Canvas(mBitmap);
- Fill the entire canvas with the background color.
mCanvas.drawColor(mColorBackground);
- Draw the "Keep tapping" text onto the canvas. You need to supply a string, x and y positions, and a
Paint
object for styling.mCanvas.drawText(getString(R.string.keep_tapping), 100, 100, mPaintText);
- Increase the offset.
mOffset += OFFSET;
At the end of the
drawSomething()
method,invalidate()
the view so that the system redraws the view every timedrawSomething()
is executed.When a view is invalidated, the system does not draw the view with the values it already has. Instead, the system recalculates the view with the new values that you supply. The screen refreshes 60 times a second, so the view is drawn 60 times per second. To save work and time, the system can reuse the existing view until it is told that the view has changed, the existing view is invalid, and the system thus has to recalculate an updated version of the view.
view.invalidate();
Note: If you run the app at this point, it should start with a blank screen, and when you tap, the screen fills and the text appears.In the
else
block, inside theif
statementSet the color of
mPaint
. This code generates the next color by subtracting the current offset times a multiplier from the original color. A color is represented by a single number, so you can manipulate it in this way for some fun effects.- Change the size of the
mRect
rectangle to the width of the view, minus the current offset. - Draw the rectangle with
mPaint
styling. - Increase the offset.
Below is the complete if portion of the code.
if (mOffset < halfWidth && mOffset < halfHeight) {
// Change the color by subtracting an integer.
mPaint.setColor(mColorRectangle - MULTIPLIER*mOffset);
mRect.set(
mOffset, mOffset, vWidth - mOffset, vHeight - mOffset);
mCanvas.drawRect(mRect, mPaint);
// Increase the indent.
mOffset += OFFSET;
}
In the
else
statement, when the offset is too large to draw another rectangle:Set the color of
mPaint
.- Draw a circle with the paint.
- Get the "Done" string and calculate its bounding box, then calculate x and y to draw the text at the center of the circle. The bounding box defines a rectangle that encloses the string. You cannot make calculations on a string, but you can use the dimensions of the bounding box to calculate, its center.
else { mPaint.setColor(mColorAccent); mCanvas.drawCircle(halfWidth, halfHeight, halfWidth / 3, mPaint); String text = getString(R.string.done); // Get bounding box for text to calculate where to draw it. mPaintText.getTextBounds(text, 0, text.length(), mBounds); // Calculate x and y for text so it's centered. int x = halfWidth - mBounds.centerX(); int y = halfHeight - mBounds.centerY(); mCanvas.drawText(text, x, y, mPaintText); }
- Run your app and tap multiple times to draw. Rotate the screen to reset the app.
Solution code
Android Studio project: SimpleCanvas.
Summary
- To draw on the display of a mobile device with Android you need a
View
, aCanvas
, aPaint
, and aBitmap
object. - The
Bitmap
is the physical drawing surface. TheCanvas
provides an API to draw on the bitmap, thePaint
is for styling what you draw, and theView
displays theBitmap
. - You create a
Bitmap
, associate it with aView
, create aCanvas
with aPaint
object for theBitmap
, and then you can draw. - You must
invalidate()
the view when your are done drawing, so that the Android System redraws the display. - All drawing happens on the UI thread, so performance matters.
Related concepts
The related concept documentation is in The Canvas class.
Learn more
Android developer documentation:
Canvas
classBitmap
classView
classPaint
classBitmap.config
configurations- Canvas and Drawables
- Graphics Architecture series of articles (advanced)