Screen blinking when using a webview with flash

Solution 1:

THIS ANSWER ADDED LATER:

So the problem is that Adobe's Flash Player creates a SurfaceView inside the WebView and there is a delay between the SurfaceView appearing and the Flash engine bothering to draw anything. In that small gap the SurfaceView appears totally black.

The solution I've found is to subclass WebView and add a customized SurfaceView to the view tree. Secondly it is also important to suppress the first draw() call to the Adobe view. The code for my subclassed WebView is as follows:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.webkit.WebView;
import android.widget.AbsoluteLayout;

public class FixAdobeWebView extends WebView {

    View whiteView;
    private boolean eatenFirstFlashDraw;

    public FixAdobeWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        whiteView = new WhiteSurfaceView(context);
        whiteView.setLayoutParams(new AbsoluteLayout.LayoutParams(800, 480, 0, 0));
        addView(whiteView);
    }


    private class WhiteSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
        public WhiteSurfaceView(Context context) {
            super(context);
            getHolder().addCallback(this);
        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Canvas canvas = holder.lockCanvas();
            if (canvas != null) {
                canvas.drawColor(Color.WHITE);
                holder.unlockCanvasAndPost(canvas);
            }
        }
        @Override
        public void surfaceCreated(SurfaceHolder holder) { }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) { }
    }


    //
    // Override drawChild to eat the first draw of the FlashPaintSurface
    //
    @Override 
    protected boolean drawChild (Canvas canvas, View child, long drawingTime) {
             if (!eatenFirstFlashDraw && child.getClass().getName().equals("com.adobe.flashplayer.FlashPaintSurface")) {
                 eatenFirstFlashDraw = true;
                 return true;
             }
        return super.drawChild(canvas, child, drawingTime);
    }
}

The layout XML should declare an instance of this class, i.e. :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/white"
    >
<org.test.FixAdobeWebView android:id="@+id/webview1" 
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    />
</LinearLayout>

On my Nexus One running CM6.1 this works perfectly in that the WebView appears as solid white until the Flash SurfaceView starts drawing. You will probably need to adapt it a bit, e.g. get rid of the hardcoded 800x480 dimensions, and also destroy the white SurfaceView once it's no longer needed.

ORIGINAL (WRONG) ANSWER:

Looks like Adobe are to blame for this one.

The least awful workaround I've found so far is to :

  1. Make the background of the WebView's parent a compatible colour (i.e. white).
  2. Hide the WebView initially.
  3. Wait until you get the page loaded event, and then queue a runnable to show the WebView after a one second delay.

It means you get a guaranteed one second delay before you can see anything, but it's less jarring than that horrible black flash afaik.

Layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/white"
    >
<WebView android:id="@+id/webview1" 
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    android:visibility="invisible"    
    />
</LinearLayout>

Code:

    final Handler handler = new Handler();
    ...
    final WebView mWebView1 = (WebView) findViewById(R.id.webview1);
    mWebView1.getSettings().setJavaScriptEnabled(true);
    mWebView1.getSettings().setPluginsEnabled(true);
    mWebView1.setWebViewClient(new WebViewClient() {
        @Override 
        public void onPageFinished(WebView view, String url) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mWebView1.setVisibility(View.VISIBLE);                      
                }
            }, 1000);
        }
    });
    mWebView1.loadUrl("file:///android_asset/graph_01.swf");        

There might be a better way though that would remove the need for a fixed delay...

It's been a while since I did anything with Flash but I seem to recall there is a way for the Flash control to use the browser's Javascript engine (a quick Google turns up the Flex Ajax Bridge which seems likely).

What you could do is, with the aforementioned bridge, use ActionScript to call a Javascript function which does an alert(). In your Java code you'd hook your mWebView1 up to a WebChromeClient which implements onJsAlert(). In that function you'd make the WebView visible.