一、需求是什么呢?
将界面上呈现的部分内容,生成图片分享到社交媒体
二、实现方案
1、将分享内容显示到一个底部弹窗上,这里使用的是Compose UI 做的(后面会说到这里的坑),弹窗使用的是ModalBottomSheet
2、使用view.drawToBitmap,将显示的View 绘制到bitmap上。这里会使用到一个方法Modifier.onGloballyPositioned,获取到当前需要截图内容的区域。
modifier.onGloballyPositioned {
onScreenshotBounds(it.boundsInWindow())
}
3、将bitmap存储到本地并生成Uri,这样第三方平台就可以分享这张图片
三、实现过程中遇到的各种问题
实现方案是完全没有问题的,但是实际做的时候遇到两个各种阻碍
1、分享到第三方平台,这里需要选择具体分享到哪个平台,这是一个已经实现的组件,使用的是BottomSheetDialogFragment,分享内容预览的内容也是一个弹窗,是用的Compose 的ModalBottomSheet实现的,也就是会出现这种情况:选择分享平台的弹窗是在分享内容预览的弹窗智之上,而且是用原生View和Compose 混合实现的。这里出的一个bug是,当屏幕旋转时,弹窗恢复后,分享预览的弹窗,也就是ModalBottomSheet会置于顶部,分享平台选择的弹窗会被置于底部
关于这个bug,最开始以为是BottomSheetDialogFragment在屏幕旋转后没有恢复状态,但是经过测试后,发现并不是这样的,而是因为被顶部的ModalBottomSheet遮挡了,看起来是消失了。
解决方案: 最终选择了将分享内容预览的弹窗也放到了BottomSheetDialogFragment中,这样就可以解决掉上面的那个bug
2、使用BottomSheetDialogFragment实现分享预览:
问题1:弹窗没有按内容全屏显示,而是折叠了
解决方法:重写onCreateDialog
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return BottomSheetDialog(requireContext(), R.style.BottomSheetDialogTheme).apply {
behavior.skipCollapsed = true
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
问题2:分享内容时可滑动,和弹窗的下拉隐藏,手势有冲突,导致下滑内容时,弹窗隐藏,内容无法下滑
解决方法:未可滑动的组件添加nestedScroll,解决滑动冲突。这里需要补充一下关于Compose View 对滑动冲突对的解决方案,看起来有点复杂,需要进一步去研究学习。
Column(
modifier = Modifier
.fillMaxWidth()
.nestedScroll(rememberNestedScrollInteropConnection())
.verticalScroll(rememberScrollState())
)
问题3:非常棘手的,可滑动的Compose View 无法截图到未显示的内容
解决方法:
查阅了相关资料后,包括询问AI 解决方案。最多的一种方式是使用离屏渲染,也就是将要截图的内容在看不到的地方重新全部渲染出来,然后进行截图到bitmap。还有一种方式就是使用原生的可滑动View,NestedScrollView。我之前做过长截图分享,当时所要分享的内容就是可滑动,就是用的NestedScrollView实现的,因此我采用了这种方式,将用Compose UI 实现的分享内容放到NestedScrollView中,因为我觉得离屏渲染性能不好,而且实现起来较为复杂。
这里还有一个小的改动:由于使用了BottomSheetDialogFragment包裹了分享内容,那么截图区域就要使用boundsInRoot。
modifier.onGloballyPositioned {
onScreenshotBounds(it.boundsInRoot())
}
这是因为boundsInWindow是获取的基于Window的坐标,boundsInRoot是基于root composable的坐标。我们截图用的View是用的LocalView.current,而这个返回的是当前 Composable 所在的 View,也就是承载这个 Compose UI 的 Android View,如:ComposeView
问题4:BottomSheetDialogFragment顶部无圆角
这个问题其实很简单,但是也还是花了很长时间去实现。刚开始按照搜索到方法在onCreate中setStyle 来设置顶部圆角,经过尝试无效;后面在看其他人的解决方案时,找到了根本原因和解决办法。首先需要将BottomSheetDialog的背景色设置为透明色,第二步就是要在根View上将background设置为圆角的shape。
修改BottomSheetDialog背景色为透明的方法:
<style name="BottomSheetDialogTheme" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
<item name="bottomSheetStyle">@style/ModalBottomSheetDialogStyle</item>
<item name="android:colorBackground">@color/bottom_sheet_background</item>
</style>
<style name="ModalBottomSheetDialogStyle" parent="Widget.MaterialComponents.BottomSheet.Modal">
<item name="backgroundTint">@color/transparent</item>
</style>
```
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return BottomSheetDialog(requireContext(), R.style.BottomSheetDialogTheme).apply {
behavior.skipCollapsed = true
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
这里要注意的是,在onCreateView中设置根View背景色为圆角后,嵌套的内容View 不要设置背景色,不然会把圆角覆盖。比如:我用的NestedScrollView作为的根View,ComposeView作为子View,那么我就不能再给子View设置其他颜色的背景色