Embed Webview in Android Fragment

Anthony Rumsey edited this page May 28, 2014 · 8 revisions
Clone this wiki locally

Embedding a Cordova WebView into an Android fragment

This tutorial demonstrates how to embed a Cordova WebView into an Android fragment. The Phonegap documentation already provides a good overview on how to embed a Cordova WebView in an activity however there are some key differences when using Fragments.

Background

Android introduced fragments in Android 3.0 (API level 11), primarily to support more dynamic and flexible UI designs on large screens, such as tablets.

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities).

Setup

Follow the the existing Phonegap documentation to include the Cordova .jar file as part of your Android project build.

Fragment Layout

A Fragment defines its own view layout. This is where the CordovaWebView component will need to be included.

eg. /res/layout/my_cordova_frag.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="0dp">

    <org.apache.cordova.CordovaWebView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id = "@+id/myWebView"/>

</LinearLayout>

Fragment Class

Modify an existing fragment class (or create a new one) so that it extends Fragment and implements the CordovaInterface.

  1. Override onCreateView to inflate the layout containing the Cordova Web View using a custom Cordova context.
  2. Use the static Cordova Config class to initialize the view based on the supplied config resource.
  3. Specify the initial URL to load into the webview.
public class MyFragment extends Fragment implements CordovaInterface {
    CordovaWebView myWebView;

    public static MyFragment newInstance() {
        MyFragment fragment = new MyFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        LayoutInflater localInflater = inflater.cloneInContext(new CordovaContext(getActivity(), this));
        View rootView = localInflater.inflate(R.layout.my_cordova_frag, container, false);
        myWebView = (CordovaWebView) rootView.findViewById(R.id.myWebView);
        Config.init(getActivity());
        myWebView.loadUrl(Config.getStartUrl());
        return rootView;
    }
}

Cordova Context proxy

A local inflater needs to be cloned for this fragment with a custom context. The custom context acts as a proxy for the Cordova web view that is initialized on a activity and the fragment. As the web view makes calls on the activity the custom context takes care of delegating to the fragment instance.

    private class CordovaContext extends ContextWrapper implements CordovaInterface
    {
        CordovaInterface cordova;

        public CordovaContext(Context base, CordovaInterface cordova) {
            super(base);
            this.cordova = cordova;
        }
        public void startActivityForResult(CordovaPlugin command,
                                           Intent intent, int requestCode) {
            cordova.startActivityForResult(command, intent, requestCode);
        }
        public void setActivityResultCallback(CordovaPlugin plugin) {
            cordova.setActivityResultCallback(plugin);
        }
        public Activity getActivity() {
            return cordova.getActivity();
        }
        public Object onMessage(String id, Object data) {
            return cordova.onMessage(id, data);
        }
        public ExecutorService getThreadPool() {
            return cordova.getThreadPool();
        }
    }

Implement Cordova Interface

With the custom context implemented all the Cordova methods can be added to the Fragment class much like would be done on a CordovaActivity.

    // Plugin to call when activity result is received
    protected CordovaPlugin activityResultCallback = null;
    protected boolean activityResultKeepRunning;

    // Keep app running when pause is received. (default = true)
    // If true, then the JavaScript and native code continue to run in the background
    // when another application (activity) is started.
    protected boolean keepRunning = true;

    private final ExecutorService threadPool = Executors.newCachedThreadPool();

    public Object onMessage(String id, Object data) {
        return null;
    }

    public void onDestroy() {
        super.onDestroy();
        if (myWebView.pluginManager != null) {
            myWebView.pluginManager.onDestroy();
        }
    }

    @Override
    public ExecutorService getThreadPool() {
        return threadPool;
    }

    @Override
    public void setActivityResultCallback(CordovaPlugin plugin) {
        this.activityResultCallback = plugin;
    }

    public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
        this.activityResultCallback = command;
        this.activityResultKeepRunning = this.keepRunning;

        // If multitasking turned on, then disable it for activities that return results
        if (command != null) {
            this.keepRunning = false;
        }

        // Start activity
        super.startActivityForResult(intent, requestCode);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        CordovaPlugin callback = this.activityResultCallback;
        if (callback != null) {
            callback.onActivityResult(requestCode, resultCode, intent);
        }
    }

Copy Files

Now that the a Fragment in your Android project has been configured to accept a Cordova web view the final step is to copy content.

Be sure to build your Phonegap project for the android platform first before copying.

Web Content

Copy the application's HTML and JavaScript files to the Android project's /assets/www directory. This should also include any plugins that were added during the Phonegap build process.

/platforms/android/assets/www -> /assets/www

Plugins

Copy native source/JARs for any plugins your Phonegap project uses.

/platforms/android/libs -> /libs
/platforms/android/src -> /src/main/java

(do not include the default activity added by the Phonegap build)

Config XML

Copy the config.xml file to the project's /res/xml directory.

/platforms/android/res/xml/config.xml -> /res/xml/config.xml