Android Canvas APIs with Kotlin and KTX
Learn how to use the Android KTX extension functions to clean up Canvas drawing code
Have you ever wanted to write a Custom View on Android but you were too afraid to deal with X, Y translations on a Canvas object?
Well, working with them got a whole lot easier when using Kotlin and the Android KTX Extension functions provided by the Android Team at Google.
Drawing on Canvas without KTX 🙀🙅🏽♀️
If you want to translate (move) an object you are drawing on a
Canvas, how would you go about doing that? You would likely need to do something like the following:
Canvas#restore() can be used to
save() the current matrix (transformations like translate, rotate etc) and
restore() the state of the
Canvas back to its original transformations.
drawCircle() method that we are calling will be drawn at a translated point on the
restore is called, the
Canvas is no longer translated and any further operations on that Canvas after that point will not be translated.
If we wanted to then do something more complex, for instance, draw a path that is scaled up after we have translated, the code would look as follows:
To do multiple translations on a Canvas, we would then want to use
restoreToCount with the specific checkpoint. This will notify the canvas of the certain checkpoint of the transformations that should be restored.
We can see that this can easily get out of control and it becomes really difficult to follow what transformations will be applied to a certain
Improving Canvas API calls with Android KTX 😻
To use these extension functions, you need to make sure you are importing the core-ktx dependency in your app level
build.gradle file (look here for the latest version):
If we are using KTX, we can simplify the previous draw examples to be the following:
This wraps up this logic in a block, that makes it easier to understand and our code is cleanly separated. We also don’t need to specify the canvas on which to draw on at this point, since the block now has the Canvas in
this scope. If we wished to draw certain parts of the Canvas at different points, say we wanted to translate and scale, we can nest the
withRotate function inside the first
Now our function calls are clearly separated with parentheses and we can easily see which canvas transformations will be applied to the
drawRect function by using these extension functions.
Diving into the Android KTX implementation 📝
This is one of the extension functions for Canvas defined in the KTX library:
Taking a deeper look into how this extension function works, we can see that the functions wrap up the logic of saving and restoring the canvas. The last parameter is a function and that function (
block()) is a function literal with receiver. This sounds complicated but this just means that the
Canvas instance then becomes the
this scope for that function definition.
This allows us in the
block() function, to call the Canvas methods without having to specify the canvas object directly. For example, we don’t have to call
canvas.drawCircle() anymore, we can now just call
drawCircle() and the correct
Canvas object will be used for that method call.
block param is the last parameter of the function (and it is a function itself) so we are able to extract the
block function outside of the parentheses. For example, both of the following usages are acceptable:
Outside of parenthesis:
It is worth noting that in Android Studio, the first example here will produce a lint warning to tell you to rather use the second option.
What a nifty mechanism for cleaning up our Canvas API interactions!
We can see how the AndroidX Canvas Extension functions can help improve the readability of our canvas transformation code. There are a few other extension functions for Canvas, some of the others include:
Have you found any other useful extension functions in the KTX library? Let me know on Twitter @riggaroo