在视图变得不可见后执行任务
Posted
技术标签:
【中文标题】在视图变得不可见后执行任务【英文标题】:Doing a task after a view became invisible 【发布时间】:2021-04-02 17:36:08 【问题描述】:我正在使用 android MediaProjection
Api 编写一个屏幕截图应用程序,其中一个覆盖按钮显示在所有内容的顶部,用户可以单击它以在任何地方捕获屏幕截图。由于 MediaProjection 记录屏幕内容,因此覆盖按钮本身位于捕获的屏幕截图中。为了在截屏时隐藏按钮,我尝试将视图visibility
设置为INVISIBLE
,截屏并将其还原为VISIBLE
,但由于更改可见性是Android 中的异步操作,有时覆盖按钮仍然存在出现在录制的镜头中。
我改成下面的 sn-p 并且它在我的实验中起作用:
floatingButton?.setOnClickListener view ->
view.visibility = View.INVISIBLE
view.postDelayed(100)
takeShot()
view.post view.visibility = View.VISIBLE
但这基本上是说我感觉很好,在 100 毫秒内,按钮将不可见。这不是一个好的解决方案,对于视频,100 毫秒内的内容可能与用户当时实际看到的内容大不相同。
Android 不提供 onVisibiltyChangedListener 之类的东西,那么在确保视图可见性发生变化后,我该如何执行任务?
编辑 1
这是takeShot()
方法:
private fun takeShot()
val image = imageReader.acquireLatestImage()
val bitmap = image?.run
val planes = image.planes
val buffer: ByteBuffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * width
val bitmap = Bitmap.createBitmap(
width + rowPadding / pixelStride,
height,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
image.close()
bitmap
bitmap?.let
serviceScope.launch
gallery.store(it)
代码在前台服务中,当用户接受媒体投影时,我创建ImageReader
和VirtualDisplay
:
imageReader = ImageReader.newInstance(size.width, size.height, PixelFormat.RGBA_8888, 2)
virtualDisplay = mediaProjection.createVirtualDisplay(
"screen-mirror",
size.width,
size.height,
Resources.getSystem().displayMetrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, // TODO: DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC ??
imageReader.surface, null, null
)
mediaProjection.registerCallback(object : MediaProjection.Callback()
override fun onStop()
virtualDisplay.release()
mediaProjection.unregisterCallback(this)
, null)
我试过没有暂停和协程的东西,结果是一样的,所以它们很可能与问题无关。
【问题讨论】:
试试ViewTreeObserver。 我尝试使用 AndroidXView.doOnPreDraw
但没有成功。你能解释一下吗?
***.com/a/32778292/4168607 。我猜这应该可行。
我使用了该方法并通过设置日志,我看到当覆盖按钮不可见时,实际上调用了捕获方法 (imageReader.acquireLatestImage()
),但它有时仍会出现在屏幕截图中! acquireLatestImage()
似乎没有必要给你“最新图像”。时间是不确定的。
你能把takeShot()
方法加上 question 吗?还要在其他设备上检查它。
【参考方案1】:
似乎我的问题与MediaProjection
有关,这将是一个单独的问题,但这个问题本身是相关的。
我最终使用了这个(几乎是为doOnPreDraw()
复制粘贴 core-ktx 代码)。请注意:
这不适用于 View.INVISIBLE,因为 INVISIBLE 不会触发“布局”
我不赞同这一点,因为它是全球性的,这意味着与“someView”视图层次结构相关的每个可见性更改都将调用onGlobalLayout
方法(因此您的操作/可运行)。
我保存接受的答案以获得更好的解决方案。
// usage
// someView.doOnVisibilityChange(become = View.GONE)
// someView is GONE, do stuff here
//
inline fun View.doOnVisibilityChange(become: Int, crossinline action: (view: View) -> Unit)
OneShotVisibilityChangeListener(this) action(this)
visibility = newVisibility
class OneShotVisibilityChangeListener(
private val view: View,
private val runnable: Runnable
) : ViewTreeObserver.OnGlobalLayoutListener, View.OnAttachStateChangeListener
private var viewTreeObserver: ViewTreeObserver
init
viewTreeObserver = view.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(this)
view.addOnAttachStateChangeListener(this)
override fun onGlobalLayout()
removeListener()
runnable.run()
private fun removeListener()
if (viewTreeObserver.isAlive)
viewTreeObserver.removeOnGlobalLayoutListener(this)
else
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
view.removeOnAttachStateChangeListener(this)
override fun onViewAttachedToWindow(v: View)
viewTreeObserver = v.viewTreeObserver
override fun onViewDetachedFromWindow(v: View)
removeListener()
【讨论】:
以上是关于在视图变得不可见后执行任务的主要内容,如果未能解决你的问题,请参考以下文章
当 viewController 的视图变得可见时,视图的框架设置不正确
当tableview单元格变得不可见时如何获取UITextField(UITableViewCell的子视图)文本值