Previous  | Next  | Home

WebView Demo


In A Simple First App we created our first simple app. Let's now build a somewhat more sophisticated Android project. We shall put two buttons on the screen, and then have the buttons load particular web addresses into new screens when they are pressed. This is more advanced than our first simple project because now we must manage more than one button on the screen, and because in the Simple First App project we displayed a webpage using the default web browser built into the system, but in this project we shall display the content of web addresses in a basic browser that we shall create and manage ourselves programatically using the Android WebView class.

Although all of these steps are fairly rudimentary, completing them will already teach us a lot about how to build Android applications. In this one example we shall gain experience in how to

In short, when we finish this project we will have already touched on many basics of Android development. So let's get started!


Creating the Project in Android Studio

Following the general procedure in Creating a New Project, either choose Start a new Android Studio project from the Android Studio homepage, or from the Android Studio interface choose File > New > New Project. Fill out the fields in the resulting screens as follows,

Application Name: WebViewDemo
Company Domain:< YourNamespace >
Package Name: <YourNamespace> . webviewdemo
Project Location: <ProjectPath> WebViewDemo
Target Devices: Phone and Tablet; Min SDK API 15
Add an Activity: Empty Activity
Activity Name: MainActivity (check the Generate Layout File box)
Layout Name: activity_main

where your namespace should be substituted for <YourNamespace> (com.lightcone in my case) and <ProjectPath> is the path to the directory where you will store this Android Studio Project (/home/guidry/StudioProjects/ in my case).

A project WebViewDemo should now appear in the left panel (project panel) of the Android Studio interface, as in the figure below. (Note: choose Android in the drop-down menu at the top of the project panel if it is not already chosen.) If it is not already open, open the file in the main editing window, as illustrated in the following figure:

We see that Android Studio has generated automatically a stub for the MainActivity class:

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }

This is a short segment of code, but it already does a lot. We (through our loyal sidekick Android Studio) have now:

Before proceeding, if the import commands at the top of this file are folded up in the AS code listing, click the + sign to their left to expand them.

If you have chosen to use version control for your projects (I recommend it strongly!), now would be a good time to commit this project to version control so that from this point onward your changes can be tracked and stored by the version control system.


Adding OnClickListener

Let us now modify this stub so that it can do some useful things. First, since we are going to add button widgets to the page for which we will need to listen for and process click events, we have the class implement the OnClickListener interface by modifying the first line defining the class to read

public class MainActivity extends AppCompatActivity implements OnClickListener {

In the AS display the word "OnClickListener" turns red, indicating a compile error. If you click with the mouse on the word and execute Alt-Enter from the keyboard, a popup suggesting possible fixes should appear, as in the following figure.

Since OnClickListener is an Android interface, the most likely problem is just that we have not imported the appropriate Android classes and indeed the two options for quick fixes indicate that we should import android.view.View or android.content.DialogInterface. The first choice is the appropriate one in this context. We can insert it manually, but Android Studio will do it for us: clicking on the first choice, we see that AS automatically inserts a new import statement

import android.view.View;

(If the import statements are folded up, click the + beside them to expand them), and OnClickListener is converted to View.OnClickListener. But now a new compile error appears, indicated by a wavy red line under the entire line containing View.OnClickListener and a red lightbulb above the line. Clicking on the line to give focus and executing Alt-Enter gives the popup with suggested fixes shown in the following figure.

The choice that we want is "Implement methods", which will implement a method that is required by OnClickListener. Select that and the following window should appear:

The choice onClick(v:View):void is the method required (a good thing, since it is the only option). Select it and click OK. Android Studio will add some more code, and now the errors should disappear, indicating that the project has compiled successfully. The code now reads

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; import android.view.View; public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onClick(View v) { } }

where the added code is indicated in red. This compiles with no error messages because a stub for the method onClick(View v) has been inserted that satisfies the formal requirements for the OnClickListener interface. However the added onClick method does not do anything yet and we still must add the code appropriate to our goals.

An interface in Java is a reference type similar to, but not the same as, a class. An interface cannot contain any method bodies, only constants, nested types, and method signatures (methods without bodies). An interface cannot be instantiated; it can only be
  • implemented (by classes), or

  • extended (by other interfaces).
An abstract method is one that is declared without an implementation (with the expectation that the implementation will be supplied by something else). If a class contains abstract methods, it itself must be declared abstract. All methods of an interface are implicitly abstract.

