如何选择性地或可滚动地截屏可组合项?

2023-12-09

我正在写一个截图库捕获屏幕

val view: View = LocalView.current

我获取它的边界并平移 Composable 的位置以捕获 Composable 的可见部分。

我这样做

@Composable
fun ScreenshotBox(
    modifier: Modifier = Modifier,
    screenshotState: ScreenshotState,
    parentView: ComposeView? = null,
    content: @Composable () -> Unit,
) {
    val view: View = parentView ?: LocalView.current
    
    var composableBounds by remember {
        mutableStateOf<Rect?>(null)
    }

    DisposableEffect(Unit) {

        screenshotState.callback = {
            composableBounds?.let { bounds ->
                if (bounds.width == 0f || bounds.height == 0f) return@let

                view.screenshot(bounds) { imageResult: ImageResult ->
                    screenshotState.imageState.value = imageResult

                    if (imageResult is ImageResult.Success) {
                        screenshotState.bitmapState.value = imageResult.data
                    }
                }
            }
        }

        onDispose {
            val bmp = screenshotState.bitmapState.value
            bmp?.apply {
                if (!isRecycled) {
                    recycle()
                }
            }
            screenshotState.bitmapState.value = null
            screenshotState.callback = null
        }
    }

    Box(modifier = modifier
        .onGloballyPositioned {
            composableBounds = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                it.boundsInWindow()
            } else {
                it.boundsInRoot()
            }
        }
    ) {
        content()
    }
}

用于获取可组合位置和捕获的扩展

fun View.screenshot(
    bounds: Rect,
    bitmapCallback: (ImageResult) -> Unit
) {

    try {

        val bitmap = Bitmap.createBitmap(
            bounds.width.toInt(),
            bounds.height.toInt(),
            Bitmap.Config.ARGB_8888,
        )

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            // Above Android O not using PixelCopy throws exception
            // https://stackoverflow.com/questions/58314397/java-lang-illegalstateexception-software-rendering-doesnt-support-hardware-bit
            PixelCopy.request(
                (this.context as Activity).window,
                bounds.toAndroidRect(),
                bitmap,
                {
                    when (it) {
                        PixelCopy.SUCCESS -> {
                            bitmapCallback.invoke(ImageResult.Success(bitmap))
                        }
                        PixelCopy.ERROR_DESTINATION_INVALID -> {
                            bitmapCallback.invoke(
                                ImageResult.Error(
                                    Exception(
                                        "The destination isn't a valid copy target. " +
                                                "If the destination is a bitmap this can occur " +
                                                "if the bitmap is too large for the hardware to " +
                                                "copy to. " +
                                                "It can also occur if the destination " +
                                                "has been destroyed"
                                    )
                                )
                            )
                        }
                        PixelCopy.ERROR_SOURCE_INVALID -> {
                            bitmapCallback.invoke(
                                ImageResult.Error(
                                    Exception(
                                        "It is not possible to copy from the source. " +
                                                "This can happen if the source is " +
                                                "hardware-protected or destroyed."
                                    )
                                )
                            )
                        }
                        PixelCopy.ERROR_TIMEOUT -> {
                            bitmapCallback.invoke(
                                ImageResult.Error(
                                    Exception(
                                        "A timeout occurred while trying to acquire a buffer " +
                                                "from the source to copy from."
                                    )
                                )
                            )
                        }
                        PixelCopy.ERROR_SOURCE_NO_DATA -> {
                            bitmapCallback.invoke(
                                ImageResult.Error(
                                    Exception(
                                        "The source has nothing to copy from. " +
                                                "When the source is a Surface this means that " +
                                                "no buffers have been queued yet. " +
                                                "Wait for the source to produce " +
                                                "a frame and try again."
                                    )
                                )
                            )
                        }
                        else -> {
                            bitmapCallback.invoke(
                                ImageResult.Error(
                                    Exception(
                                        "The pixel copy request failed with an unknown error."
                                    )
                                )
                            )
                        }
                    }

                },
                Handler(Looper.getMainLooper())
            )
        } else {
            val canvas = Canvas(bitmap)
                .apply {
                    translate(-bounds.left, -bounds.top)
                }
            this.draw(canvas)
            canvas.setBitmap(null)
            bitmapCallback.invoke(ImageResult.Success(bitmap))
        }
    } catch (e: Exception) {
        bitmapCallback.invoke(ImageResult.Error(e))
    }
}

问题是它将屏幕上的所有内容截图为捕获按钮,我不想使用捕获浮动操作按钮

Scaffold(
    floatingActionButton = {
        ExtendedFloatingActionButton(onClick = {
            screenshotState.capture()
        }) {
            Text(text = "Capture")
        }
    },
    floatingActionButtonPosition = FabPosition.End,
    content = { paddingValues: PaddingValues ->
        ScreenshotSample(screenshotState, paddingValues)

        if (showDialog) {
            ImageAlertDialog(imageResult = imageResult) {
                showDialog = false
            }
        }
    }
)

https://raw.githubusercontent.com/SmartToolFactory/Compose-Screenshot/master/art/screenshot.gif

我尝试使用

@Composable
fun ScreenshotBoxWithView(
    modifier: Modifier = Modifier,
    screenshotState: ScreenshotState,
    content: @Composable () -> Unit
) {

    AndroidView(
        factory = {
            ComposeView(it)
        },
        update = {
            it.setContent {
                ScreenshotBox(
                    modifier = modifier,
                    parentView = it,
                    screenshotState = screenshotState
                ) {
                    content()
                }
            }
        }
    )
}

它仍然返回与 Bitmap 中的 FloatingActionButton 相同的结果。有没有一种方法可以对特定的可组合项进行屏幕截图,这也使得可以使用Modifier.scroll(),而不是从 View 捕获返回屏幕上的所有内容?


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何选择性地或可滚动地截屏可组合项? 的相关文章

随机推荐