注意: - 我正在使用Cordova 1.9.0和JQM 1.1.0,我在Android 2.3上测试
这是我的 ChildBrowser.java
import java.io.IOException;
import java.io.InputStream;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.text.InputType;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class ChildBrowser extends Plugin {
protected static final String LOG_TAG = "ChildBrowser";
private static int CLOSE_EVENT = 0;
private static int LOCATION_CHANGED_EVENT = 1;
private String browserCallbackId = null;
private Dialog dialog;
private WebView webview;
private EditText edittext;
private boolean showLocationBar = true;
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
Log.i("ChildBrowser", "Plugin Called");
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
if (action.equals("showWebPage")) {
this.browserCallbackId = callbackId;
// If the ChildBrowser is already open then throw an error
if (dialog != null && dialog.isShowing()) {
return new PluginResult(PluginResult.Status.ERROR, "ChildBrowser is already open");
}
result = this.showWebPage(args.getString(0), args.optJSONObject(1));
if (result.length() > 0) {
status = PluginResult.Status.ERROR;
return new PluginResult(status, result);
} else {
PluginResult pluginResult = new PluginResult(status, result);
pluginResult.setKeepCallback(true);
return pluginResult;
}
}
else if (action.equals("close")) {
closeDialog();
JSONObject obj = new JSONObject();
obj.put("type", CLOSE_EVENT);
PluginResult pluginResult = new PluginResult(status, obj);
pluginResult.setKeepCallback(false);
return pluginResult;
}
else if (action.equals("openExternal")) {
result = this.openExternal(args.getString(0), args.optBoolean(1));
if (result.length() > 0) {
status = PluginResult.Status.ERROR;
}
}
else {
status = PluginResult.Status.INVALID_ACTION;
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param usePhoneGap Load url in PhoneGap webview
* @return "" if ok, or error message.
*/
public String openExternal(String url, boolean usePhoneGap) {
try {
Intent intent = null;
if (usePhoneGap) {
intent = new Intent().setClass((Context) this.ctx.getActivity(), org.apache.cordova.DroidGap.class);
intent.setData(Uri.parse(url)); // This line will be removed in future.
intent.putExtra("url", url);
// Timeout parameter: 60 sec max - May be less if http device timeout is less.
intent.putExtra("loadUrlTimeoutValue", 60000);
// These parameters can be configured if you want to show the loading dialog
intent.putExtra("loadingDialog", "Wait,Loading web page..."); // show loading dialog
intent.putExtra("hideLoadingDialogOnPageLoad", true); // hide it once page has completely loaded
}
else {
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
}
this.ctx.getActivity().startActivity(intent);
return "";
} catch (android.content.ActivityNotFoundException e) {
Log.d(LOG_TAG, "ChildBrowser: Error loading url "+url+":"+ e.toString());
return e.toString();
}
}
/**
* Closes the dialog
*/
private void closeDialog() {
if (dialog != null) {
dialog.dismiss();
}
}
/**
* Checks to see if it is possible to go back one page in history, then does so.
*/
private void goBack() {
if (this.webview.canGoBack()) {
this.webview.goBack();
}
}
/**
* Checks to see if it is possible to go forward one page in history, then does so.
*/
private void goForward() {
if (this.webview.canGoForward()) {
this.webview.goForward();
}
}
/**
* Navigate to the new page
*
* @param url to load
*/
private void navigate(String url) {
InputMethodManager imm = (InputMethodManager)this.ctx.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
if (!url.startsWith("http") && !url.startsWith("file:")) {
this.webview.loadUrl("http://" + url);
} else {
this.webview.loadUrl(url);
}
this.webview.requestFocus();
}
/**
* Should we show the location bar?
*
* @return boolean
*/
private boolean getShowLocationBar() {
return this.showLocationBar;
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param jsonObject
*/
public String showWebPage(final String url, JSONObject options) {
// Determine if we should hide the location bar.
if (options != null) {
showLocationBar = options.optBoolean("showLocationBar", true);
}
// Create dialog in new thread
Runnable runnable = new Runnable() {
/**
* Convert our DIP units to Pixels
*
* @return int
*/
private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
(float) dipValue,
ctx.getActivity().getResources().getDisplayMetrics()
);
return value;
}
public void run() {
// Let's create the main dialog
dialog = new Dialog((Context) ctx, android.R.style.Theme_NoTitleBar);
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCancelable(true);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
try {
JSONObject obj = new JSONObject();
obj.put("type", CLOSE_EVENT);
sendUpdate(obj, false);
} catch (JSONException e) {
Log.d(LOG_TAG, "Should never happen");
}
}
});
// Main container layout
LinearLayout main = new LinearLayout((Context) ctx);
main.setOrientation(LinearLayout.VERTICAL);
// Toolbar layout
RelativeLayout toolbar = new RelativeLayout((Context) ctx);
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
toolbar.setHorizontalGravity(Gravity.LEFT);
toolbar.setVerticalGravity(Gravity.TOP);
// Action Button Container layout
RelativeLayout actionButtonContainer = new RelativeLayout((Context) ctx);
actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
actionButtonContainer.setId(1);
// Back button
ImageButton back = new ImageButton((Context) ctx);
RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);
backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
back.setLayoutParams(backLayoutParams);
back.setContentDescription("Back Button");
back.setId(2);
try {
back.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_left.png"));
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
back.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goBack();
}
});
// Forward button
ImageButton forward = new ImageButton((Context)ctx);
RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);
forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
forward.setLayoutParams(forwardLayoutParams);
forward.setContentDescription("Forward Button");
forward.setId(3);
try {
forward.setImageBitmap(loadDrawable("www/childbrowser/icon_arrow_right.png"));
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
forward.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goForward();
}
});
// Edit Text Box
edittext = new EditText((Context) ctx);
RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
edittext.setLayoutParams(textLayoutParams);
edittext.setId(4);
edittext.setSingleLine(true);
edittext.setText(url);
edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
edittext.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
navigate(edittext.getText().toString());
return true;
}
return false;
}
});
// Close button
ImageButton close = new ImageButton((Context) ctx);
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
close.setLayoutParams(closeLayoutParams);
forward.setContentDescription("Close Button");
close.setId(5);
try {
close.setImageBitmap(loadDrawable("www/childbrowser/icon_close.png"));
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
// WebView
webview = new WebView((Context) ctx);
webview.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
webview.setWebChromeClient(new WebChromeClient());
WebViewClient client = new ChildBrowserClient(edittext);
webview.setWebViewClient(client);
WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setBuiltInZoomControls(true);
settings.setPluginsEnabled(true);
settings.setDomStorageEnabled(true);
webview.loadUrl(url);
webview.setId(6);
webview.getSettings().setLoadWithOverviewMode(true);
webview.getSettings().setUseWideViewPort(true);
webview.requestFocus();
webview.requestFocusFromTouch();
// Add the back and forward buttons to our action button container layout
actionButtonContainer.addView(back);
actionButtonContainer.addView(forward);
// Add the views to our toolbar
toolbar.addView(actionButtonContainer);
toolbar.addView(edittext);
toolbar.addView(close);
// Don't add the toolbar if its been disabled
if (getShowLocationBar()) {
// Add our toolbar to our main view/layout
main.addView(toolbar);
}
// Add our webview to our main view/layout
main.addView(webview);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.FILL_PARENT;
lp.height = WindowManager.LayoutParams.FILL_PARENT;
dialog.setContentView(main);
dialog.show();
dialog.getWindow().setAttributes(lp);
}
private Bitmap loadDrawable(String filename) throws java.io.IOException {
InputStream input = ctx.getActivity().getAssets().open(filename);
return BitmapFactory.decodeStream(input);
}
};
this.ctx.getActivity().runOnUiThread(runnable);
return "";
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param obj a JSONObject contain event payload information
*/
private void sendUpdate(JSONObject obj, boolean keepCallback) {
if (this.browserCallbackId != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
result.setKeepCallback(keepCallback);
this.success(result, this.browserCallbackId);
}
}
/**
* The webview client receives notifications about appView
*/
public class ChildBrowserClient extends WebViewClient {
EditText edittext;
/**
* Constructor.
*
* @param mContext
* @param edittext
*/
public ChildBrowserClient(EditText mEditText) {
this.edittext = mEditText;
}
/**
* Notify the host application that a page has started loading.
*
* @param view The webview initiating the callback.
* @param url The url of the page.
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String newloc;
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
newloc = url;
} else {
newloc = "http://" + url;
}
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc);
}
try {
JSONObject obj = new JSONObject();
obj.put("type", LOCATION_CHANGED_EVENT);
obj.put("location", url);
sendUpdate(obj, true);
} catch (JSONException e) {
Log.d("ChildBrowser", "This should never happen");
}
}
}
}
这是我的 childbrowser.js
/**
* Constructor
*/
function ChildBrowser() {
};
ChildBrowser.CLOSE_EVENT = 0;
ChildBrowser.LOCATION_CHANGED_EVENT = 1;
/**
* Display a new browser with the specified URL.
* This method loads up a new web view in a dialog.
*
* @param url The url to load
* @param options An object that specifies additional options
*/
ChildBrowser.prototype.showWebPage = function(url, options) {
if (options === null || options === "undefined") {
var options = new Object();
options.showLocationBar = true;
}
cordova.exec(this._onEvent, this._onError, "ChildBrowser", "showWebPage", [url, options]);
};
/**
* Close the browser opened by showWebPage.
*/
ChildBrowser.prototype.close = function() {
cordova.exec(null, null, "ChildBrowser", "close", []);
};
/**
* Display a new browser with the specified URL.
* This method starts a new web browser activity.
*
* @param url The url to load
* @param usecordova Load url in cordova webview [optional]
*/
ChildBrowser.prototype.openExternal = function(url, usecordova) {
if (usecordova === true) {
navigator.app.loadUrl(url);
}
else {
cordova.exec(null, null, "ChildBrowser", "openExternal", [url, usecordova]);
}
};
/**
* Method called when the child browser has an event.
*/
ChildBrowser.prototype._onEvent = function(data) {
if (data.type == ChildBrowser.CLOSE_EVENT && typeof window.plugins.childBrowser.onClose === "function") {
window.plugins.childBrowser.onClose();
}
if (data.type == ChildBrowser.LOCATION_CHANGED_EVENT && typeof window.plugins.childBrowser.onLocationChange === "function") {
window.plugins.childBrowser.onLocationChange(data.location);
}
};
/**
* Method called when the child browser has an error.
*/
ChildBrowser.prototype._onError = function(data) {
if (typeof window.plugins.childBrowser.onError === "function") {
window.plugins.childBrowser.onError(data);
}
};
/**
* Maintain API consistency with iOS
*/
ChildBrowser.install = function(){
return window.plugins.childBrowser;
};
/**
* Load ChildBrowser
*/
cordova.addConstructor(function() {
cordova.addPlugin("childBrowser", new ChildBrowser());
});
在 index.html 我已经像这样使用了它
<script type="text/javascript" charset="utf-8" src="cordova-1.9.0.js"></script>
<script type="text/javascript" charset="utf-8" src="childbrowser.js"></script>
<script type="text/javascript">
function init(){
document.addEventListener("deviceready",onDeviceReady,false);
}
function onDeviceReady(){
console.log("Hello");
window.plugins.childBrowser.showWebPage("http://www.google.com", { showLocationBar: true });
}
</script>
在 plugins.xml 我添加了这样的
<plugins>
<plugin name="childBrowser" value="com.phonegap.test.ChildBrowser"></plugin>
</plugins>
我得到的日志是
I/dalvikvm(15883): Could not find method android.webkit.WebView.<init>, referenced from method org.apache.cordova.CordovaWebView.<init>
W/dalvikvm(15883): VFY: unable to resolve direct method 303: Landroid/webkit/WebView;.<init> (Landroid/content/Context;Landroid/util/AttributeSet;IZ)V
D/dalvikvm(15883): VFY: replacing opcode 0x70 at 0x0001
D/dalvikvm(15883): VFY: dead code 0x0004-0059 in Lorg/apache/cordova/CordovaWebView;.<init> (Landroid/content/Context;Landroid/util/AttributeSet;IZ)V
I/CordovaLog(15883): Changing log level to DEBUG(3)
I/CordovaLog(15883): Found preference for useBrowserHistory=false
D/CordovaLog(15883): Found preference for useBrowserHistory=false
E/dalvikvm(15883): Could not find class 'android.webkit.WebResourceResponse', referenced from method org.apache.cordova.CordovaWebViewClient.generateWebResourceResponse
W/dalvikvm(15883): VFY: unable to resolve new-instance 115 (Landroid/webkit/WebResourceResponse;) in Lorg/apache/cordova/CordovaWebViewClient;
D/dalvikvm(15883): VFY: replacing opcode 0x22 at 0x0046
W/dalvikvm(15883): VFY: unable to find class referenced in signature (Landroid/webkit/WebResourceResponse;)
D/dalvikvm(15883): VFY: dead code 0x0048-004c in Lorg/apache/cordova/CordovaWebViewClient;.generateWebResourceResponse (Ljava/lang/String;)Landroid/webkit/WebResourceResponse;
W/dalvikvm(15883): VFY: unable to find class referenced in signature (Landroid/webkit/WebResourceResponse;)
W/dalvikvm(15883): VFY: unable to find class referenced in signature (Landroid/webkit/WebResourceResponse;)
I/dalvikvm(15883): Could not find method android.webkit.WebViewClient.shouldInterceptRequest, referenced from method org.apache.cordova.CordovaWebViewClient.shouldInterceptRequest
W/dalvikvm(15883): VFY: unable to resolve virtual method 322: Landroid/webkit/WebViewClient;.shouldInterceptRequest (Landroid/webkit/WebView;Ljava/lang/String;)Landroid/webkit/WebResourceResponse;
D/dalvikvm(15883): VFY: replacing opcode 0x6f at 0x0015
D/dalvikvm(15883): VFY: dead code 0x0018-0019 in Lorg/apache/cordova/CordovaWebViewClient;.shouldInterceptRequest (Landroid/webkit/WebView;Ljava/lang/String;)Landroid/webkit/WebResourceResponse;
D/DroidGap(15883): DroidGap.init()
D/CordovaWebView(15883): >>> loadUrl(file:///android_asset/www/index.html)
D/PluginManager(15883): init()
D/CordovaWebView(15883): >>> loadUrlNow()
D/DroidGap(15883): onMessage(onPageStarted,file:///android_asset/www/index.html)
D/SoftKeyboardDetect(15883): Ignore this event
D/SoftKeyboardDetect(15883): Ignore this event
D/szipinf(15883): Initializing inflate state
D/szipinf(15883): Initializing zlib to inflate
D/Cordova(15883): onPageFinished(file:///android_asset/www/index.html)
D/CordovaWebView(15883): >>> loadUrlNow()
D/DroidGap(15883): onMessage(onNativeReady,null)
D/DroidGap(15883): onMessage(onPageFinished,file:///android_asset/www/index.html)
I/Database(15883): sqlite returned: error code = 14, msg = cannot open file at source line 25467
D/CordovaLog(15883): [DEPRECATION NOTICE] window.addPlugin and window.plugins will be removed in version 2.0.
D/CordovaLog(15883): file:///android_asset/www/cordova-1.9.0.js: Line 294 : [DEPRECATION NOTICE] window.addPlugin and window.plugins will be removed in version 2.0.
I/Web Console(15883): [DEPRECATION NOTICE] window.addPlugin and window.plugins will be removed in version 2.0. at file:///android_asset/www/cordova-1.9.0.js:294
D/DroidGap(15883): onMessage(networkconnection,3g)
D/CordovaLog(15883): Hello
D/CordovaLog(15883): file:///android_asset/www/index.html: Line 38 : Hello
I/Web Console(15883): Hello at file:///android_asset/www/index.html:38
D/CordovaLog(15883): Error: SyntaxError: Unable to parse JSON string
D/CordovaLog(15883): file:///android_asset/www/cordova-1.9.0.js: Line 1012 : Error: SyntaxError: Unable to parse JSON string
I/Web Console(15883): Error: SyntaxError: Unable to parse JSON string at file:///android_asset/www/cordova-1.9.0.js:1012
D/dalvikvm(15883): GC_CONCURRENT freed 327K, 51% free 2905K/5831K, external 884K/1038K, paused 5ms+17ms
D/DroidGap(15883): onMessage(spinner,stop)
是否有其他人面临与我相同的问题?如何解决这个问题?
答案 0 :(得分:3)
在你的plugins.xml中你有:
<plugin name="childBrowser" value="com.phonegap.test.ChildBrowser"></plugin>
但它应该是:
<plugin name="ChildBrowser" value="com.phonegap.test.ChildBrowser"></plugin>
childbrower.js代码需要将Java代码映射到“ChildBrowser”。你可以看到案件问题。如果查看cordova.exec()命令,您会看到“ChildBrowser”是服务名称。