How to convert all content of a composable to Bitmap in Android Jetpack compose?

1.2k views Asked by At

I am trying to convert all the content of a LazyColumn to bitmap, and then export it to a pdf file.

I did find this library to take screenshots of what is showing but not for all the content. Content that was not showing is not included. So I also try to draw canvas instead, but we can not apply all styles of the content (like a markdown, custom background,...) So I am thinking that we can have a custom native Android View, it may be possible to do it, but I can't find a way to pass a composable into it.

So anyone done that before or has a solution for this?

Edit 1: I did try to use draw content in compose graphics alpha-02, but the content in the LazyColumn was unable to scroll:

    onDrawWithContent {
        val pictureCanvas = Canvas(picture.beginRecording(width, height))

        draw(this, this.layoutDirection, pictureCanvas, this.size) {
////
        }
        picture.endRecording()

        drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
      }
2

There are 2 answers

2
F.Mysir On

I had the same requirement like you and wanted to be able to achieve capturing all screen. So I was able to achieve this inside a scrollable view with Capturable

The component which needs to be captured should be placed inside Capturable composable as follows:

@Composable
fun TicketScreen() {
    // I was able to capture the full width of the composable which was not visible on the screen with a scroll modifier on top of the Capturable for example:
    Column(modifier = Modifier.horizontalScroll(rememberScrollState())) {
        Capturable(
            modifier = Modifier,
            controller = controller,
            onCaptured = { bitmap, error ->
                if (state.printVides) {
                    onEvent(ScanPrintEvent.SetImageBitmapVides(bitmap?.asAndroidBitmap()))
                } else {
                    onEvent(ScanPrintEvent.SetImageBitmapNormal(bitmap?.asAndroidBitmap()))
                }
            }
        ) {
           YourComposable()
        }
    }
}
1
Chirag Thummar On

Steps to Export Bitmaps

Use drawIntoCanvas Method and delegate to

  1. android.graphics.Picture class.
  2. Then save Picture to Local Storage.

First Thing First Add The Latest Release Of Compose Dependency1.6.0-alpha03 or new

implementation "androidx.compose.ui:ui:1.6.0-alpha03"

Modifier.drawWithCache keeps the objects that are created inside of it cached. The objects are cached as long as the size of the drawing area is the same, or any state objects that are read have not changed. This modifier is useful for improving the performance of drawing calls as it avoids the need to reallocate objects (such as: Brush, Shader, Path etc.) that are created on the draw.

val picture = remember { Picture() }
        Column(
            modifier = Modifier
                .padding(padding)
                .fillMaxSize()
                .drawWithCache {
                    // Example that shows how to redirect rendering to an Android Picture and then
                    // draw the picture into the original destination
                    val width = this.size.width.toInt()
                    val height = this.size.height.toInt()
                    onDrawWithContent {
                        val pictureCanvas =
                            androidx.compose.ui.graphics.Canvas(
                                picture.beginRecording(
                                    width,
                                    height
                                )
                            )
                        draw(this, this.layoutDirection, pictureCanvas, this.size) {
                            [email protected]()
                        }
                        picture.endRecording()

                        drawIntoCanvas { canvas -> canvas.nativeCanvas.drawPicture(picture) }
                    }
                }
        ) {
            ScreenContentToCapture()
        }

After drawing the cache into Picture. You can use Picture an object to get the bitmap and export them to Local storage.

Saving the Image

We will use Picture Object to capture the image and draw it into canvas and save that Bitmap to Local storage using the FileWriter.

private fun createBitmapFromPicture(picture: Picture): Bitmap {
    val bitmap = Bitmap.createBitmap(
        picture.width,
        picture.height,
        Bitmap.Config.ARGB_8888
    )

    val canvas = android.graphics.Canvas(bitmap)
    canvas.drawColor(android.graphics.Color.WHITE)
    canvas.drawPicture(picture)
    return bitmap
}

private suspend fun Bitmap.saveToDisk(context: Context): Uri {
    val file = File(
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
        "screenshot-${System.currentTimeMillis()}.png"
    )

    file.writeBitmap(this, Bitmap.CompressFormat.PNG, 100)

    return scanFilePath(context, file.path) ?: throw Exception("File could not be saved")
}

private fun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) {
    outputStream().use { out ->
        bitmap.compress(format, quality, out)
        out.flush()
    }
}

Here you can refer complete code

https://gist.github.com/chiragthummar/35aa1d15882c4d5a105a3f89be821214