Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android WebView renders blank/white, view doesn't update on css changes or HTML changes, animations are choppy

When ever I make a css class change, the changes don't always appear. This appears to happen when I have a touch event that adds something like a down class name to a button. The button doesn't update, and neither does anything else on the page. It's very inconstant in when it works. I've also noticed that sometimes my elements appear white with no content or anything. This is very frustrating!

like image 721
Kyle Avatar asked Nov 21 '12 19:11

Kyle


People also ask

How do I display HTML content in WebView?

Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml. In the above code, we have taken web view to show html content.

What is WebView in HTML?

WebView is a view that display web pages inside your application. You can also specify HTML string and can show it inside your application using WebView. WebView makes turns your application to a web application. In order to add WebView to your application, you have to add <WebView> element to your xml layout file.

What is WebView in android with example?

Android WebView is used to display web page in android. The web page can be loaded from same application or URL. It is used to display online content in android activity. Android WebView uses webkit engine to display web page.

How does WebView work in Android?

The WebView class is an extension of Android's View class that allows you to display web pages as a part of your activity layout. It does not include any features of a fully developed web browser, such as navigation controls or an address bar. All that WebView does, by default, is show a web page.


2 Answers

Note:
There's a better solution as of Android 4.4+. It's a drop-in WebView replacement called CrossWalk. It uses the latest Chromium-kit and it's fantastic. You can read up about it here: crosswalk-project.org

Also, it appears that since Android 4.4, the invalidate() solution is no longer necessary and you can get away with using some of the other safer answer. I would only use this invalidate() approach as a last-ditch effort.


I'm answering my own question to hopefully help people out with the same issues as me.

I've tried several methods to making things better, even the all notorious -webkit-transform: translate3d(0,0,0); Even that didn't work all too well.

Let me share with you what did work.

First, use the most recent API. I'm using API 15. In your AndroidManifest.xml, make sure to enable hardware acceleration. If your version of API does not support this, then move on to the next bit.

If your version does support it, you can enable it by modifying your manifest:

<application    ...    android:hardwareAccelerated="true"> 

Also, make sure that your manifest has the minimum supported API to the one that you are using. Since I'm using API 15, this is what my manifest has:

<uses-sdk     android:minSdkVersion="15"     android:targetSdkVersion="15" /> 

(Update: you should now modify that values in your build.gradle)

Now, in your primary CSS file for what will be presented in a WebView, add something like this:

body div {     -webkit-transform: translate3d(0,0,0); } 

Add onto the body div bit with any other element types you have; you can probably exclude images, ul, li, etc. The reason for applying this CSS style to everything is just by trial and error, and I found that brute-applying it to everything appears to work the best. With a larger DOM tree, you may need to be more-specific. I'm not sure what the specification would be, however.

When you instantiate your WebView, there are some settings you'll want to set:

@Override public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     super.loadUrl("file:///android_asset/www/index.html");     appView.getSettings().setRenderPriority(RenderPriority.HIGH);     appView.getSettings()             .setPluginState(WebSettings.PluginState.ON_DEMAND); } 

Second to last, but crucial bit: I was reading through the source code for the WebView class and found this little tiny comment about force redrawing. There is a static final boolean, that when set to true will force the view to always redraw. I'm not huge on Java syntax, but I don't think you can directly change a static final attribute of a class. So what I ended up doing was I extended the class like so:

import org.apache.cordova.CordovaWebView;  import android.content.Context; import android.graphics.Canvas;  public class MyWebView extends CordovaWebView {     public static final String TAG = "MyWebView";      public MyWebView(Context context) {         super(context);     }      @Override     protected void onDraw(Canvas canvas) {         super.onDraw(canvas);         // Warning! This will cause the WebView to continuously be redrawn         // and will drain the devices battery while the view is displayed!         invalidate();     }  } 

Keep in mind, I'm using Cordova/PhoneGap, so I had to extend from the CordovaWebView. If you see in the onDraw method, there is a call to invalidate. This will cause the view to redraw constantly. I highly recommend adding in logic to only redraw when you need it, however.

There is one final step, if you are using Cordova. You have to tell PhoneGap to use your new WebView class instead of their own WebView class. In your MainActivity class, add this:

public void init(){     CordovaWebView webView = new MyWebView(MainActivity.this);     super.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView)); } 

That's it! Try and run your app and see if everything is much smoother. Before doing all of these changes, the pages would appear white, no CSS changes would be applied until after tapping on the screen again, animations were super choppy or not even noticeable. I should mention that the animations are still choppy, but far much less choppy than before.

If anyone has anything to add to this, just comment under. I'm always open for optimizations; and I'm fully aware there may be better ways to do what I have just recommended.

If my above solution did not work for you, could you describe your specific situation and what results you are seeing with Androids WebView?

Lastly, I have enabled this answer as a "community wiki", so anyone and everyone, feel free to make adjustments.

Thanks!


Edit:

With the most latest PhoneGap, you'll need to have your init() method look more like this:

public void init(){     CordovaWebView webView = new MyWebView(MainActivity.this);     super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView)); } 
like image 173
7 revs, 3 users 79% Avatar answered Oct 12 '22 05:10

7 revs, 3 users 79%


I implemented kyle's solution and it solved the problem. Howewer I noticed a huge battery drain on android 4.0.4 when the app was open. Also after the change I had users complaining that the swiftKey keyboard was not working with my app anymore.

Every change in my app are triggered by a user action so I came up with a modified version that only trigger invalidate() after a touchEvent:

    Handler handler = new Handler();     public boolean onTouchEvent (MotionEvent event){         super.onTouchEvent(event);         handler.postDelayed(triggerInvalidate, 60);             handler.postDelayed(triggerInvalidate, 300);         return true;     }      private Runnable triggerInvalidate=new Runnable(){         public void run(){             invalidate();         }     }; 

Never did any programming with Java so there might be a better solution to do this.

like image 27
Olivier Avatar answered Oct 12 '22 06:10

Olivier