When a class implements an interface, it must provide a method body for each of the methods declared in the interface, or the class must be declared abstract. Notice that these were the two quick-fix options suggested above by Android Studio:
  1. add the unimplemented method onClick(View v), or

  2. declare the class MainActivity abstract
for the error introduced when we implemented the OnClickListener interface.


Android Studio Is Only a Faithful Servant

Obviously AS has been very helpful in laying out the basic initial structure of our class, but it has only done what a good programmer would have done by a manual analysis.

  1. When "implements OnClickListener" was added it was necessary to import the Android classes defining this interface, and since an interface in Java generally requires that certain methods be implemented (even if only as stubs), we had to add those required methods.

  2. In this case, if we had consulted the Android documentation by going to the Android developer docs and putting "OnClickListener" in the search field we would have gotten two options:

  3. Choosing the first we get documentation for the interface View.OnClickListener. So we must import android.view.View.OnClickListener.

  4. Furthermore, we find on the same page that this interface has the single method public abstract void onClick (View v).

  5. Thus, by the general rules for Java interfaces, we must provide an implementation of this method if we employ the interface View.OnClickListener (or we must declare the class abstract).

All that Android Studio has done is carry out this sequence of programmer deductions rather automatically.


Layout of the User Interface

Now we wish to

  1. Lay out a page containing some button widgets,

  2. Define event handlers for the buttons, and

  3. Implement some actions when the buttons are clicked.

First let's define some string and color resources that will be needed. Open the file strings.xml under res/values and edit it to read

<resources> <string name="app_name">WebViewDemo</string> <string name="button1_label">Chili Recipe</string> <string name="button2_label">NOAA Tropical Satellite</string> </resources>

Then open the file colors.xml and edit it to read as follows:

<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <color name="background">#fffcfcf2</color> </resources>

where the numbers are hexadecimal (hex) numbers specifying colors.

In Android, 32-bit colors with alpha-opacity are specified by a sequence #AARRGGBB where
  • AA is a hexadecimal (hex) number defining the alpha-opacity,

  • RR is a hex number defining the amount of red,

  • GG is a hex number defining the amount of green, and

  • BB is a hex number defining the amount of blue.
