Jetpack Compose Take Screenshot Of Composable Function?
Solution 1:
You can create a test, set the content to that composable and then call composeTestRule.captureToImage(). It returns an ImageBitmap.
Example of usage in a screenshot comparator: https://github.com/android/compose-samples/blob/e6994123804b976083fa937d3f5bf926da4facc5/Rally/app/src/androidTest/java/com/example/compose/rally/ScreenshotComparator.kt
Solution 2:
You can get position of a composable view inside the root compose view using onGloballyPositioned, and then draw the needed part of the root view into the Bitmap:
val view = LocalView.current
var capturingViewBounds by remember { mutableStateOf<Rect?>(null) }
Button(onClick = {
val bounds = capturingViewBounds ?: return@Buttonval image = Bitmap.createBitmap(
bounds.width.roundToInt(), bounds.height.roundToInt(),
Bitmap.Config.ARGB_8888
).applyCanvas {
translate(-bounds.left, -bounds.top)
view.draw(this)
}
}) {
Text("Capture")
}
ViewToCapture(
modifier = Modifier
.onGloballyPositioned {
capturingViewBounds = it.boundsInRoot()
}
)
Note that if you have some view on top of ViewToCapture, like placed with a Box, it'll still be on the image.
p.s. there's a bug which makes Modifier.graphicsLayer effects, offset { IntOffset(...) }(you still can use offset(dp) in this case), scrollable and lazy views position not being displayed correctly on the screenshot. If you've faced it, please star the issue to get more attention.
Solution 3:
As @Commonsware mentioned in the comment, and assuming this is not about screenshot testing:
According to official docs you can access the view version of your composable function using LocalView.current, and export that view to a bitmap file like this (the following code goes inside the composable function):
val view = LocalView.current
val context = LocalContext.current
val handler = Handler(Looper.getMainLooper())
handler.postDelayed(Runnable {
val bmp = Bitmap.createBitmap(view.width, view.height,
Bitmap.Config.ARGB_8888).applyCanvas {
view.draw(this)
}
bmp.let {
File(context.filesDir, "screenshot.png")
.writeBitmap(bmp, Bitmap.CompressFormat.PNG, 85)
}
}, 1000)
The writeBitmap method is a simple extension function for File class. Example:
privatefun File.writeBitmap(bitmap: Bitmap, format: Bitmap.CompressFormat, quality: Int) {
outputStream().use { out ->
bitmap.compress(format, quality, out)
out.flush()
}
}
Solution 4:
You can create a preview function with @Preview , run the function on phone or emulator and take the screenshot of the component.
Solution 5:
Using PixelCopy worked for me:
@RequiresApi(Build.VERSION_CODES.O)suspendfun Window.drawToBitmap(
config: Bitmap.Config = Bitmap.Config.ARGB_8888,
timeoutInMs: Long = 1000
): Bitmap {
var result = PixelCopy.ERROR_UNKNOWN
val latch = CountDownLatch(1)
val bitmap = Bitmap.createBitmap(decorView.width, decorView.height, config)
PixelCopy.request(this, bitmap, { copyResult ->
result = copyResult
latch.countDown()
}, Handler(Looper.getMainLooper()))
var timeout = false
withContext(Dispatchers.IO) {
runCatching {
timeout = !latch.await(timeoutInMs, TimeUnit.MILLISECONDS)
}
}
if (timeout) error("Failed waiting for PixelCopy")
if (result != PixelCopy.SUCCESS) error("Non success result: $result")
return bitmap
}
Example:
val scope = rememberCoroutineScope()
val context = LocalContext.current as Activity
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
Button(onClick = {
scope.launch {
//wrap in a try catch/blockif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
bitmap = context.window.drawToBitmap()
}
}
}) {
Text(text = "Take Screenshot")
}
Box(
modifier = Modifier
.background(Color.Red)
.padding(10.dp)
) {
bitmap?.let {
Image(
bitmap = it.asImageBitmap(),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
)
}
}
Post a Comment for "Jetpack Compose Take Screenshot Of Composable Function?"