Search code examples
androidkotlinandroid-jetpack-composescreenshot

How to take a full screenshot capture in Android compose?


I am developing an android app using Jetpack Compose.

I have a simple page. It has a one scrollable Column like:

Column(
    modifier = Modifier
        .verticalScroll(rememberScrollState())
        .fillMaxSize()
        .padding(it)
        .padding(bottom = 32.dp)
) {
    if (!dataMap.isNullOrEmpty()) {
        dataMap.entries.forEachIndexed { index, (key, value) ->
            val header = key
            val list = value

            Text(text = title)

            list.forEach { item ->
                ItemUi(item)
            }
        }
    }
}

Actually the dataMap is not simple, it has nested list. Anyway the size of the dataMap is limited, it's not that big. So I just use Column not LazyColumn.

And I have a FAB button to share the screen. I can write the code take current screen and it works fine.

But the problem is that it only takes a screenshot current visible area not the fully scrolled area.

My takeScreenshot function is here:

private fun takeScreenShot(view: View, dir: File): File? {
    try {
        view.isDrawingCacheEnabled = true
        val bitmap: Bitmap = Bitmap.createBitmap(view.drawingCache)
        view.isDrawingCacheEnabled = false
        val dateText = DateFormat.format("yyyy-MM-dd_hh:mm:ss", Date())
        val imageFile = File("$dir/result-$dateText.jpeg")
        FileOutputStream(imageFile).use { fos ->
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos)
            fos.flush()
        }
        return imageFile
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
    return null
}

and this is called with:

@Composable
fun MyScreen() {
    val activity = LocalContext.current as Activity
    val requestPermissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
        if (isGranted) {
            takeScreenShot(activity.window.decorView.rootView, activity.filesDir)
        }
    }
}

Solution

  • Check this out jetpack-composable-to-bitmap-image

    You can inflate an unbounded AndroidView and use drawToBitmap to convert it into bitmap

    @Composable
    fun screenshotableComposable(content : @Composable () -> Unit): () -> Bitmap {
        val context = LocalContext.current
        val composeView = remember { ComposeView(context = context) }
        fun captureBitmap(): Bitmap = composeView.drawToBitmap()
        AndroidView(
            factory = {
                composeView.apply {
                    setContent {
                        content()
                        /*...content...*/
                    }
                }
            },
            modifier = Modifier.wrapContentSize(unbounded = true)   //  Make sure to set unbounded true to draw beyond screen area
        )
    
        return ::captureBitmap
    }
    

    and from where onClick method is present, declare screenshotableComposable which you call from onClick with screenshotableComposable.invoke() and it will return bitmap

    val screenshotableComposable = screenshotableComposable(
        content = {
            /*...composable content...*/
        }
    )
    
    
    onClick = {
        val bitmap = screenshotableComposable.invoke()
    }
    

    There is an issue with that you need to draw content twice, one which actually renders on screen and other which renders within screenshotableComposable