Simply put, we shouldn’t create any new instances of objects inside the onDraw() method, because our UI won’t be smooth if we allocate a lot of new objects that eventually will be garbage collected. We need to move creation of the Paint object out of this method.

The last thing is to add our view to the layout file.

Now let’s run our project and see the results!

Yeah! We’ve drawn a line 😊 But it’s not centered as we wanted to. Actually our view takes the whole space. Probably because we haven’t specified how much space the view should take. It’s time to override onMeasure() method.

First, we need to decode the mode and size of the view. Why? Let’s look at what the documentation of onMeasure() says about these parameters.

Parameters:

widthMeasureSpec — horizontal space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.

heightMeasureSpec — vertical space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.

Parent view passes these parameters in the encoded form, so we need to decode them first — it’s probably connected with some kind of optimisations.

Then we need to check the mode. There are three different modes:

EXACTLY — when we set the exact dp value (like 300dp) on our view or it’s match_parent ,

, AT_MOST — when it’s wrap_content ,

, UNSPECIFIED — when it doesn’t matter, and a View can be as big as it want to be, because i.e. parent is a ScrollView (then the height in portrait mode is irrelevant).

You can play with it and check if it’s true, for example by adding some logs inside the onMeasure() method and changing layout params.

So as we can see, we take the given dp value if it’s provided, otherwise we take the default ones which are defined in the res/values/dimens.xml resource file.

After all, remember to call setMeasuredDimension() method ⚠️ to apply the view dimensions.

Now let’s change the parameters a bit to have a horizontal line and run the project!

Cool! Again, now you should play a bit with different layout_width and layout_height parameters to see what’s changing. Turn on Show Layout Bounds option in Developer Options to see the exacts space that our view is taking. If you aren’t familiar with this option, you can find how to enable it for example in my article about margin and padding.

Ok, but we want to have a thicker line, kind of a bar, so we’ll be able to manipulate its height as well as the width. Let’s change the line to the rectangle

How to draw a rectangle?

Fortunately it’s pretty easy with our example. All we need to do is to change drawLine() method to drawRect() and apply the height value instead of 0.0F to the bottom parameter. We should also refactor the name of the linePaint to barPaint .

Let’s also change the volume_bar_default_height to 12dp and run the project.

Now we want to change a color, let’s say, to grey. But there is no color parameter on the drawRectangle() method. And it shouldn’t be, because through the Canvas we’re saying what to draw, however we use the Paint class to say how we’re going to do this. So we’ll use our barPaint to change the color.

The next step is to draw a circle that will show the current volume level.

How to draw a circle?