ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Create Bitmaps From Jetpack Composables

Currently, I’m developing an Android app that is built using Jetpack Compose. The app records live Zoom meetings directly to an MP4 file in real-time. When the video is played, I include an image at the very beginning to indicate the title of the video, the participants in the Zoom session, date recorded, the app’s logo and a background image. This image is a bitmap and it was created using Jetpack composables. There are many examples where developers need to create a bitmap that consists of complex layouts that display text, images, borders and shapes. This article explains how to create these kinds of bitmaps using Jetpack composables. The source code for the sample app can be downloaded at:

There are several approaches to creating bitmaps including:

  • Use a canvas and draw everything using primitive API commands
  • Create a view using either an XML layout or programmatically constructing one and then convert the view into a bitmap
  • Display the layout on the screen and programmatically take a screenshot
  • Create a layout using Jetpack Compose and convert it into a bitmap.

There are plenty of posts on Stackoverflow that cover all but the last suggestion, since Compose is still new at this point. If your layout is pretty simple, using an xml layout or creating a view programmatically might do. Using views under the old view system doesn’t require that the view be shown or even attached to any window. But if your layout is fairly complex, using a view can be tedious. Using a canvas is perhaps the worst approach as this requires a lot more coding and tends to act more like painting. Taking a snapshot is okay if you are able to display the layout. There are many cases though, like mine, where you really don’t want to display the layout and the layout is fairly complex.

Having worked with Compose for the last 6 months, I can attest that it is a much better approach to laying out a screen than with the older view system. It then becomes natural to think that creating a bitmap from a composable would be the way to go. But after struggling with it for a few days, it became clear that generating the bitmap was not exactly a straightforward process.

Composables vs. Views

In an Android app built entirely from scratch using Jetpack Compose, there is only one view in the app, which is the activity’s root view. The typical way of creating a bitmap using the view system is to draw the view onto a canvas. Here is the code I use to handle this:

As already mentioned, you can provide a view that is created programmatically but not added to the screen. It turns out that you cannot do that using a composable. A composable will only be rendered when it is attached to a window. The composable need not be visible, but it must be attached to a window. The window must contain a ViewTreeLifecycleOwner which is used by Compose to determine when to recompose the composable.

For a while, I struggled to figure out how to attach a composable to a window that is not actually displayed. A window however can only be created by creating an activity. This seemed to rule out getting a composable to render without attaching it to an actual activity.

I then began to think how I could attach a composable to an activity but not display it, yet get it to perform its rendering. Fortunately, Jetpack Compose has support for integrating views into composables and composables into views. After much thought, I came up with a solution that requires using both of these approaches to achieve rendering a composable without displaying it. The custom view however must still be added to the activity.

The solution to generating the bitmap from a composable without displaying the composable can be summed up with these steps:

  1. Create a custom view and add a ComposeView to it. Set its visibility property to “gone” to keep the view hidden. Call the setContent function on the ComposeView within the custom view and provide the composable layout within setContent that will be rendered to the bitmap.
  2. Include the custom view in the activity using an AndroidView.

1. Create a custom view

To create a custom view, just create a class that extends some primitive layout. In this sample, I use LinearLayoutCompat. It doesn’t matter what you use since the view will not be displayed:

The CatInfo is the composable I am using that will be used to render the layout for the bitmap. It is important to note that when you create a bitmap, you must specify the width and height of the bitmap. You should also specify the height and width of your parent composable to prevent it from exceeding the bounds of the bitmap. In this sample, though I don’t bother since I know the size of the image and have made sure the size of the bitmap will be large enough to prevent any clipping.

The viewTreeObserver.addOnGlobalLayoutListener is necessary to determine when the custom view has completed its rendering. Only then can the view be converted to a bitmap.

When the bitmap has been created, the onBitmapCreated callback will be called to provide the client with the generated bitmap. This callback is just one way of providing your client with the bitmap. How you provide the bitmap to your client is up to you. I chose to do it this way because while the bitmap does not have to be displayed, for this sample, I wanted to display it to show the results.

IMPORTANT

If you need to pass parameters to your composable, it is important where and how you define those parameters. It isn’t clear why some cases fail to compile. This appears to be a bug in the Kotlin compiler and/or Jetpack Compose as these failures only apply when embedding a composable inside a custom view. Here are examples of what will compile and what will fail when using parameters:

2. Embed custom view into activity

Use AndroidView in your activity to embed your custom view:

While you’re here, please check out my other articles:

Navigation with Animated Transitions Using Jetpack Compose

A sample application that uses a custom navigation library allowing you to navigate screens with animated transitions and pass any data type:

In-App Testing For Android

A way to develop and test apps without the need for dependency injection:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Responses (5)

Write a response