When you create you own custom view you will probably give the view its own special appearance. You probably want to draw the elements of the view onto the screen somehow. This is achieved by overriding the onDraw
method of the View
class. The signature of this method is
protected void onDraw (Canvas canvas);
When you implement the method you are given a Canvas
object on which you can draw you view. This method will be called when the system decides that your view should be rendered onto the screen. The following implementation of the onDraw
method will draw a red circle that is centered in the view and almost filles the area occupied by the view, leaving a margin of 10 pixels.
public class ColorPicker extends View { Paint paint = new Paint(); // ... // constructors see previous tutorial protected void onDraw (Canvas canvas) { int w = getWidth(); int h = getHeight(); int radius = Math.min(w,h)/2 - 10; paint.setARGB(255, 255, 0, 0); canvas.drawCircle(w/2, h/2, radius, paint); } }
The combination of a Paint
object with the Canvas
is a powerful tool for creating complex drawings. More on these two classes will be presented in future tutorials.
In essence, this is enough you need to know about the onDraw
method if you want to implement a simple View
. However there are some details that you should be aware about if you intend to write a usable View
that behaves well.
Performance
One thing you might have noted is that the Paint
object is created as a class member of our ColorPicker
class rather than as a local variable inside the onDraw
method. This is done for performance reasons. The onDraw
method is called from the UI thread. This means that you should make sure that the code inside the method performs as fast as possible. Anything that takes too much time should either be done during initialisation or in a separate thread. Allocating objects with new
is can be quite a performance drag, especially if done repeatedly. For this reason you should avoid allocating new objects within the onDraw
method.
If your drawing becomes elaborate and requires some time consuming computation you should consider placing the code in a separate thread. This will take load off the UI thread and make your view more responsive. One class that is very useful in this context is the Picture
class. I will be talking about drawing into a Picture
in a future tutorial.
Getting the dimensions
One thing you will need to do when drawing your view onto the canvas is to find out how much space you have for your view. You might be tempted to look at the Canvas
reference and you will find that Canvas
provides two methods getWidth
and getHeight
. However, the Canvas
dimensions are not reliable in telling you the size of your view. Instead you should use the getWidth
and getHeight
of the View
itself.
If you search the internet for ways to find the width and height of a custom view you will come across a common solution that overrides the onSizeChanged
method. This solution might lead to slight performance increase but is not really necessary if you draw your view completely from the main thread. A future tutorial will go into the details of how to use the onSizeChanged
method. For simple views, you can call getWidth
and getHeight
from within the onDraw
method.
Do I need to call super.onDraw()
?
This is a question that arises when beginners start to implement their own onDraw
method. It might seem logical that View.onDraw
is responsible for drawing the background of the view. This is not the case. In fact, View.onDraw
has an empty implementation and calling it won’t make any difference to how your view appears. For sake of brevity I usually omit the call to super.onDraw
.
When is onDraw
called
The onDraw
method is called whenever android thinks that your view should be redrawn. This can be tha case when your view is animated, in which case onDraw
is called for every frame in the animation. It is also called when the layout changes and your view is re-positioned on the screen.
But what if some data inside your view has changed and you want to make sure that the view is redrawn. You can’t call onDraw
directly. Instead you should call invalidate
to tell the view that it needs to redraw itself.