Android 在视图不可见后执行任务

Android 在视图不可见后执行任务,android,android-view,visibility,android-mediaprojection,Android,Android View,Visibility,Android Mediaprojection,我正在使用AndroidMediaProjectionApi编写一个屏幕截图应用程序,其中在所有内容的顶部都会显示一个覆盖按钮,用户可以单击它在任何地方捕获屏幕截图。因为MediaProjection记录屏幕内容,所以覆盖按钮本身就在捕获的屏幕截图中。为了在捕获屏幕截图时隐藏按钮,我尝试将viewvisibility设置为INVISIBLE,获取屏幕截图并将其还原为VISIBLE,但由于在Android中更改可见性是一项异步操作,有时重叠按钮仍存在于录制的快照中 我改为下面的代码片段,它在我的实

我正在使用Android
MediaProjection
Api编写一个屏幕截图应用程序,其中在所有内容的顶部都会显示一个覆盖按钮,用户可以单击它在任何地方捕获屏幕截图。因为MediaProjection记录屏幕内容,所以覆盖按钮本身就在捕获的屏幕截图中。为了在捕获屏幕截图时隐藏按钮,我尝试将view
visibility
设置为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(此)}
    可见性=新建可见性