我有一个绘图应用程序需要大约2-5秒来加载复杂图纸的绘图(通过AsyncTask
完成)。为了获得更好的用户体验,在此期间,我将应用目录中存储的PNG版本作为ImageView
进行了快照,并在活动中显示加载ProgressBar
来电setContentView()
构造:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/flash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@color/note_bg_white"
android:contentDescription="@string/content_desc_flash_img"
android:focusable="false" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
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_width="match_parent"
android:layout_height="18dp"
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_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff">
<com.my.package.DrawingCanvas
android:id="@+id/canvas"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:background="#ffffff" />
</RelativeLayout>
当我使用简单的Canvas
模型时,这非常有效,因为自定义DrawingCanvas View
会在向用户显示之前将绘图绘制到初始onDraw()
的屏幕上,但现在使用带有绘图循环的SurfaceView
,我看到闪烁的布局,然后当绘图加载时,黑屏大约一秒,然后最后是新的DrawingCanvas。
我假设原因与绘图循环线程的启动时间有关,并且我在onDraw()
中留下了SurfaceView
,并且它被调用,但是onDraw()
中可用的画布似乎没有绘制到SurfaceView
。我也尝试在XML中设置纯色背景,希望至少能够显示白色背景,但这些背景似乎永远不会生效,甚至从代码设置它。
我对黑屏的看法有任何建议或解释吗?
编辑:
好的,已经确认onDraw()正在绘制到同一个画布,所以在那里留下我的绘制操作以及希望在初始显示SurfaceView时,用户会看到像常规Canvas实现中的那些绘图,当绘图线程旋转时,它会覆盖画布。
但是,如果我清除了绘图线程的操作,我确实看到了onDraw()的结果,但是再次,黑屏闪烁后。如果我完全删除onDraw()覆盖,我仍然会看到黑色闪光,然后我会看到XML中带有白色背景的布局。
所以,看起来无论如何,我总是会看到黑屏,除非可能不是切换布局,我只是修改已经激活的现有'flash'布局?
EDIT2:
尝试使用ViewStub,以便在加载笔记后将SurfaceView扩展到现有View中,但同样的issu仍然适用。就像我所知,SurfaceView构造函数和对surfaceCreated()执行的调用之间有一个相当大的(约200ms)延迟,但不确定这是黑屏发生的原因,或者为什么屏幕被绘制到黑...
EDIT3:
我现在的最后一次尝试包括制作SurfaceView transparent。这与现有的布局相结合,只需通过ViewStub添加到该布局就可以得到一个可行的解决方案,但是,当SurfaceView加载时,在SurfaceView显示之前,屏幕闪烁黑色,透明的。如果有任何其他想法尝试,请发布它们。
答案 0 :(得分:147)
我想我找到了黑色闪光的原因。在我的情况下,我在Fragment中使用SurfaceView,并在执行某些操作后动态地将此片段添加到活动中。当我将片段添加到活动时,屏幕闪烁黑色。我检查了SurfaceView源代码的grepcode,这是我发现的:当窗口中的表面视图出现在第一次时,它通过调用私有IWindowSession.relayout(..)
方法请求窗口的参数发生变化。此方法“为您提供”新的框架,窗口和窗口表面。我觉得那一刻屏幕正好闪烁。
解决方案非常简单:如果您的窗口已经有适当的参数,它将不会刷新所有窗口的内容,屏幕也不会闪烁。最简单的解决方案是将0px
高度平面SurfaceView添加到活动的第一个布局中。这将在屏幕上显示活动之前重新创建窗口,当您设置第二个布局时,它将继续使用具有当前参数的窗口。我希望这会有所帮助。
更新:看起来好像多年后这种行为仍然存在。我建议使用TextureView而不是SurfaceView。这实际上是一个相同的新实现,没有这种副作用,并且在移动它时没有黑色背景问题(例如在ScrollView,ViewPager,RecyclerView等中)。
答案 1 :(得分:5)
我不会用两个单独的布局来做这件事。
尝试将您的根元素设为RelativeLayout,然后使SurfaceView和ImageView成为兄弟姐妹。无论你的线程等发生什么,ImageView都应该在SurfaceView上面完全不透明,所以你不会得到闪光灯。
一旦您的工作线程加载了绘图并且SurfaceView可以绘制自己,请从视图层次结构中删除进度条和ImageView。
答案 2 :(得分:2)
我假设您在表面视图中绘制了一些重型代码,因为据我所知,在一次绘制所有内容后,曲面视图将显示完整视图。我建议你先去表面视图的onDraw()方法,然后在画布的背景上设置,然后强制调用invalidate以避免黑屏。添加条件以确保仅强制使用此强制无效。
答案 3 :(得分:1)
在完全绘制并发布SurfaceView曲面的内容之前,您需要确保onSurfaceChanged()不会返回。
答案 4 :(得分:1)
如果您将NavigationDrawer与片段一起使用,则Evos解决方案将无效! 尝试使用NavigationDrawer与Activities而不是片段,这将帮助您100%
如何使用活动实现NavDrawer:link
另一个有用的link。 (如果SurfaceView将在SlidingMenu上绘制)
答案 5 :(得分:0)
“onDraw()中可用的画布似乎没有绘制到SurfaceView” - 您确定不创建Surface View的第二个(或第三个......)实例吗?其中一些可能是黑色的,并显示一秒钟。我在这里看不到代码,因此,我无法确定,但如果它在我的应用程序中,我会先检查这个错误。
答案 6 :(得分:0)
我面临同样的问题,但是感谢你的回答
有一个不那么混乱的方法,只是把 。getWindow()setFormat(PixelFormat.TRANSLUCENT);在主持人活动中 调用setContentView()之前的onCreate()回调。
答案 7 :(得分:0)
CrazyOrr的解决方案对我有用,但它对Evos的最佳答案的评论很深,所以这里又是:
有一个不那么混乱的方法,只是把 。getWindow()setFormat(PixelFormat.TRANSLUCENT);在主持人活动中 调用setContentView()之前的onCreate()回调。 - 疯狂或者5月5日 &#39; 16在7:29
简单地说
getWindow().setFormat(PixelFormat.TRANSLUCENT);
活动onCreate()
为我工作。