SurfaceView 在加载时闪烁黑色
Posted
技术标签:
【中文标题】SurfaceView 在加载时闪烁黑色【英文标题】:SurfaceView flashes black on load 【发布时间】:2012-02-05 01:40:04 【问题描述】:我有一个绘图应用程序,加载复杂绘图的绘图大约需要 2-5 秒(通过 AsyncTask
完成)。为了获得更好的用户体验,在此期间,我将存储的 PNG 版本的图形从 app 目录中刷新为 ImageView
,并在 Activity 构造函数中显示调用 setContentView()
的加载 ProgressBar
:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<ImageView
android:id="@+id/flash"
android:layout_
android:layout_
android:src="@color/note_bg_white"
android:contentDescription="@string/content_desc_flash_img"
android:focusable="false" />
<RelativeLayout
android:layout_
android:layout_
android:paddingBottom="6dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:gravity="bottom|center_vertical">
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:id="@+id/toolbar_progress"
android:layout_
android:layout_
android:gravity="bottom|center_vertical" />
</RelativeLayout>
</FrameLayout>
当AsyncTask
完成后,我会使用新布局再次调用setContentView()
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_
android:layout_
android:background="#ffffff">
<com.my.package.DrawingCanvas
android:id="@+id/canvas"
android:layout_
android:layout_
android:focusable="true"
android:background="#ffffff" />
</RelativeLayout>
当我使用简单的Canvas
模型时,这非常有效,因为自定义 DrawingCanvas View
会在显示给用户之前在初始 onDraw()
上将绘图绘制到屏幕上,但现在使用 @ 987654334@ 带有绘图循环,我看到闪烁的布局,然后在加载绘图时,黑屏大约一秒钟,最后是新的 DrawingCanvas。
我假设原因与绘图循环线程的启动时间有关,并且我将onDraw()
的覆盖留在了SurfaceView
中,它被调用了,但是画布在onDraw()
似乎没有吸引到SurfaceView
。我还尝试在上面的 XML 中设置纯色背景,希望至少显示白色背景,但这些似乎从未生效,即使从代码中设置也是如此。
对我在黑屏中看到的内容有何建议或解释?
编辑:
好的,已经确认 onDraw() 正在绘制到同一个画布上,所以将我的绘制操作留在了那里,同时希望在 SurfaceView 的初始显示时,用户会像在常规 Canvas 实现中一样看到这些绘图,当绘图线程启动时,它会覆盖画布。
但是,如果我清除绘图线程操作,我确实会看到 onDraw() 结果,但同样是在黑屏闪烁之后。如果我完全删除 onDraw() 覆盖,我仍然会看到黑色闪烁,然后我会看到 XML 中带有白色背景的布局。
所以,看起来无论如何,我总是会看到黑屏,除非我可能不是切换布局,而是简单地修改已经处于活动状态的现有“flash”布局?
EDIT2:
尝试使用ViewStub,以便在加载注释后将 SurfaceView 膨胀到现有视图中,但同样的问题仍然适用。据我所知,SurfaceView 构造函数和对 surfaceCreated() 的调用之间存在相当大的延迟(约 200 毫秒),但不确定这是黑屏发生的地方,或者屏幕被绘制到的原因黑...
EDIT3:
我现在的最后一次尝试包括制作SurfaceView transparent。这与保留现有布局并简单地通过 ViewStub 添加到该布局相结合,虽然会产生一个可行的解决方案,但是,当 SurfaceView 正在加载时,屏幕会在显示 SurfaceView 之前闪烁黑色,一样透明。如果有人有任何其他想法可以尝试,请发布。
【问题讨论】:
onCreate 中的一些繁重操作可能会产生这样的结果。 我在 onCreate() 中显示了“flash”布局,没有延迟。加载绘图并启动 SurfaceView 后出现延迟/黑屏,这仅涉及启动线程。 我们可以查看您的 DrawingCanvas 类的源代码吗? 很遗憾,由于法律限制,我只能发布小片段以外的任何内容。 【参考方案1】:我想我找到了黑闪的原因。在我的情况下,我在 Fragment 中使用 SurfaceView 并在某些操作后动态将此片段添加到活动中。当我将片段添加到活动中的那一刻,屏幕闪烁黑色。我检查了 SurfaceView 源的 grepcode,我发现:当表面视图第一次出现在窗口中时,它通过调用私有 IWindowSession.relayout(..)
方法请求更改窗口的参数。这种方法“给”你一个新的框架、窗口和窗口表面。我认为屏幕在那一刻闪烁。
解决方案非常简单:如果您的窗口已经有适当的参数,它不会刷新窗口的所有内容,屏幕也不会闪烁。最简单的解决方案是将0px
高度纯SurfaceView 添加到活动的第一个布局中。这将在活动显示在屏幕上之前重新创建窗口,并且当您设置第二个布局时,它将继续使用具有当前参数的窗口。我希望这会有所帮助。
更新:看起来多年后这种行为仍然存在。我建议使用TextureView 而不是 SurfaceView。从字面上看,这是一个新的实现,没有这种副作用,并且在您移动它时也没有黑色背景问题(例如在 ScrollView、ViewPager、RecyclerView 等中)。
【讨论】:
这个答案听起来很疯狂,在您的活动布局(根布局的活动)中添加一个 0px * 0px SurfaceView,实际上消除了您片段中 SurfaceView 的黑色闪烁!我做到了,效果很好! @ErikZ:谢谢,空的 SurfaceView 解决了我的问题! 此解决方案消除了 CameraPreview as described in the documentation 的一些严重闪烁 - 特别是在开始预览时。 嗯......解决方案很脏但有效:) 还有一种不那么凌乱的方法,只需在调用 setContentView() 之前将getWindow().setFormat(PixelFormat.TRANSLUCENT);
放入宿主活动的 onCreate() 回调中即可。【参考方案2】:
我不会使用两个单独的布局来执行此操作。
尝试使您的根元素成为 RelativeLayout,然后将 SurfaceView 和 ImageView 设置为该元素的同级子元素。无论您的线程等发生什么,ImageView 都应该在 SurfaceView 上完全不透明地绘制,这样您就不会得到闪光。
一旦您的工作线程加载了绘图并且 SurfaceView 可以自行绘制,请从视图层次结构中移除进度条和 ImageView。
【讨论】:
我试过这个没有成功,但如果我找不到闪退的根本原因,我可能会很快重新审视这个。 应该可以。如果您发布 com.my.package.DrawingCanvas 的代码可能会有所帮助? 这并不能解释问题,但似乎可以作为一种解决方法,因此作为 hackbod 授予的赏金从未扩展可能是根本原因,并且我尝试过的所有内容都与该行相关思考似乎失败了。 我将创建一个演示项目并将其通过电子邮件发送给您,如果您给我一个发送地址。 已发送演示项目的电子邮件。【参考方案3】:我假设您在表面视图中绘制了一些繁重的代码,因为据我所知,表面视图将在绘制完所有内容后显示完整的视图。我建议您首先转到表面视图的 onDraw() 方法,然后在画布的背景上设置,然后强制调用无效以避免黑屏。添加一个条件以确保此强制无效仅被调用一次。
【讨论】:
【参考方案4】:我遇到了同样的问题,但感谢这个答案
有一个不那么凌乱的方法,只是把 getWindow().setFormat(PixelFormat.TRANSLUCENT);在主办活动的 调用 setContentView() 之前的 onCreate() 回调。
【讨论】:
【参考方案5】:您需要确保 onSurfaceChanged() 在您完全绘制并发布 SurfaceView 的 Surface 内容之前不会返回。
【讨论】:
如何确保 Surfaceview 被完全绘制和发布?什么可能导致在完整的帖子和绘图之前调用onSurfaceChanged()
?我已经确认视图在我的绘图线程启动之后调用 onSurfaceChanged(),但在绘图循环能够完成它的第一个操作之前(在 lockCanvas() 调用中触发?)
hackbod 是 Android 团队的高级开发人员,她可能是对的。我从来没有想过尝试从 onSurfaceChanged() 中绘图,但现在她建议这样做有点明显。她的意思是在你的 onSurfaceChanged() 中调用 holder.lockCanvas(),然后绘制一些东西,然后调用 holder.unlockCanvasAndPost()。如果您在视图树中也有一个占位符图像视图,只需用完全透明填充表面视图的画布即可达到所需的效果(不遮挡它)。
将绘图代码添加到onSurfaceChanged()
不幸的是,没有任何效果 - 仍然看到黑色闪烁。【参考方案6】:
如果您将 NavigationDrawer 与片段一起使用,则 Evos 解决方案将不起作用! 尝试将 NavigationDrawer 与活动一起使用而不是片段,这将 100% 帮助您
如何使用活动实现 NavDrawer:link 另一个有用的link。 (如果 SurfaceView 将绘制在 SlidingMenu 之上)
【讨论】:
好吧,它甚至与导航抽屉无关,它更多的是关于动态添加的带有表面的片段。【参考方案7】:CrazyOrr 的解决方案对我有用,但 Evos 的最佳答案在 cmets 很深,所以又来了:
有一个不那么凌乱的方法,只是把 getWindow().setFormat(PixelFormat.TRANSLUCENT);在主办活动的 调用 setContentView() 之前的 onCreate() 回调。 – CrazyOrr 5 月 5 日 16 年 7 点 29 分
简单地说
getWindow().setFormat(PixelFormat.TRANSLUCENT);
在活动的onCreate()
为我工作。
【讨论】:
【参考方案8】:" onDraw() 中可用的画布似乎没有绘制到 SurfaceView" - 你确定你没有创建 Surface View 的第二个(或第三个......)实例吗?其中一些可能是黑色的并显示一秒钟。我在这里看不到代码,所以我不能确定,但如果它在我的应用程序中,我会首先检查这个错误。
【讨论】:
以上是关于SurfaceView 在加载时闪烁黑色的主要内容,如果未能解决你的问题,请参考以下文章