Home » Android » android – SurfaceView flashes black on load

android – SurfaceView flashes black on load

Posted by: admin April 23, 2020 Leave a comment

Questions:

I have a drawing app that takes about 2-5 seconds to load the drawing for complicated drawings (done via an AsyncTask). For a better user experience, during this time I flash the stored PNG version of the drawing I have from the app directory as an ImageView, and show a loading ProgressBar calling setContentView() in the Activity constructor:

<?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>

When the AsyncTask is complete, I then call setContentView() again with the new layout:

<?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>

When I was using a simple Canvas model, this worked perfectly, as the custom DrawingCanvas View would draw the drawing to the screen on the initial onDraw() before being shown to the user, but now using the SurfaceView with a drawing loop, I am seeing the flashed layout, then when the drawing is loaded, a black screen for about a second, then finally the new DrawingCanvas.

I am assuming that the reason is related to the start-up time of the drawing loop thread, and I’ve left my overide of onDraw() in the SurfaceView, and it gets called, but the canvas available in onDraw() does not seem to draw to the SurfaceView. I’ve also tried setting a solid color background in the XML above hoping at a minimum to show a white background, but those never seem to take affect, even setting it from code.

Any advice or an explanation of what I am seeing with the black screen?

EDIT:

Ok, have confirmed that onDraw() is drawing to the same canvas, so left my draw ops in there as well hoping that on the initial showing of the SurfaceView, the user would see those drawings like in the regular Canvas implementation, and when the drawing thread had spun up, it would overwrite the Canvas.

However, if I clear the drawing thread operations, I do see the onDraw() results, but again, AFTER the black screen flash. And if I remove the onDraw() override completely, I still see the black flash and then I see the layout with the white background from the XML.

So, it looks like no matter what, I am always going to see the black screen, unless perhaps instead of switching the layouts, I simply modify the existing ‘flash’ layout that is already active?

EDIT2:

Have tried using a ViewStub so that I can inflate the SurfaceView into the existing View after the note is loaded, but the same issu still applies. As near as I can tell, there is a sizable (~200ms) delay between the SurfaceView constructor and the call to surfaceCreated() executing, but not sure that this is where the black screen is happening, or why the screen is being drawn to black…

EDIT3:

My final attempt for now includes making the SurfaceView transparent. This combined with leaving the existing layout in place and simply adding to that layout via the ViewStub would have resulted in a working solution I though, but still, for a split second when the SurfaceView is loading the screen flashes black before the SurfaceView is shown, as transparent. If anyone has any other ideas to try, please post them.

How to&Answers:

I think I found the reason for the black flash. In my case I’m using a SurfaceView inside a Fragment and dynamically adding this fragment to the activity after some action. The moment when I add the fragment to the activity, the screen flashes black. I checked out grepcode for the SurfaceView source and here’s what I found: when the surface view appears in the window the very fist time, it requests the window’s parameters changing by calling a private IWindowSession.relayout(..) method. This method “gives” you a new frame, window, and window surface. I think the screen blinks right at that moment.

The solution is pretty simple: if your window already has appropriate parameters it will not refresh all the window’s stuff and the screen will not blink. The simplest solution is to add a 0px height plain SurfaceView to the first layout of your activity. This will recreate the window before the activity is shown on the screen, and when you set your second layout it will just continue using the window with the current parameters. I hope this helps.

UPDATE: Looks like after years this behavior is still there. I would recommend to use TextureView instead of SurfaceView. This is literally a newer implementation of same thing that don’t have this side effect as well as don’t have a problem of black background when you moving it (for instance within ScrollView, ViewPager, RecyclerView etc).

Answer:

I would not do this with two separate layouts.

Try making your root element a RelativeLayout and then having the SurfaceView and ImageView be sibling children of that. Whatever happens with your threading etc, the ImageView should be drawn wholly opaque on top of the SurfaceView, so you won’t get a flash.

Once your worker thread has loaded the drawing and the SurfaceView can draw itself, remove the progress bar and ImageView from the view hierarchy.

Answer:

I assume that you’re drawing some heavy code in your surface view because as far as I know, the surface view will show the complete view after drawing everything one time. I suggest you first go to the onDraw() method of surface view then set on background of canvas then call forcefully invalidate to avoid that black screen. Add a condition to be sure that this forcefully invalidate is only called once.

Answer:

You need to make sure that onSurfaceChanged() doesn’t return until you have completely drawn and posted the contents of the SurfaceView’s Surface.

Answer:

If you use NavigationDrawer with fragments, than Evos solution will not work!
Try to use NavigationDrawer with Activities instead of fragments, this will help you 100%

How to implement NavDrawer with activities: link
Another helpful link. (in case of SurfaceView will draw on top of SlidingMenu)

Answer:

” the canvas available in onDraw() does not seem to draw to the SurfaceView”
– Are you sure that you do not create the second (or third…) instance of the Surface View? And some of them could be black and be shown for a second. I don’t see the code here, so, I can’t tell surely, but if it were in my application, I would check for this error at first.

Answer:

I facing the same issue but thank for this answer

There is a less messy approach, just put
getWindow().setFormat(PixelFormat.TRANSLUCENT); in the host activity’s
onCreate() callback before calling setContentView().

Answer:

CrazyOrr’s solution worked for me, but it was deep in the comments for the top answer by Evos, so here it is again:

There is a less messy approach, just put
getWindow().setFormat(PixelFormat.TRANSLUCENT); in the host activity’s
onCreate() callback before calling setContentView(). – CrazyOrr May 5
’16 at 7:29

Simply putting

getWindow().setFormat(PixelFormat.TRANSLUCENT); 

in the activity’s onCreate() worked for me.