本人只是菜鸡一枚,写博客只是为了做一下笔记,年纪大了。如果有说得不准确或者不好的地方请指出!!虚心请教大神指点。如果你觉得本文章太基础没什么意义,也给点掌声鼓励鼓励。
上一篇文章用cordova的plugin使得html可以调用Activity并且获取Activity回调的数据,前文连接:http://blog.csdn.net/tangjiarao/article/details/48288875。但是上一篇文章出现以下一些问题:
1、没有实现混合开发
上一篇文章加载html的页面是cordovaActivity,它继承Activity,且有一个CordovaWebView对象—appView,appView并不是一个真正的View,它是一个Interface。当cordovaActivity调用loadUrl()方法去加载页面的时候,其实是通过appView来加载。以下是CordovaActivity 的部分源码:
- public class CordovaActivity extends Activity {
- ......
- protected CordovaWebView appView;
- ......
- /**
- * Load the url into the webview.
- */
- public void loadUrl(String url) {
- if (appView == null) {
- init();
- }
- // If keepRunning
- this.keepRunning = preferences.getBoolean("KeepRunning", true);
- appView.loadUrlIntoView(url, true);
- }
public class CordovaActivity extends Activity {
......
protected CordovaWebView appView;
......
/**
* Load the url into the webview.
*/
public void loadUrl(String url) {
if (appView == null) {
init();
}
// If keepRunning
this.keepRunning = preferences.getBoolean("KeepRunning", true);
appView.loadUrlIntoView(url, true);
}
CordovaWebView是一个接口,它不是一个View,它需要一个像webView一样的容器给它去显示html页面,所以需要通过一个显示容器去实例化它。其中的appView.getView();方法就是用来显示html的真实的View。以下是CordovaActivity 的部分源码:
- protected void init() {
- appView = makeWebView();
- createViews();
- if (!appView.isInitialized()) {
- appView.init(cordovaInterface, pluginEntries, preferences);
- }
- cordovaInterface.onCordovaInit(appView.getPluginManager());
- // Wire the hardware volume controls to control media if desired.
- String volumePref = preferences.getString("DefaultVolumeStream", "");
- if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
- }
- }
- ......
- protected void createViews() {
- //Why are we setting a constant as the ID? This should be investigated
- appView.getView().setId(100);
- appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- setContentView(appView.getView());
- if (preferences.contains("BackgroundColor")) {
- int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
- // Background of activity:
- appView.getView().setBackgroundColor(backgroundColor);
- }
- appView.getView().requestFocusFromTouch();
- }
- /**
- * Construct the default web view object.
- *
- * Override this to customize the webview that is used.
- */
- protected CordovaWebView makeWebView() {
- return new CordovaWebViewImpl(makeWebViewEngine());
- }
- protected CordovaWebViewEngine makeWebViewEngine() {
- return CordovaWebViewImpl.createEngine(this, preferences);
- }
- protected CordovaInterfaceImpl makeCordovaInterface() {
- return new CordovaInterfaceImpl(this) {
- @Override
- public Object onMessage(String id, Object data) {
- // Plumb this to CordovaActivity.onMessage for backwards compatibility
- return CordovaActivity.this.onMessage(id, data);
- }
- };
- }
- ......
protected void init() {
appView = makeWebView();
createViews();
if (!appView.isInitialized()) {
appView.init(cordovaInterface, pluginEntries, preferences);
}
cordovaInterface.onCordovaInit(appView.getPluginManager());
// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
}
......
protected void createViews() {
//Why are we setting a constant as the ID? This should be investigated
appView.getView().setId(100);
appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(appView.getView());
if (preferences.contains("BackgroundColor")) {
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
// Background of activity:
appView.getView().setBackgroundColor(backgroundColor);
}
appView.getView().requestFocusFromTouch();
}
/**
* Construct the default web view object.
*
* Override this to customize the webview that is used.
*/
protected CordovaWebView makeWebView() {
return new CordovaWebViewImpl(makeWebViewEngine());
}
protected CordovaWebViewEngine makeWebViewEngine() {
return CordovaWebViewImpl.createEngine(this, preferences);
}
protected CordovaInterfaceImpl makeCordovaInterface() {
return new CordovaInterfaceImpl(this) {
@Override
public Object onMessage(String id, Object data) {
// Plumb this to CordovaActivity.onMessage for backwards compatibility
return CordovaActivity.this.onMessage(id, data);
}
};
}
......
从以下代码看出,appView的大小已经是设定为全屏幕覆盖了,所有修改appView的大小和位置都很不方便,甚至与原生的控件结合都很难
- <span style="font-size:10px;">ppView.getView().setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));</span>
<span style="font-size:10px;">ppView.getView().setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));</span>
那应该怎么解决?
2.Java只是回调将值返回给html的js回调方法,但是却没有真正地主动去调用js方法。
3.定义插件的方法不规范
所以所以!!先来看看演示效果:
FirstActivity,包含了一个native按钮,还有一个html页面。在对话框输入内容,点击enter。
跳转到NAtive SecondActivity,并且显示信息,在输入框中输入内容,sendBack
SecondActivity Message下方的红色字体,就是从SecondActivity回调回来的
点击确认按钮,在Button Message下方显示的就是主动调用JS方法输出的内容
实现步骤:
(1)刚刚说到用CordovaActivity来结合原生很复杂,所以用cordova提供的一个控件——SystemWebView。它是继承WebView的,在CordovaActivity中的appView.getView();实际上返回的是它。由于它是继承webView的,它就相当于一个控件,你可以在xml中定义它,使用它,确定它的位置。以下是xml布局文件:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="match_parent">
- <Button
- android:id="@+id/bt1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="确定"/>
- <org.apache.cordova.engine.SystemWebView
- android:id="@+id/cordovaView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
- </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/bt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
<org.apache.cordova.engine.SystemWebView
android:id="@+id/cordovaView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
再来看看FirstActivity的实现:
- public class FirstActivity extends Activity {
- /**
- * 一个加载网页的容器,继承WebView
- */
- private SystemWebView systemWebView;
- private CordovaWebView cordovaWebView;
- private ConfigXmlParser parser;
- private MyCordovaInterfaceImpl myCordovaInterface;
- private Button b;
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.second);
- b =(Button)findViewById(R.id.bt1);
- systemWebView =(SystemWebView)findViewById(R.id.cordovaView);
- //为初始化CordovaWebView提供参数
- parser=new ConfigXmlParser();
- //这里会解析res/xml/config.xml配置文件
- parser.parse(this);
- //实例化CordovaWebView
- cordovaWebView= new CordovaWebViewImpl(new SystemWebViewEngine(systemWebView));
- //为初始化CordovaWebView提供参数,传入本Activity
- myCordovaInterface =new MyCordovaInterfaceImpl(this);
- //初始化CordovaWebView
- cordovaWebView.init(myCordovaInterface, parser.getPluginEntries(), parser.getPreferences());
- //执行加载动作
- cordovaWebView.loadUrl("file:///android_asset/www/InputData.html");
- b.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- //调用html的JS方法
- cordovaWebView.loadUrl("javascript:javaCallJs()");
- }
- });
- }
- /**
- * 为初始化CordovaWebView提供参数
- */
- private static class MyCordovaInterfaceImpl extends CordovaInterfaceImpl {
- private Activity mActivity;
- public MyCordovaInterfaceImpl(Activity activity) {
- super(activity);
- mActivity = activity;
- }
- public Activity getActivity() {
- return mActivity;
- }
- }
- /**
- * 接收从SecondActivity传来的数据,并且调用htmlJS方法来显示
- * @param requestCode
- * @param resultCode
- * @param intent
- */
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
- switch (resultCode) {
- case Activity.RESULT_OK:
- Bundle b=intent.getExtras();
- String str=b.getString("flags");
- cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");
- break;
- default:
- break;
- }
- }
- }
public class FirstActivity extends Activity {
/**
* 一个加载网页的容器,继承WebView
*/
private SystemWebView systemWebView;
private CordovaWebView cordovaWebView;
private ConfigXmlParser parser;
private MyCordovaInterfaceImpl myCordovaInterface;
private Button b;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
b =(Button)findViewById(R.id.bt1);
systemWebView =(SystemWebView)findViewById(R.id.cordovaView);
//为初始化CordovaWebView提供参数
parser=new ConfigXmlParser();
//这里会解析res/xml/config.xml配置文件
parser.parse(this);
//实例化CordovaWebView
cordovaWebView= new CordovaWebViewImpl(new SystemWebViewEngine(systemWebView));
//为初始化CordovaWebView提供参数,传入本Activity
myCordovaInterface =new MyCordovaInterfaceImpl(this);
//初始化CordovaWebView
cordovaWebView.init(myCordovaInterface, parser.getPluginEntries(), parser.getPreferences());
//执行加载动作
cordovaWebView.loadUrl("file:///android_asset/www/InputData.html");
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//调用html的JS方法
cordovaWebView.loadUrl("javascript:javaCallJs()");
}
});
}
/**
* 为初始化CordovaWebView提供参数
*/
private static class MyCordovaInterfaceImpl extends CordovaInterfaceImpl {
private Activity mActivity;
public MyCordovaInterfaceImpl(Activity activity) {
super(activity);
mActivity = activity;
}
public Activity getActivity() {
return mActivity;
}
}
/**
* 接收从SecondActivity传来的数据,并且调用htmlJS方法来显示
* @param requestCode
* @param resultCode
* @param intent
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
switch (resultCode) {
case Activity.RESULT_OK:
Bundle b=intent.getExtras();
String str=b.getString("flags");
cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");
break;
default:
break;
}
}
}
有没有觉得实例化CordovaWebView的方式很眼熟,其实就是把CordovaActivity实例化appView的方法抽离出来,再给它一个可以自定义修改位置的SystemWebView,那你就可以自由地放置它的位置
以下代码是主动调用html里面的JS方法,通过loadUrl(JavaScript:+jscode);方式,这个jscode可以是一个方法也可以是一串js代码。
- b.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- //调用html的JS方法
- cordovaWebView.loadUrl("javascript:javaCallJs()");
- }
- });
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//调用html的JS方法
cordovaWebView.loadUrl("javascript:javaCallJs()");
}
});
onActivityResult()是用来接收从SecondActivity回传的信息,在上一篇文章里面,我们把它写在插件里面来接收回调,那现在为什么要把onActivityResult()方法写在FirstActivity呢?对于这个问题我也很烦恼,我试了很多次,查找了网上很多资料,看了源码又改过源码,但是SecondActivity回传的信息却一直返回到FirstActivity中,所以我唯有在FirstActivity中接收这个回调Intent,然后主动调用JS方法来达到修改html的效果。但是如果用cordovaActivity加载的时候却不会出现这个问题。(这个问题也因为我能力不够,而且也没有深切理解源码而无法解决,但是我会努力去跟进的!!)
好!反正我的解决方法是在FirstActivity里接收回调信息:
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
- switch (resultCode) {
- case Activity.RESULT_OK:
- Bundle b=intent.getExtras();
- String str=b.getString("flags");
- cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");
- break;
- default:
- break;
- }
- }
InputData.htmlpublic void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); switch (resultCode) { case Activity.RESULT_OK: Bundle b=intent.getExtras(); String str=b.getString("flags"); cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')"); break; default: break; } }
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="format-detection" content="telephone=no" />
- <meta name="msapplication-tap-highlight" content="no" />
- <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->
- <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
- <title>InputData</title>
- <script type="text/javascript" src="cordova.js"></script>
- <script type="text/javascript" charset="utf-8">
- document.addEventListener("deviceready", onDeviceReady, true);
- function onDeviceReady(){
- //获取输入框和按钮对象
- var text1 = document.getElementById("name");
- var text2 = document.getElementById("Number");
- var text3 = document.getElementById("Age");
- var btn =document.getElementById("Enter");
- var output = document.getElementById("output");
- //给对象加上监听
- btn.addEventListener('click', onClick);
- function onClick(){
- //获取输入框值
- var Name =text1.value;
- var Number =text2.value;
- var Age =text3.value;
- var success = function(message) {
- alert("Success" + message);
- output.innerHTML = message;
- };
- var error = function(message) { alert("Oopsie! " + message); };
- //用插件调用值
- //dataTransportPlugins.createEvent(Name, Number, Age, success, error);
- navigator.dataTransportJs.demo(Name, Number, Age, success, error);
- }
- }
- </script>
- <script type="text/javascript" charset="utf-8">
- function javaCallBackJs(str){
- var output = document.getElementById("output2");
- output.innerHTML = str;
- }
- function javaCallJs(){
- var output = document.getElementById("output1");
- output.innerHTML = "this is from Button of FirstActivity"
- }
- </script>
- </head>
- <body>
- <p>
- <label for="name"> Name: </label>
- <input type="text" name="input" id="name" value="" />
- </p>
- <p>
- <label for="name"> Number: </label>
- <input type="text" name="input" id="Number" value="" />
- </p>
- <p>
- <label for="name"> Age: </label>
- <input type="text" name="input" id="Age" value="" />
- </p>
- <button id="Enter">Enter</button>
- <p>
- Button Message : <div id="output1" style="color:red;"></div>
- </p>
- <p>
- SeconeActivity Message : <div id="output2" style="color:red;"></div>
- </p>
- </body>
- </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="format-detection" content="telephone=no" /> <meta name="msapplication-tap-highlight" content="no" /> <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 --> <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" /> <title>InputData</title> <script type="text/javascript" src="cordova.js"></script> <script type="text/javascript" charset="utf-8"> document.addEventListener("deviceready", onDeviceReady, true); function onDeviceReady(){ //获取输入框和按钮对象 var text1 = document.getElementById("name"); var text2 = document.getElementById("Number"); var text3 = document.getElementById("Age"); var btn =document.getElementById("Enter"); var output = document.getElementById("output"); //给对象加上监听 btn.addEventListener('click', onClick); function onClick(){ //获取输入框值 var Name =text1.value; var Number =text2.value; var Age =text3.value; var success = function(message) { alert("Success" + message); output.innerHTML = message; }; var error = function(message) { alert("Oopsie! " + message); }; //用插件调用值 //dataTransportPlugins.createEvent(Name, Number, Age, success, error); navigator.dataTransportJs.demo(Name, Number, Age, success, error); } } </script> <script type="text/javascript" charset="utf-8"> function javaCallBackJs(str){ var output = document.getElementById("output2"); output.innerHTML = str; } function javaCallJs(){ var output = document.getElementById("output1"); output.innerHTML = "this is from Button of FirstActivity" } </script> </head> <body> <p> <label for="name"> Name: </label> <input type="text" name="input" id="name" value="" /> </p> <p> <label for="name"> Number: </label> <input type="text" name="input" id="Number" value="" /> </p> <p> <label for="name"> Age: </label> <input type="text" name="input" id="Age" value="" /> </p> <button id="Enter">Enter</button> <p> Button Message : <div id="output1" style="color:red;"></div> </p> <p> SeconeActivity Message : <div id="output2" style="color:red;"></div> </p> </body> </html>
下面两个JS方法分别是secondActivity回传时候调用的和按确认Button时候调用的方法:
- <script type="text/javascript" charset="utf-8">
- function javaCallBackJs(str){
- var output = document.getElementById("output2");
- output.innerHTML = str;
- }
- function javaCallJs(){
- var output = document.getElementById("output1");
- output.innerHTML = "this is from Button of FirstActivity"
- }
- </script>
<script type="text/javascript" charset="utf-8">
function javaCallBackJs(str){
var output = document.getElementById("output2");
output.innerHTML = str;
}
function javaCallJs(){
var output = document.getElementById("output1");
output.innerHTML = "this is from Button of FirstActivity"
}
</script>
本例用到的插件类DataTransportPlugin 和 secondActivity代码跟上一篇文章是一样的,这里就不写出来了
最后就看看怎么规范的注册插件,在上一篇文章是通过暴露js接口来引入插件:
- <script type="text/javascript" src="js/dataTransportJs.js"></script>
<script type="text/javascript" src="js/dataTransportJs.js"></script>
现在的方法是通过在assets/www/cordova_plugins.js引入定义的js文件
- cordova.define('cordova/plugin_list', function(require, exports, module) {
- module.exports = [
- {
- "file": "plugins/cordova-plugin-whitelist/whitelist.js",
- "id": "cordova-plugin-whitelist.whitelist",
- "runs": true
- },
- {
- "file": "plugins/cordova-plugin-whitelist/dataTransportJs.js",
- "id": "org.apache.cordova.dataTransportJs",
- "merges": [
- "navigator.dataTransportJs"
- ]
- },
- ];
- module.exports.metadata =
- // TOP OF METADATA
- {
- "cordova-plugin-whitelist": "1.0.0"
- }
- // BOTTOM OF METADATA
- });
cordova.define('cordova/plugin_list', function(require, exports, module) { module.exports = [ { "file": "plugins/cordova-plugin-whitelist/whitelist.js", "id": "cordova-plugin-whitelist.whitelist", "runs": true }, { "file": "plugins/cordova-plugin-whitelist/dataTransportJs.js", "id": "org.apache.cordova.dataTransportJs", "merges": [ "navigator.dataTransportJs" ] }, ]; module.exports.metadata = // TOP OF METADATA { "cordova-plugin-whitelist": "1.0.0" } // BOTTOM OF METADATA });
module.exports中第二个实体就是定义的js,其中:
file:是js插件接口的路径,统一都放在plugins文件夹下
id:是定义的一个id
merges:是在html中调用的插件(可以看html中调用的的语句)
dataTransportJs.js代码其实跟上一篇文章中的差不多,只是多了一些定义,如果你会node.js就很容易能理解。
dataTransportJs.js实现代码:
- //定义一个模块
- cordova.define("org.apache.cordova.dataTransportJs", function(require, exports, module) {
- //加载模块
- var exec = require('cordova/exec');
- module.exports = {
- demo: function(Name, Number, Age, successCallback, errorCallback) {
- exec(
- successCallback,
- errorCallback,
- 'DataTransportPlugin',
- 'dataTransport',
- [{
- "Name": Name,
- "Number": Number,
- "Age": Age,
- }]
- );
- },
- };
- });
最后还是需要在config.xml中定义://定义一个模块 cordova.define("org.apache.cordova.dataTransportJs", function(require, exports, module) { //加载模块 var exec = require('cordova/exec'); module.exports = { demo: function(Name, Number, Age, successCallback, errorCallback) { exec( successCallback, errorCallback, 'DataTransportPlugin', 'dataTransport', [{ "Name": Name, "Number": Number, "Age": Age, }] ); }, }; });
- <feature name="DataTransportPlugin">
- <param name="android-package" value="ivy.cordova.example.plugins.DataTransportPlugin" />
- </feature>
<feature name="DataTransportPlugin"> <param name="android-package" value="ivy.cordova.example.plugins.DataTransportPlugin" /> </feature>
看到这里你可能会有个疑问,既然只是用一个SystemWebView,那为什么要用cordova,直接在Activity里面用WebView就好了。是的!我也这么认为!所以到现在我都还没有体会到cordova的好处= =。等项目做完后应该就有结论了!
参考博客:
http://blog.csdn.net/aaawqqq/article/details/20480615
代码下载:
http://download.csdn.net/detail/tangjiarao/9095385