In fact, if you want to display complex HTML content (for simple HTML formatting TextView is enough), add JavaScript support to it, etc. the only choice you have is to use WebView component to handle all that stuff. Moreover, WebView can be configured to support pinch-to-zoom, custom URL handling, etc.
As you can see it is a really powerful component in Android components toolbox!
In this post I want to highlight several aspects of WebView usage, as long as some of them can cause confusion if you haven't met them before.
1. Display HTML data with special characters (ö, ü, ä, etc.)
It's better to set UTF-8 encoding explicitly, because some Android versions (e.g. 4.0) apparently ignore encoding inside the HTML.
Solution:
myWebView.loadData(myHtmlString, "text/html; charset=UTF-8", null);
2. Show/hide progress indicator during web page loading
No surprises here, just a tip for a common task! :-)
Solution: set custom WebViewClient with overriden onPageStarted, onPageFinished methods
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
mProgressView.setVisibility(View.VISIBLE);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
mProgressView.setVisibility(View.INVISIBLE);
}
});
3. Open some URLs with WebView, open other URLs by the default web browser
By default WebView opens all URLs in an external web browser. But if you set custom web client to the WebView (e.g. see 2nd section before), then it will open all URLs within itself. So, you need to override shouldOverrideUrlLoading and implement your custom way to decide how to proceed (open within or external browser).
Solution: set custom WebViewClient and override shouldOverrideUrlLoading
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// open URL in the web view itself
if (url.contains("sample.com"))
return super.shouldOverrideUrlLoading(view, url);
// open URL in an external web browser
else {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
}
});
4. Adjust WebView height when content changes
If you create application like Pocket, Readability or any other HTML content related application - you probably will end up with the need to change WebView content dynamically. It can be loading of the next article, or changing font size to improve users eyes comfort, etc.
And the tricky part here is that WebView (with layout_height="wrap_content") doesn't shrink down to wrap the new content with smaller height. It grows up correctly (e.g. if you increased font size or loaded an article that is bigger than the previous one). But if you load a tiny article, or set smaller font size - you will see WebView with height as if the previous longer data is still displaying. So there will be white blank space below the content. Why? Seems like a WebView bug. You can google to find that many people complain about it. For example:
http://stackoverflow.com/questions/1973565/how-to-resize-a-android-webview-after-adding-data-in-it
http://stackoverflow.com/questions/10845701/webview-size-is-expanding-but-not-contracting-as-per-the-text-in-it?lq=1
Solution:
There are lots of suggestions such as: hide and show WebView, check that layout_width was set to "wrap_content", use FrameLayout, and more. And none of them work. So I needed a solution.
The first solution I found is to create a new WebView instance and load required data into it. A fresh WebView instance has a zero height so any data will be taller and all works fine. But, it's quite ineffective to instantiate WebView all the time, also there is a blinking when an theWebView instance is replaced with a new one.
Now I needed a better solution! :-) And I've found it!
My solution is to load empty data to the WebView, before smaller content can be/should be loaded. And immediately after it proceed to the required action (e.g. load a new article, decrease font size).
// load empty data to shrink the WebView instance
mArticleWebView.loadUrl(Constants.ASSETS_EMPTY);
// load real data
mArticleWebView.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
Where Constants.ASSETS_EMPTY is just a string constant pointing to the static empty HTML page in the assets folder. So the Constants.ASSETS_EMPTY value could be "file:///android_asset/Empty.html".
With this workaround I am able to implement seamless articles switching and font size changing in my application! :-)
Thanks for sharing tips. Useful.
ReplyDeleteThanks!
Deletenice article
DeleteI tried this trick but it doesn't work. What's in Empty.html?
ReplyDeleteHello ShiFu, please be more detailed in your comments. What trick (there are several of them were mentioned) didn't work for you and under which circumstances?
DeleteEmpty.html is just an empty valid HTML document.
For example: http://pastebin.com/k6k64Jc1
The trick with empty.html doesn't work. Android 4.2.1
ReplyDeleteActually, if you're loading content from assets, then you can call reload before reloading content - this does the trick.
DeleteHi Vladimir, thanks for the hint.
DeleteActually, at the moment of writing, Android 4.2 wasn't released. So, yes, maybe! :-)
By the way, WebView itself can have different behavior on different devices: e.g. WebView with transparent background at many HTC devices will be rendered as white. This can be solved by disabling hardware acceleration, however this may be unnacceptable for some cases.
Hi Vladimir, Thanks for your trick but when i write webview.reload() it prompts me to pick browser to complete the action:( how to avoid this? Anyway i m loading the content from the database and only thing which i m using "image " stored in asset folder. Please help!!!
DeleteMany thanks , this works like charm.
ReplyDeleteThanks, Eviatar!
DeleteThis comment has been removed by a blog administrator.
ReplyDeleteHey man with WebViews inside the ListView question! :-)
ReplyDeletePlease describe (here or to my email) in more details the thing that you are trying to achieve. I will try to help.
p.s. sorry for the long answer (I had a vacation) and deleting you comment (accidentally clicked "remove comment" and seems there is no "undo" action at Blogger admin page). ^__^
is it possible to implement a web app such as this in android? how much do you expect it would cost? thanks
ReplyDeletereset layoutparams webview when load new data to fix blank space below content
ReplyDeleteLayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);
webview.setLayoutParams(params);
webview.loadData()
Very useful. Thanks
Deletethanks a ton! was really mad about this issue..finally solved :)
ReplyDeleteInstead of preparing an asset to use it by
ReplyDeletemArticleWebView.loadUrl(Constants.ASSETS_EMPTY);
you can also load an empty HTML with
mArticleWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);