kotlin 如何在Android compose中拍摄完整的屏幕截图?

rggaifut  于 2023-10-23  发布在  Kotlin
关注(0)|答案(2)|浏览(214)

我正在使用Jetpack Compose开发Android应用程序。
我有一个简单的页面。它有一个可滚动的列,如:

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)
            }
        }
    }
}

实际上dataMap并不简单,它有嵌套列表。无论如何,dataMap的大小是有限的,它不是那么大。所以我只使用列而不是LazyColumn。
我有一个FAB按钮来共享屏幕。我可以写的代码采取当前屏幕和它的工作正常。
但问题是,它只需要截图当前可见区域,而不是完全滚动的区域。
我的takeScreenshot函数如下:

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
}

这被称为:

@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)
        }
    }
}
wvt8vs2t

wvt8vs2t1#

看这个jetpack-composable-to-bitmap-image
你可以膨胀一个无限的AndroidView并使用drawToBitmap将其转换为位图

@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
}

并从onClick方法存在的地方,声明screenshotableComposable,您从onClick调用screenshotableComposable.invoke(),它将返回位图

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

onClick = {
    val bitmap = screenshotableComposable.invoke()
}

有一个问题是,您需要绘制两次内容,一次实际呈现在屏幕上,另一次在screenshotableComposable中呈现

dgtucam1

dgtucam12#

从Compose 1.6.0-alpha 01+开始,在Compose中有一种新的原生方式可以做到这一点:https://developer.android.com/jetpack/compose/graphics/draw/modifiers#composable-to-bitmap

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
                        )
                    )
                // requires at least 1.6.0-alpha01+
                draw(this, this.layoutDirection, pictureCanvas, this.size) {
                    [email protected]()
                }
                picture.endRecording()

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

) {
    ScreenContentToCapture()
}

要将图片转换为位图:

val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    Bitmap.createBitmap(picture)
} else {
    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)
    bitmap
}

相关问题