Let's start by drawing bitmaps or images. Instead of having a white background, we'll use an image as background for our custom view. Using the source code from our previous example, we could do some very simple modifications to draw an image:
First, let's define a Bitmap object that will hold a reference to the image:
private Bitmap backgroundBitmap;
To start, let's initialize it with the application icon we already have on our application:
public CircularActivityIndicator(Context context, AttributeSet attributeSet) { super(context, attributeSet); backgroundBitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher);
BitmapFactory provides us several ways to load and decode images.
Once we have the image loaded, we can draw it on our onDraw() method by calling the drawBitmap(Bitmap bitmap, float left, float top, Paint paint) method:
@Override protected void onDraw(Canvas canvas) { if (backgroundBitmap != null) { canvas.drawBitmap(backgroundBitmap, 0, 0, null); }
As we don't need anything special from our Paint object, we've set it to null; we'll use it later in this book, but for the moment, just ignore it.
If backgroundBitmap is null, it means that it couldn't load the image; so, for safety, we should always check. This code will just draw the icon on the top-left corner of our custom view, although we could change its position by setting either different coordinates-here we used 0,0-or applying a transformation to our canvas like we did before. For example, we can rotate the image based on the angle selected by the user:
@Override protected void onDraw(Canvas canvas) { // apply a rotation of the bitmap based on the selectedAngle if (backgroundBitmap != null) { canvas.save(); canvas.rotate(selectedAngle, backgroundBitmap.getWidth() / 2,
backgroundBitmap.getHeight() / 2); canvas.drawBitmap(backgroundBitmap, 0, 0, null); canvas.restore(); }
Note that we've added the center of the image as the pivot point, or otherwise will rotate by its top-left corner.
There are other ways to draw images; Android has another method for drawing an image from a source Rect to a destination Rect. The Rect object allows us to store four coordinates and use it as a rectangle.
The method drawBitmap(Bitmap bitmap, Rect source, Rect dest, Paint paint) is very useful for drawing a portion of an image into any other size we want. This method will take care of scaling the selected portion of the image to fill the destination rectangle. For example, we could use the following code if we wanted to draw the right half of the image scaled to the whole custom view size.
First, let's define the background Bitmap and two Rect; one to hold the source dimensions and other for the destination:
private Bitmap backgroundBitmap; private Rect bitmapSource; private Rect bitmapDest;
Then, let's instantiate them on the class constructor. It's not a good practice to do it on the onDraw() method, as we should avoid allocating memory to methods that are called on every frame or every time we draw our custom view. Doing so will trigger additional garbage collector cycles and affect performance.
public CircularActivityIndicator(Context context, AttributeSet attributeSet) { super(context, attributeSet); backgroundBitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher); bitmapSource = new Rect(); bitmapSource.top = 0; bitmapSource.left = 0; if(backgroundBitmap != null) { bitmapSource.left = backgroundBitmap.getWidth() / 2; bitmapSource.right = backgroundBitmap.getWidth(); bitmapSource.botto m = backgroundBitmap.getHeight(); } bitmapDest = new Rect();
By default, Rect initializes the four coordinates to 0 but here, for clarity, we set the top and the left coordinates to 0. If the image was loaded successfully, we set the right and bottom to the width and height of the image, respectively. As we want to draw only the right half of the image only, we update the left border to half the width of the image.
On the onDraw() method, we set the right and bottom coordinates of the destination Rect to the width and height of the custom view and we draw the image:
@Override protected void onDraw(Canvas canvas) { if (backgroundBitmap != null) { bitmapDest.right = getWidth(); bitmapDest.bottom = getHeight(); canvas.drawBitmap(backgroundBitmap, bitmapSource, bitmapDest,
null); }
Let's check the result:
![](assets/0da5787f-9638-4cf0-846e-56f9a313f84e.png)
We can see it doesn't abide by the aspect ratio of the image, but let's solve it by computing the ratio of the smaller dimension, either horizontal or vertical, and scale it by this ration. Then, apply it to the other dimension. We will see the following code after calculating the image ratio:
@Override protected void onDraw(Canvas canvas) { if (backgroundBitmap != null) { if ((bitmapSource.width() > bitmapSource.height() && getHeight() >
getWidth()) || (bitmapSource.width() <= bitmapSource.height() && getWidth() >=
getHeight())) { double ratio = ((double) getHeight()) / ((double)
bitmapSource.height()); int scaledWidth = (int) (bitmapSource.width() * ratio); bitmapDest.top = 0; bitmapDest.bottom = getHeight(); bitmapDest.left = (getWidth() - scaledWidth) / 2; bitmapDest.right = bitmapDest.left + scaledWidth; } else { double ratio = ((double) getWidth()) / ((double)
bitmapSource.width()); int scaledHeight = (int) (bitmapSource.height() * ratio); bitmapDest.left = 0; bitmapDest.right = getWidth(); bitmapDest.top = 0; bitmapDest.bottom = scaledHeight; } canvas.drawBitmap(backgroundBitmap, bitmapSource, bitmapDest,
null); }
We can also draw a Bitmap using a transformation Matrix. To do so, we can create a new instance of Matrix and apply a transformation:
private Matrix matrix;
Create an instance on the constructor. Do not create an instance on the onDraw() instance, as it will pollute the memory and trigger unnecessary garbage collection, as mentioned earlier:
matrix = new Matrix(); matrix.postScale(0.2f, 0.2f); matrix.postTranslate(0, 200);
Please be careful with the matrix operation order; there are also post-operations and pre-operations. Check the matrix class documentation for more information.
On the onDraw() method, just draw Bitmap using the drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) method and using the matrix we've initialized on our class constructor. In this example, we also used a null Paint object to simplify, as we don't need anything specific from the Paint object here.
canvas.drawBitmap(backgroundBitmap, matrix, null);
Although these are the most common methods to draw a Bitmap onto a Canvas, there are a few more methods.
Furthermore, check the Example12-Drawing folder on the GitHub repository to see the full source code of this example.