The hex numbers range from 00 to ff (that is, 0-255 decimal), with 00 meaning no contribution for the component and ff meaning full contribution. For example, opaque black would be specified by #ff000000 (full opacity, but no RGB colors). It is also possible to define
  • 24-bit color (#RRGGBB),

  • 16-bit color with alpha-opacity (#ARGB),

  • and 12-bit color (#RGB).
Finally, the Color class has a few colors predefined as static constants (for example, Color.MAGENTA or Color.TRANSPARENT).

You can also edit these XML and Java files manually using a text editor if you choose, by navigating to the appropriate directories in the directory tree of your workspace on your disk that corresponds to the project tree in Android Studio. Be very careful with editing XML however, as it requires exact syntax and form or it will generate an error message when you try to compile. For example, any whitespace before the first XML line may generate an error message.

We are now ready to lay out the opening screen of our app. Open res > layout and double-click on activity_main.xml to open it. In the Design view, set the theme to Holo Light. Then edit the file in the Text view to read

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="" xmlns:tools="" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="16dp" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingTop="16dp" android:background="@color/background" tools:context="<YourNamespace>.webviewdemo.MainActivity"> <Button android:id="@+id/button1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/button1_label" android:textSize="18sp" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginTop="56dp" /> <Button android:id="@+id/button2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/button2_label" android:textSize="18sp" android:layout_below="@+id/button1" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> </RelativeLayout>

In this file

The units for sizes and distances used in this layout are discussed in the following box, and a more complete discussion of XML attributes may be found in the Android Layout document. A comprehensive list of XML attributes in Android may be found at R.attr.

Notice the size units dp, sp in these layout elements. Generally in Android layout sizes can be specified in
  • pixels (px),

  • millimeters (mm),

  • inches (in),

  • points (pt), which are equal to 1/72 of an inch,

  • density-independent pixels (dp or dip), or

  • scale-independent pixels (sp).
It is good practice to use sp for font sizes and dip for other user interface sizes rather than absolute units like inches or pixels, since then your interface will scale better on different devices with different screen resolutions.

We now should have an opening screen laid out that will display two buttons. Execute the program on an emulator or a phone or tablet:

  1. Click on the Run button (right-pointing green triangle) in the toolbar.

  2. Choose an emulator or a connected device, if prompted to do so.

For example, the following prompt asks to choose among one of two emulators and a Huawei Nexus 6P phone attached to the computer by USB

Choose and click OK. You should see on the emulator or device a screen like that shown below (which is a screenshot from a Huawei Nexus 6P phone),

where the strings displayed as titles and button labels were those defined in strings.xml, and the background color was defined in colors.xml.

You can easily take screenshots from emulators or actual devices in your own development using Android Studio. Instructions are given in the Appendix Device Screen Shots.


Using Intents to Launch New Activities

This is now a user interface with a label and two buttons, but it doesn't do anything except that the buttons may change color momentarily to indicate that they have been clicked by the mouse on the emulator (or pressed by a finger on an actual device). Now we shall add event handlers to cause actions to be taken when the buttons are clicked. When each button is clicked, we wish to launch a new screen (that will replace the original screen) on which we are going to do something. As we discussed in Android User Interfaces the standard way to do that is to launch an intent, with the corresponding screen defined by a new Java class. Let's first create the basic stub for the new class, leaving some details to be filled in later, and then define the event handlers that open the new screen.

Right click on app/java/<YourNamespace>.webviewdemo in the AS project pane and select New > Java Class. In the resulting popup window, give the new class the name Webscreen,

and click OK. This should create a new file Edit this file so that it has the content

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; import android.webkit.WebView; import android.util.Log; public class Webscreen extends Activity { public static final String URL = ""; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.webscreen); } }

(Several imports have been added and a String variable URL has been defined that we will need shortly.)

  1. In R.layout.webscreen the word "webscreen" will turn red, indicating a compile error.

  2. The R. notation indicates that this is a resource that Java expects Android to supply at compile time and it is missing.

  3. Indeed, clicking on the red word with the mouse and then executing Alt-Enter will generate a popup indicating that we need to create a layout resource file called webscreen.xml. The full notation R.layout.webscreen indicates that it expects this resource to reside in a file res/layout/webscreen.xml (see the box below), which of course has not been created yet.

At compilation, each XML resource file is compiled into a View resource.
  • The resource is loaded by calling setContentView(int layoutResID) from the Activity.onCreate() callback, which is called by the Android framework at launch of the app; see the lifecycle diagram in Application Lifecycles.

  • The reference layoutResID to your layout resource is passed as an argument to setContentView in the form R.layout.layout_file_name, where layout_file_name is the name of the file (without extension) that you have placed in res/layout containing the layout specification.
For a more extensive discussion, see the Android Layout document.

So this problem can be fixed by adding a file webscreen.xml that will define the layout of the new screen:

  1. Right-click on res/layout and select New > XML > Layout XML File.

  2. In the resulting popup window choose a layout file name webscreen (no .xml extension; AS will supply it) and a LinearLayout for the XML root tag.

Click Finish. The resulting file (webscreen.xml) reads

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent"> </LinearLayout>

and you should find that the error indicated for has disappeared because now the resource webscreen.xml (which is supplied as R.layout.webscreen by Android at compilation) is defined.


Event Handlers

Now we add event listeners and edit the onClick method of to add the corresponding event handlers so that the file reads

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; import android.view.View; import android.content.Intent; public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Add Click listeners for all buttons View firstButton = findViewById(; firstButton.setOnClickListener(this); View secondButton = findViewById(; secondButton.setOnClickListener(this); } // Process the button click events @Override public void onClick(View v) { switch(v.getId()){ case Intent j = new Intent(this, Webscreen.class); j.putExtra(<YourNamespace>.webviewdemo.Webscreen.URL, ""); startActivity(j); break; case Intent k = new Intent(this, Webscreen.class); k.putExtra(<YourNamespace>.webviewdemo.Webscreen.URL, ""); startActivity(k); break; } } }

where the added and modified lines are indicated in red and you must replace <YourNamespace> with your namespace. A number of important things are happening in this compact segment of code:

The putExtra method of the Intent class is a way to pass variables to the class that will be launched by the intent. In this case the value of a string corresponding to a web address (the first argument of putExtra) is communicated by assigning its value to a string variable defined in the class launched by the intent (the second argument), but variables of other types may be passed in this way also. See the various overloaded versions of putExtra(String name, Bundle value) documented under the class Intent. Note that the variable name in the putExtra arguments must have a package prefix (e.g., <YourNamespace>.myClass rather than myClass).

This should now compile, but if we launch it on the emulator or device and press one of the buttons we get an error popup:

Checking the logcat file (see The Purr of the Logcat) we find the source of the problem immediately:

06-12 22:57:11.302 I/ActivityManager(59):Starting activity:Intent { cmp=com.lightcone.webviewdemo/.Webscreen (has extras)} 06-12 22:57:11.302 D/AndroidRuntime(399):Shutting down VM 06-12 22:57:11.302 W/dalvikvm(399): threadid=1:thread exiting with uncaught exception (group=0x4001d800) 06-12 22:57:11.342 E/AndroidRuntime(399):FATAL EXCEPTION: main 06-12 22:57:11.342 E/AndroidRuntime(399):android.content.ActivityNotFoundException: Unable to find explicit activity class {com.lightcone.webviewdemo/com.lightcone.webviewdemo.Webscreen}; have you declared this activity in your AndroidManifest.xml?

This is because Android requires that all activities must be declared in the AndroidManifest.xml file. To fix it,

  1. Open app/manifests/AndroidManifest.xml and

  2. Add an appropriate <activity></activity> tag within the <application></application> tag

so that the manifest file now reads

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="" package="<YourNamespace>.webviewdemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="WebViewDemo" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Webscreen" android:label="Web"> </activity> </application> </manifest>

where the added line is highlighted in red. If the app is executed now, clicking on the first button loads a new blank screen, hitting the return button on the phone takes us back to the first screen, and clicking on the second button again loads a new blank screen. We are getting close! Now we just need to add content to these blank screens.


Adding WebViews

First, let's check that the variables are being passed by the putExtra command to the new activities launched with the intent. Edit the file so that it reads

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; import android.webkit.WebView; import android.util.Log; public class Webscreen extends Activity { public static final String URL = ""; private static final String TAG = "Class Webscreen"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.webscreen); String turl = getIntent().getStringExtra(URL); Log.i(TAG, "URL = "+turl); } }

