我正在写一个截图库捕获屏幕
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 捕获返回屏幕上的所有内容?