Take a screenshot of a view

These snippets allows grabbing a screenshot of a View or Activity as a bitmap. There are special version to make this work on SurfaceViews and TextureViews.

To grab a complete Activity, simply apply the snippets below on its root view.

Simple, old method

view.isDrawingCacheEnabled = true
val sourceBitmap = view.drawingCache
val bitmap = sourceBitmap.copy(sourceBitmap.config, false)

These methods are deprecated, but still work. Note that it might not even be necessary to clone the Bitmap, e.g. if you write it directly to a file before it is recycled or modified.

New method (Android O and above)

This is supposed to work a lot better with SurfaceViews, but I have had no success grabbing a still frame from an ExoPlayer that uses a SurfaceView. However, if you set up ExoPlayer to use a TextureView instead, this method works, you should use the PixelCopy API:

val locationOfViewInWindow = IntArray(2)
view.getLocationInWindow(locationOfViewInWindow)
try {
	val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
    PixelCopy.request(
        window,
        Rect(
            locationOfViewInWindow[0],
            locationOfViewInWindow[1],
            locationOfViewInWindow[0] + view.width,
            locationOfViewInWindow[1] + view.height
        ), bitmap, { copyResult ->
            if (copyResult == PixelCopy.SUCCESS) {
                callback(bitmap)
            }
        },
        Handler()
    )
} catch (e: IllegalArgumentException) {
    // PixelCopy may throw IllegalArgumentException, make sure to handle it
    e.printStackTrace()
}

TextureView special

The above method works with a TextureView as well, but if it is only a TextureView you’re interested in, there is a shortcut:

val bitmap = textureView.bitmap

Wrapping it all together

In one monster snippet. window is a field availablt in the context of an Activity, so use requireActivity().window when this is used in a Fragment.

fun createScreenshot(view: View, callback: (Bitmap) -> Unit) {
    if (view is TextureView) {
        callback(view.bitmap.copy(view.bitmap.config, false))
        return
    }

    window?.let { window ->
        val locationOfViewInWindow = IntArray(2)
        view.getLocationInWindow(locationOfViewInWindow)
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
                PixelCopy.request(
                    window,
                    Rect(
                        locationOfViewInWindow[0],
                        locationOfViewInWindow[1],
                        locationOfViewInWindow[0] + view.width,
                        locationOfViewInWindow[1] + view.height
                    ), bitmap, { copyResult ->
                        if (copyResult == PixelCopy.SUCCESS) {
                            callback(bitmap)
                        }
                    },
                    Handler()
                )
            } else {
                view.isDrawingCacheEnabled = true
                callback(view.drawingCache.copy(view.drawingCache.config, false))
            }
        } catch (e: IllegalArgumentException) {
            // PixelCopy may throw IllegalArgumentException, make sure to handle it
            e.printStackTrace()
        }
    }
}