The getIntent().getStringExtra(URL) now assigns the string variable passed using putExtra, and the Log.i allows us to check whether the variable is being passed correctly.

The method getIntent() of the Activity class returns the Intent that started the activity. The method getStringExtra(String) of the Intent class takes a String argument corresponding to a variable passed to the Intent using putExtra() and returns its String value. Thus the net effect of the chained expression

String turl = getIntent().getStringExtra(URL);
is to use the getIntent() method of Webscreen (which it inherits from its superclass Activity) to return the Intent that launched Webscreen, and then to use the getStringExtra method of that Intent to return the String corresponding to the variable URL that was passed to the Intent using the Intent method putExtra(), and finally assign it to the local String variable turl.

Executing the app in the emulator after these changes and clicking the two buttons successively we find in the logcat output

06-12 23:40:19.131 I/ActivityManager(59):Starting activity: Intent { cmp=com.lightcone.webviewdemo/.Webscreen (has extras)} 06-12 23:40:19.231 I/WebscreenClass(501): URL= 06-12 23:40:19.521 I/ActivityManager(59): Displayed activity com.lightcone.webviewdemo/.Webscreen: 354 ms (total 354 ms) 06-12 23:40:22.071 W/KeyCharacterMap(501): No keyboard for id 0 06-12 23:40:22.071 W/KeyCharacterMap(501): Using default keymap: /system/usr/keychars/qwerty.kcm.bin 06-12 23:40:23.361 I/ActivityManager(59): Starting activity: Intent{ cmp=com.lightcone.webviewdemo/.Webscreen (has extras)} 06-12 23:40:23.441 I/WebscreenClass(501): URL=

Thus, the web addresses are indeed being passed to the Webscreen instance. Now finally, let us embed a WebView in the screen generated by the Webscreen object so that we can display these web addresses as web pages. Add code to so that it reads

package <YourNamespace>.webviewdemo; import; import android.os.Bundle; import android.webkit.WebView; import android.util.Log; public class Webscreen extends Activity { public static final String URL = ""; private static final String TAG = "WebscreenClass"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.webscreen); String turl = getIntent().getStringExtra(URL); Log.i(TAG, " URL = "+turl); WebView webview = new WebView(this); setContentView(webview); // Simplest usage: No exception thrown for page-load error webview.loadUrl(turl); } }

