如何解决抽屉动画期间的显示撕裂问题

Posted

技术标签:

【中文标题】如何解决抽屉动画期间的显示撕裂问题【英文标题】:How to tackle display tearing during drawer animations 【发布时间】:2016-03-25 09:27:52 【问题描述】:

我正在使用允许底部和顶部边缘抽屉的第 3 方抽屉 (https://gist.github.com/patrickfav/6284130MultipleOrientationSlidingDrawer)。

当我为抽屉的打开设置动画时,我得到了显示撕裂,但仅在特定情况下。单击抽屉标题时,动画流畅美观。

我在主窗口中有一个 RecyclerView。如果用户单击列表视图中的条目,抽屉幻灯片将打开,显示所选条目的详细信息(单击轨道,从底部向上滑动媒体控件)。抽屉动画的一部分是在抽屉打开时将主窗口内容淡化为黑色。所以有大量的透支。奇怪的是,当通过单击 RecyclerView 触发动画时,我在动画过程中会出现显示屏撕裂(严重闪烁,发生在从屏幕顶部向下大约 2/3 处)。

我正在寻找有关如何进行的建议:如何调试,或者关于什么可能导致动画期间显示闪烁的理论。

这是我迄今为止尝试过的。

最初,我虽然这是由 RecyclerView 条目中的 RippleDrawable 动画引起的(在新闻和用于指示选择的激活更改上。为了防止这种情况,我尝试通过调用 recyclerView 来取消波纹动画。 jumpDrawablesToCurrentState() 在各个点(动画开始,以及在每次动画更新期间)。据我所知,RippleDrawable 动画已被有效取消。但显示撕裂仍然发生。事情有点复杂。状态变化动画直到发生某种调度后才会被触发;但是对 startAnimation 的调用似乎也不会在可绘制状态更改完成后才会发生。所以我认为我做对了。以防万一,我'已经尝试在每个动画传递期间调用 recyclerView.jumpDrawablesToCurrentState ,这肯定应该取消可绘制动画。仍然没有乐趣。

我已将点击本身的响应推迟到抽屉打开动画完成之前,因此在动画发生时不会进行主要的背景或前景操作。也没有音频运行,没有服务活动可言。分析表明在动画运行时没有与绘图无关的代码在运行。

由于一切都发生得相对较快,因此很难准确判断发生了什么,除了正在发生严重的闪烁。由于涉及动画,因此无法在动画中间中断调试器以查看发生了什么。一旦我中断调试器,下一次重绘将处于完全打开状态,因为动画位置来自当前设备 uptimeMillis()。

如果我分析动画,我会看到大量的文本布局操作,这表明回收器视图条目可能会在动画期间不断地执行布局。这真的没有意义。我没有理由想到布局会发生。可能只是文本调用与 onDraw 操作有关。但我提到这一点是因为我在分析中看到的似乎有点奇怪。动画期间的大部分 CPU 时间似乎都花在了执行文本测量操作上,大概是用于 RecyclerView 条目中的内容。

发生大量透支。抽屉本身有一个包含专辑插图的大位图,而且我可能已经堆叠了半透明的图层和背景。例如RecyclerView 中各个条目的 RippleDrawable 背景很可能会导致大部分屏幕的完全过度绘制。此外,当抽屉动画到打开位置时,视图有一个完整的过度绘制通道,它会使背景内容变暗。话虽如此,当通过单击抽屉的标题触发动画时,抽屉的动画效果非常好。只是不是当启动动画的点击来自 RecyclerView 时。所以我不认为过度绘制是真正的问题。

不管怎样,显示翻录的位置似乎与选择和按下的位置无关。单击 RecyclerView 中的第一个或最后一个条目不会影响显示翻录发生的位置。 ADB shell dumpsys SurfaceFlinger 表示正在三层(状态栏、导航栏和主要活动)上执行硬件组合。所以抽屉本身没有使用 SurfaceFlinger 组合(如果它发生至少指向导致显示器撕裂的大致方向)。但是,在动画运行时,我无法真正获得 SurfaceFlinger 状态。动画期间可能正在使用硬件合成。它确实看起来像显示翻录,我想不出如果 SurfaceFLinger 不进行合成,为什么会发生翻录。

我完全不知道问题可能是什么,或者尝试调试它的方法。任何关于一般调试方法的建议,或关于问题原因的建议都将不胜感激。

【问题讨论】:

【参考方案1】:

所描述的撕裂发生在哪一层?是在抽屉本身上,还是在抽屉后面的布局上?

不管怎样,我有几个建议:

    过度绘制有时会出现问题,尤其是在布局复杂的情况下 - 我在 GoogleMaps Fragments 上特别遇到过问题。在我的案例中解决这个问题的方法是创建一个简单的透明叠加层来投射过度绘制,如下所示:

    <FrameLayout for drawer....>
    
    <View
    android:layout_
    android:layout_
    android:background="@android:color/transparent" />
    
    <LinearLayout w/ fragments etc..../>
    
    </FrameLayout>
    

    众所周知,抽屉动画不符合预期目的……它们会在布局转换期间疯狂地滞后。我知道你说过你在做任何事情之前确保抽屉动画完成,但这是值得仔细检查的。为此,我个人将所有导航代码都放在了 OnDrawerClosed() 方法中,并使用 itemSelected() 代码设置标志并关闭抽屉。

【讨论】:

撕裂发生在抽屉本身,在专辑插图位图的中间(如果重要的话)。项目 1. 非常有趣。关于为什么会有所作为的任何理论? onClick 事件发生后,RecyclerView 中一定有什么事情发生了。并且可能有。例如,刷新可绘制对象发生在已发布的事件中。重新布局和绘图也是如此。先前和当前选定的项目也已失效,因此在单击后调度的事件中也会发生重新绑定。不过,要弄清楚这一切并不容易。 是的,我当然明白为什么你对这个问题如此困惑;这听起来像是那些真正难以调试的奇怪 Android 怪癖之一。不幸的是,我自己不知道有任何简单的方法来调试奇怪的 UI 事件......我个人只会尝试尽可能简化布局并通过反复试验来隔离问题......删除过度绘制,替换位图使用更通用的视图,删除抽屉等(看似)未受影响的元素。抱歉,我无法提供更多帮助。

以上是关于如何解决抽屉动画期间的显示撕裂问题的主要内容,如果未能解决你的问题,请参考以下文章

如何更改导航抽屉图标?

如何在使用 Pace.JS 加载期间显示 Div

如何在导航抽屉中的两个片段之间传递数据

如何正确实现可拖动的“抽屉”自定义 UIView

如何在 Flutter 中收听抽屉打开/关闭动画

单击导航抽屉上的项目时如何解决问题 TabLayouts?