Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I overlay a native view on top of PhoneGap's CordovaWebView in Android?

I'm building a phonegap plugin which needs to render a native UI view on top of the WebView that PhoneGap provides. In iOS this is very simple, just create the view and add it to PhoneGap's webView's scrollView. This will render the control on top of the webView and allow it to scroll with the HTML content (note that this example uses a UIButton, but I will be applying this to a custom UI control):

-(void)createNativeControl:(CDVInvokedUrlCommand *)command
{
    NSDictionary* options = [command.arguments objectAtIndex:0];
    NSNumber* x = [options objectForKey:@"x"];
    NSNumber* y = [options objectForKey:@"y"];
    NSNumber* width = [options objectForKey:@"width"];
    NSNumber* height = [options objectForKey:@"height"];

    CGRect rect = CGRectMake([x floatValue], [y floatValue], [width floatValue], [height floatValue]);
    self._nativeControl = [UIButton buttonWithType:UIButtonTypeSystem];
    self._nativeControl.frame = rect;

    [self._nativeControl addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self._nativeControl setTitle:@"Click me" forState:UIControlStateNormal];
    [self.webView.scrollView addSubview:self._nativeControl];

    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackID];
}

I have tried doing something roughly equivalent in Android, but have not had success. Here is my most recent attempt:

@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    System.out.println(String.format("%s action called", action));

    if ("echo".equals(action)) {
        String message = args.optString(0);
        this.echo(message, callbackContext);
        return true;
    } else if ("createNativeControl".equals(action)) {
        this.createNativeControl(callbackContext);
        return true;
    }

    return false;
}

private void createNativeControl(CallbackContext callbackContext) {
    // Find the frame layout parent and place the control on top - theoretically
    // this should appear on TOP of the webView content since the TextView child is
    // added later
    FrameLayout frameLayout = (FrameLayout) webView.getParent().getParent();

    TextView view = new TextView(frameLayout.getContext());
    view.setText("Hello, Android!");
    view.setVisibility(View.VISIBLE);

    frameLayout.addView(view, 100,100);
    callbackContext.success();
}

How can I accomplish this in Android?

like image 843
Travis Avatar asked Oct 15 '13 03:10

Travis


People also ask

Is Cordova a WebView?

Starting with Cordova 1.9, the Android platform relies on a CordovaWebView component, which builds on a legacy CordovaActivity component that pre-dates the 1.9 release.

Does Cordova use Android WebView?

I figured out that Cordova is not using the Chrome App as browser. Instead it is using the browser integrated in the "Android System WebView" app, which is updatable in Google Play Store.

How does Cordova WebView work?

How Does Cordova Work Under the Hood? Cordova's user interface is a web view. You can think of the web view as a tab in a browser. When you compile a Cordova application, it doesn't actually take your HTML, CSS, and JavaScript code and automagically converts it into native code, specific to each platform.


2 Answers

With Cordova Android 4.0.2, I was able to add a view on top of the webview, like this:

public class HVWMaps extends CordovaPlugin {

    @Override
    public void initialize(CordovaInterface cordova, CordovaWebView webView) {

        FrameLayout layout = (FrameLayout) webView.getView().getParent();

        TextView textView = new TextView(layout.getContext());
        textView.setBackgroundColor(Color.BLUE);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(500, 500);
        params.setMargins(100, 100, 100, 100);
        textView.setLayoutParams(params);
        layout.addView(textView);
    }
}

It seems that the interface for CordovaWebView has changed recently, so this may not work with older versions. For this example, you'll also need to add <param name="onload" value="true" /> to the feature in your plugin.xml so that initialize(..) gets called:

<platform name="android">
    <config-file target="res/xml/config.xml" parent="/*">
        <feature name="YourPlugin" >
            <param name="android-package" value="com.your.plugin"/>
            <param name="onload" value="true" />
        </feature>
    </config-file>
</platform>

This doesn't scroll with the webview as you mentioned iOS does, but for my project I didn't need it to.

like image 86
dwb Avatar answered Oct 19 '22 03:10

dwb


So I spend some time on this myself, my solution has multiple parts:

  • You have your CordovaWebView in your layout XML
  • Your Activity extends the CordovaActivity
  • You override few methods:

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout_page);
    super.init();
    loadUrl("file://" + getFilesDir()+"index.html");
    
    ....and other code you like...
    }
    
    
     @Override
        protected CordovaWebView makeWebView() {
            SystemWebViewEngine systemWebViewEngine =
            new SystemWebViewEngine((SystemWebView) findViewById(R.id.cordovaWebView));
    return new CordovaWebViewImpl(systemWebViewEngine);
        }
    
        @Override
        protected void createViews() {
    //must override this, otherwise crash
    if (preferences.contains("BackgroundColor")) {
        int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
        // Background of activity:
        appView.getView().setBackgroundColor(backgroundColor);
    }
            appView.getView().requestFocusFromTouch();
        }
    

So the makeWebView() is the key part, where you override the CordovaActivity's method to return your own CordovaWebView instance from the XML.

The createViews() method has to be override too, since if you check the CordovaActivity code there is a setContentView(appView.getView()), which going to cause crash otherwise. You make sure when you override you REMOVE that part.

finally the xml (not the whole content):

    <!-- The main content view -->
    <org.apache.cordova.engine.SystemWebView
    android:id="@+id/cordovaWebView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
like image 42
narancs Avatar answered Oct 19 '22 02:10

narancs