This should compile, but when we try to execute in the emulator we find that the buttons attempt to load webpages but we get error messages in both cases that the webpage is not available. There could be many reasons (like the network being down) for such messages but in this case the explanation is quite basic: we have not yet done one final thing that is required for an Android application that tries to access the internet. It must be given explicit permission to do so, and this is done by adding to the AndroidManifest.xml file a permission line (which must be within the manifest tag and outside the application tag)

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

so that the AndroidManifest.xml file now reads

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="" package="<YourNamespace>.webviewdemo"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="WebViewDemo" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Webscreen" android:label="Web"> </activity> </application> </manifest>

Now execution in the emulator or on a device gives the left figure below for a click on the first button and the right figure below for a click on the second button. (The left image is a screen shot from a Nexus 6P phone running Android 6.0 and the right image is a screen shot from a Galaxy Nexus phone running Android 4.2.)

So our application is indeed loading and displaying---under our complete programming control---arbitrary webpages using an extension of the WebView class.

Each time WebViewDemo has been run on an emulator or device the project has been installed on that emulator or device as an app. If you look in the apps folder (open by pressing the circle with 6 dots in it near the bottom of the homescreen), there should now be an icon labeled WebViewDemo, and you can launch the app by pressing that icon (and it will remain on the device unless uninstalled).

By default, when a project is created in Android Studio a file called ic_launcher.png that corresponds to a stock Android image (see the green bugroid icon for WebView Demo in the above figure) is placed in all res/mipmap/ic_launcher.png directories corresponding to images to be used for devices of different screen resolution, and an attribute of the form android:icon="@mipmap/ic_launcher" is inserted in the AndroidManifest.xml file:

<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity">
This causes the project to use the image in the ic_launcher.png file as the app icon (at a resolution appropriate for the device). Your app can be customized by replacing the default icon in the following way. In Android Studio with the project open, select File > New > Image Asset. In the resulting popup window,
  • Choose Launcher Icons in the first field and select Image for the asset type.

  • In the Path field that then appears, navigate to an image in PNG, JPEG, or GIF (PNG preferred) format stored on your computer and select it. This image should now appear at different sizes corresponding to different device resolutions in the window. Use the tools in the window to crop, scale, etc. as desired.

  • Change the name in the Name field from the default ic_launcher to something new. For, example, in the following I have substituted a picture of your humble servant, and renamed the icon ic_launcher_mwg.

    Warning: the icon image name can contain only lowercase a-z, the numbers 0-9, or underscores. Anything else will generate errors. If icons are put with an invalid name, they may have to be removed manually from the subdirectories of /app/src/main/res/mipmap-hdpi/ on your file system.


  • Click Next, which will give a summary page. Click Finish if you are satisfied.

  • Go to the AndroidManifest.xml file and change the name of the icon from ic_launcher to the new name (ic_launcher_mwg in the preceding example).
Now if the app is installed again and you look in the app drawer, you should see that the icon associated with the app has changed to the new one:

Admittedly my picture is hardly improvement over the default green bugroid, but this example shows the general procedure to replace the default icon. (You can switch back to the default icon by changing the icon attribute in AndroidManifest.xml back to ic_launch.)

Use of customized icons is optional for development but you will certainly want to do this if you intend to release your app for others to use. There are some tricks of the trade associated with effective icon design for small screens. If you decide to make a custom icon, consult the Android icon design guidelines in the Iconography document for guidance.

The complete project for the application described above is archived at the link WebViewDemo. Instructions for installing it in Android Studio may be found in Packages for All Projects.

Last modified: July 25, 2016


1. By default the WebView class implements a view with an embedded webpage that provides no browser-like widgets, does not enable JavaScript, and ignores web page errors (this is the mode in which we employed it). Add to the embedded webview for WebViewDemo some of the customizations such as error handling, settings, and progress notification that are discussed in the WebView documentation. [Solution]

2. Use the documentation of the WebSettings and WebView classes to modify WebViewDemo so that zoom controls are built into the displayed webpage. [Solution]

3. Use the procedure outlined in the box above to replace the default launcher icon for this project with a custom icon of your choice.

Previous  | Next  | Home