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?
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.
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 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.
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.
So I spend some time on this myself, my solution has multiple parts:
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" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With