Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SurfaceView flashes black on load

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.

like image 611
Unpossible Avatar asked Jan 07 '12 20:01

Unpossible


2 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).

like image 130
Evos Avatar answered Nov 20 '22 11:11

Evos


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.

like image 5
Reuben Scratton Avatar answered Nov 20 '22 10:11

Reuben Scratton