我有一个绘图应用程序,加载复杂的绘图需要2 - 5秒(通过AsyncTask
完成)。为了获得更好的用户体验,在此期间,我将应用程序目录中存储的PNG版本的绘图作为ImageView
进行闪存,并在Activity构造函数中显示一个正在加载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
,我看到 Flink 的布局,然后当绘图加载时,黑屏大约一秒钟,最后是新的DrawingCanvas。
我假设原因与绘图循环线程的启动时间有关,我将onDraw()
的覆盖放在SurfaceView
中,它被调用,但onDraw()
中可用的画布似乎无法绘制到SurfaceView
。我还尝试在上面的XML中设置纯色背景,希望至少显示白色背景。但这些似乎从来没有生效,甚至从代码设置它。
有什么建议或解释我看到的黑屏?
编辑:
好了,我们已经确认onDraw()正在同一画布上绘制,所以我的绘制操作也保留在那里,希望在SurfaceView的初始显示时,用户可以像在常规Canvas实现中一样看到这些绘制,并且当绘制线程旋转时,它将覆盖Canvas。
然而,如果我清除了绘图线程操作,我确实看到了onDraw()的结果,但同样是在黑屏 Flink 之后。如果我完全删除onDraw()覆盖,我仍然会看到黑屏 Flink ,然后我会看到来自XML的白色背景的布局。
所以,看起来无论如何,我总是会看到黑屏,除非也许不是切换布局,我只是修改现有的'闪光'布局,已经是活动的?
编辑2:
我试过使用ViewStub,这样我就可以在注解加载后将SurfaceView膨胀到现有的视图中,但同样的问题仍然适用。据我所知,SurfaceView构造函数和对surfaceCreated()的调用执行之间有相当大的延迟(~200ms),但不确定这是黑屏发生的地方,或者为什么屏幕被绘制为黑色...
编辑3:
我现在的最后一个尝试包括制作SurfaceView transparent。这与保留现有布局并通过ViewStub简单地添加到该布局相结合,我认为这将产生一个可行的解决方案,但仍然,在加载SurfaceView时,屏幕在SurfaceView显示为透明之前 Flink 黑色。如果有人有任何其他想法可以尝试,请发布它们。
8条答案
按热度按时间hgtggwj01#
我想我找到了黑色 Flink 的原因。在我的例子中,我在一个片段中使用了一个SurfaceView,并在某个操作后将此片段动态添加到Activity中。当我将片段添加到Activity中时,屏幕 Flink 为黑色。我检查了SurfaceView源代码的grepcode,以下是我的发现:当表面视图第一次出现在窗口中时,它通过调用一个私有的
IWindowSession.relayout(..)
方法来请求改变窗口的参数。这个方法“给”你一个新的框架、窗口和窗口表面。我想屏幕就在那一刻 Flink 了。解决办法很简单:如果您的窗口已经有了合适的参数,它将不会刷新窗口的所有内容,屏幕也不会 Flink 。最简单的解决方案是在Activity的第一个布局中添加一个
0px
高度的平面SurfaceView。这将在Activity显示在屏幕上之前重新创建窗口,当您设置第二个布局时,它将继续使用具有当前参数的窗口。更新:看起来几年后这种行为仍然存在。我建议使用TextureView而不是SurfaceView。这是一个新的实现相同的东西,没有这个副作用,以及没有黑色背景的问题,当你移动它(例如在ScrollView,ViewPager,RecyclerView等)。
9rbhqvlz2#
我不会这样做与两个单独的布局。
试着让你的根元素成为一个RelativeLayout,然后让SurfaceView和ImageView成为它的兄弟孩子。无论你的线程等发生了什么,ImageView应该在SurfaceView的顶部完全不透明地绘制,这样你就不会看到闪光。
一旦你的工作线程加载了绘图并且SurfaceView可以自己绘制,从视图层次结构中移除进度条和ImageView。
eeq64g8w3#
您需要确保onSurfaceChanged()不会返回,直到您完全绘制并发布了SurfaceView的Surface的内容。
mu0hgdu04#
我假设你在你的表面视图中画了一些很重的代码,因为据我所知,表面视图在一次画完所有东西后会显示完整的视图。我建议你先去表面视图的onDraw()方法,然后在画布的背景上设置,然后调用强制无效来避免黑屏。添加一个条件来确保这个强制无效只被调用一次。
yhqotfr85#
我面临同样的问题,但感谢您的回答
还有一种不那么混乱的方法,只需输入getWindow().setFormat(PixelFormat.TRANSLUCENT);在调用setContentView()之前,在主机Activity的onCreate()回调中。
iyfamqjs6#
如果您使用NavigationDrawer与片段,比Evos解决方案将无法工作!
尝试将NavigationDrawer用于活动而不是片段,这将帮助您100%
如何使用Activity实现NavDrawer:link
另一个有用的link.(在SurfaceView的情况下将绘制在SlidingMenu的顶部)
iklwldmw7#
CrazyOrr的解决方案对我很有效,但它在Evos的热门答案的评论中很深,所以这里又是一次:
还有一种不那么混乱的方法,只需输入getWindow(). setFormat(PixelFormat.TRANSLUCENT);在调用setContentView()之前在主机活动的onCreate()回调中。-CrazyOrr 2016年5月5日7:29
简单来说
在活动的
onCreate()
中为我工作。ukxgm1gy8#
“onDraw()中可用的画布似乎无法绘制到SurfaceView”-您确定不创建第二个(或第三个...)表面视图示例吗?其中一些示例可能是黑色的,并显示一秒钟。我在这里没有看到代码,因此,我不能肯定地说,但如果它在我的应用程序中,我会首先检查此错误。