Understanding Android WebViews

Abbas Suterwala
Share

Android provides a variety of views to build the UI of your application. One example is WebView, views which can be embedded in your Activities and used to display Web pages. The ability to display web pages in your activity has a lot of advantages as it helps reuse Web pages and Web apps in an Android app. The content of the Web page can be controlled and changed from the server directly. This is useful for pages like user agreements and terms and conditions that require only periodic changes. Webview is useful for responsive design and situations where you need to load an external page which is not under your control and does not have a public android intent exposed.

This article shows you through how to embed a WebView in your activity to display Web page.

Embedding a WebView in your activity to display a web page.

Let’s start by adding the WebView to an activity. To add a WebView to a layout xml, use the following code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <WebView
      android:id="@+id/webview"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
  />
</LinearLayout>

In the code above I created a linear layout which will be a container view to hold the WebView. Inside the linear layout, I added a WebView to load a Web page via URL. As loading the Webpage via URL requires an internet connection, add the following permission to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Once the layout and permission is defined, create an activity which will use this layout and load the Web page:

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

public class WebVievActivityWithURL extends Activity {

    private WebView myWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_web_view);

        myWebView = (WebView) findViewById(R.id.webview);

        myWebView.loadUrl("https://m.facebook.com");
    }

}

In the code above, the onCreate of the activity set the content view as the layout defined and then gets the WebView using the function findViewById. The loadUrl function then loads the url into the WebView. Once complete and the activity launches, you should see the web page loaded.

Facebook login screen

Embedding a WebView in your activity to display static HTML.

In some cases you might not want to load a Web page from an URL but directly load HTML content into the WebView. WebView provides functionality to do this using the loadDataWithBaseURL function:

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

public class WebViewActivityWithHTML extends Activity {

    private WebView myWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_web_view);

        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.loadDataWithBaseURL("file:///android_asset/", getHTMLData(),
                "text/html", "UTF-8", "");
    }

    private String getHTMLData() {
        StringBuilder html = new StringBuilder();
        html.append("<html>");
        html.append("<head>");

        html.append("<link rel=stylesheet href='css/style.css'>");
        html.append("</head>");
        html.append("<body>");
        html.append("<div id ='main'> Loading html data in WebView</div>");
        html.append("</body>");
        html.append("</html>");

        return html.toString();
    }
}

The WebViewActivityWithHTML activity above uses the same activity_with_web_view layout which already defined. In the onCreate function, the reference to the WebView appears again. Use this to call loadDataWithBaseURL, which takes the base_url used for all relative paths in the HTML content.

Next, pass file:///android_asset/ for the CSS to be detected. The second parameter is the HTML data. The other parameters are the mime type, character encoding and history url.

To avoid passing the base URL, use the function loadData which takes three parameters: data, mimetype and character encoding.

I have referred to css/style.css in the HTML. To create this, add a directory called css in the assets folder and a style.css file inside that with the following content:

#main {
    color: #00ff00;
}

Once WebViewActivityWithHTML launches, the WebView loads with the static HTML.

Loading WebView

Handling navigation in WebViews

When using WebViews, callbacks might be required during the loading of a Web page. For example, when a link is clicked in the WebView, Android performs the default action, which is likely opening the URL in a different browser.

To override this and load the URL in the WebView itself, provide a WebViewClient instance in the WebView which will handle callbacks:

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class WebViewActivityWithWebClient extends Activity {

    private WebView myWebView;

    /** (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_web_view);

        myWebView = (WebView) findViewById(R.id.webview);
        myWebView.setWebViewClient(new MyWebViewClient());

        myWebView.loadData(getHTMLData(),"text/html", "UTF-8");
    }

    /**
     * @return - Static HTML data
     */
    private String getHTMLData() {
        StringBuilder html = new StringBuilder();
        html.append("<html>");
        html.append("<head>");
        html.append("</head>");
        html.append("<body>");
        html.append("<div id ='main'> <a href ='https://m.facebook.com'> Go to Face book</a></div>");
        html.append("</body>");
        html.append("</html>");

        return html.toString();
    }

    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
           view.loadUrl(url);
           return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            //You can add some custom functionality here
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
          //You can add some custom functionality here
        }

        @Override
        public void onReceivedError(WebView view, int errorCode,
                String description, String failingUrl) {
            super.onReceivedError(view, errorCode, description, failingUrl);
          //You can add some custom functionality here
        }
     }

}

The above code sets a custom WebView client which overrides the shouldOverrideUrlLoading function and loads the URL right into the WebView rather than another browser and then returns true to signify that it will override the url loading. If we run this activity and click on the Go to Facebook link it will open the Facebook link right in the WebView.

There are other callbacks such as onPageStarted, onPageFinished and onReceivedError which can be used for different kinds on processing based on app requirements.

Opening a Link

Injecting JavaScript in WebViews

When building more complex Web pages for mobile you might need to call some native methods from your Web page. To do this in a WebView, enable the JavaScript setting in the WebView and provide a class which will expose methods to call from JavaScipt:

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import android.app.Activity;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

public class WebViewActivityWithJavaScript extends Activity {
    private WebView myWebView;

    /* (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_web_view);

        myWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = myWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        myWebView.addJavascriptInterface(new MyJavaScriptInterface(this), "Android");
        myWebView.loadData(getHTMLData(),"text/html", "UTF-8");
    }

    /**
     * @return - Static HTML data
     */
    private String getHTMLData() {
        StringBuilder html = new StringBuilder();
        try {
            AssetManager assetManager = getAssets();

            InputStream input = assetManager.open("javascriptexample.html");
            BufferedReader  br = new BufferedReader(new InputStreamReader(input));
            String line;
            while ((line = br.readLine()) != null) {
                html.append(line);
            }
            br.close();
        } catch (Exception e) {
           //Handle the exception here
        }

        return html.toString();
    }

    public class MyJavaScriptInterface {
        Activity activity;

        MyJavaScriptInterface(Activity activity) {
            this.activity = activity;
        }

        @JavascriptInterface
        public void showToast(String toast) {
            Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show();
        }

        @JavascriptInterface
        public void closeActivity() {
            activity.finish();
        }
    }
}

In the code above, WebSettings of the WebView are revealed by calling the getSettings method and enabling JavaScript using the method setJavaScriptEnabled.

Then call the addJavascriptInterface method and pass it an object and a string. The string can be used to call methods on the object which have the annotation @JavascriptInterface. In the MyJavaScriptInterface class are two methods: showToast, which shows an Android toast and closeActivity, which finishes the activity.

In the above example, loadData loads the webpage (loadUrl could load a Web page from an url) which fetches the HTML in the getHTMLData function from javascriptexample.html in the assets folder.

Create the file javascriptexample.html in the assets folder:

<html>

<body>
<input type="button" value="Show Toast" onClick="showAndroidToast('Toast to JavaScript interface')" />
<br>
<input type="button" value="Close this Activity" onClick="closeActivity()" />
</body>
<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }

     function closeActivity() {
        Android.closeActivity();
    }
</script>

</html>

The code above creates two buttons, which when clicked call a Javascript function. These in turn call the JavaScipt interface functions which we exposed. If we run this activity and click on Show Toast, the toast is shown.

Android Toast

Conclusion

WebViews in Android provide a rich set of functionality and are important in building an app which needs to load external Web pages or HTML data. WebViews provide interesting methods for Web pages to interact with the Android app using the JavaScript interface.

Have fun using the WebView in your next Android app and please let me know if you have any questions or comments.

CSS Master, 3rd Edition