Android 在视图不可见后执行任务
我正在使用AndroidAndroid 在视图不可见后执行任务,android,android-view,visibility,android-mediaprojection,Android,Android View,Visibility,Android Mediaprojection,我正在使用AndroidMediaProjectionApi编写一个屏幕截图应用程序,其中在所有内容的顶部都会显示一个覆盖按钮,用户可以单击它在任何地方捕获屏幕截图。因为MediaProjection记录屏幕内容,所以覆盖按钮本身就在捕获的屏幕截图中。为了在捕获屏幕截图时隐藏按钮,我尝试将viewvisibility设置为INVISIBLE,获取屏幕截图并将其还原为VISIBLE,但由于在Android中更改可见性是一项异步操作,有时重叠按钮仍存在于录制的快照中 我改为下面的代码片段,它在我的实
MediaProjection
Api编写一个屏幕截图应用程序,其中在所有内容的顶部都会显示一个覆盖按钮,用户可以单击它在任何地方捕获屏幕截图。因为MediaProjection记录屏幕内容,所以覆盖按钮本身就在捕获的屏幕截图中。为了在捕获屏幕截图时隐藏按钮,我尝试将viewvisibility
设置为INVISIBLE,获取屏幕截图并将其还原为VISIBLE,但由于在Android中更改可见性是一项异步操作,有时重叠按钮仍存在于录制的快照中
我改为下面的代码片段,它在我的实验中起了作用:
floatingButton?.setOnClickListener{view->
view.visibility=view.INVISIBLE
视图延迟(100){
takeShot()
view.post{view.visibility=view.VISIBLE}
}
}
但它基本上是说我感觉很好,在100毫秒内,按钮将不可见。这不是一个好的解决方案,对于视频来说,100毫秒的内容可能与用户当时实际看到的内容有很大不同
Android不提供OnVisibilityChangedListener之类的东西,那么,在确保视图可见性发生变化后,我如何执行任务呢
编辑1 以下是
takeShot()
方法:
private fun takeShot(){
val image=imageReader.acquireLatestImage()
val位图=图像?。运行{
val平面=image.planes
val缓冲区:ByteBuffer=平面[0]。缓冲区
val pixelStride=平面[0]。pixelStride
val rowStride=平面[0]。rowStride
val rowPadding=行步幅-像素步幅*宽度
val bitmap=bitmap.createBitmap(
宽度+行填充/像素步长,
高度,
Bitmap.Config.ARGB_8888
)
位图.copyPixelsFromBuffer(缓冲区)
image.close()
位图
}
位图?让我看看{
serviceScope.launch{
画廊商店(it)
}
}
}
代码在前台服务中,当用户接受媒体投影时,我创建ImageReader
和VirtualDisplay
:
imageReader=imageReader.newInstance(size.width,size.height,PixelFormat.RGBA_8888,2)
virtualDisplay=mediaProjection.createVirtualDisplay(
“屏幕镜像”,
大小、宽度,
身高,
Resources.getSystem().displayMetrics.densityDpi,
DisplayManager.VIRTUAL\u DISPLAY\u FLAG\u AUTO\u MIRROR,//TODO:DisplayManager.VIRTUAL\u DISPLAY\u FLAG\u OWN\u CONTENT\u ONLY | DisplayManager.VIRTUAL\u DISPLAY\u FLAG\u PUBLIC??
imageReader.surface,null,null
)
registerCallback(对象:mediaProjection.Callback(){
覆盖桌面上的乐趣(){
virtualDisplay.release()
mediaProjection.unregisterCallback(此)
}
},空)
我在没有暂停和协同程序的情况下尝试过,结果是一样的,所以它们很可能与问题无关。似乎我的问题与
MediaProjection有关,这是一个单独的问题,但这个问题本身是相关的
我最终使用了这个(几乎复制粘贴doOnPreDraw()
的核心ktx代码)。请注意:
这不适用于View.INVISIBLE,因为INVISIBLE不会触发“布局”
我不赞成这种做法,因为它是全局性的,这意味着与“someView”视图层次结构相关的每个可见性更改都将调用onGlobalLayout
方法(因此您的操作/可运行)
我把公认的答案留作更好的解决办法
//用法
//someView.doOnVisibilityChange(变成=View.GONE){
//someView不见了,在这里做事
// }
内联乐趣视图。doOnVisibilityChange(变成:Int,交叉内联动作:(视图:视图)->Unit){
OneShotVisibilityChangeListener(此){action(此)}
可见性=新建可见性
}
类OneShotVisibilityChangeListener(
私有val视图:视图,
private val runnable:runnable
):ViewTreeObserver.OnGlobalLayoutListener,View.OnAttachStateChangeListener{
私有变量viewTreeObserver:viewTreeObserver
初始化{
viewTreeObserver=view.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(此)
view.addOnAttachStateChangeListener(此)
}
覆盖Loballayout(){
removeListener()
runnable.run()
}
私人娱乐节目重组人(){
if(viewTreeObserver.isAlive){
viewTreeObserver.removeOnGlobalLayoutListener(此)
}否则{
view.viewTreeObserver.removeOnGlobalLayoutListener(此)
}
view.removeOnAttachStateChangeListener(此)
}
覆盖视图附加的视图窗口(v:视图){
viewTreeObserver=v.viewTreeObserver
}
覆盖视图上的乐趣DetachedFromWindow(v:视图){
removeListener()
}
}
似乎我的问题与MediaProjection
有关,这将是一个单独的问题,但这个问题本身是相关的
我最终使用了这个(几乎复制粘贴doOnPreDraw()
的核心ktx代码)。请注意:
这不适用于View.INVISIBLE,因为INVISIBLE不会触发“布局”
我不赞成这种做法,因为它是全局性的,这意味着与“someView”视图层次结构相关的每个可见性更改都将调用onGlobalLayout
方法(因此您的操作/可运行)
我把公认的答案留作更好的解决办法
//用法
//someView.doOnVisibilityChange(变成=View.GONE){
//someView不见了,在这里做事
// }
内联乐趣视图。doOnVisibilityChange(变成:Int,交叉内联动作:(视图:视图)->Unit){
OneShotVisibilityChangeListener(此){action(此)}
可见性=新建可见性