Cordova(PhoneGap)Android Native混合开发值传递

来源:互联网 发布:路由器怎么没有网络啊 编辑:程序博客网 时间:2024/06/05 14:08

本人只是菜鸡一枚,写博客只是为了做一下笔记,年纪大了。如果有说得不准确或者不好的地方请指出!!虚心请教大神指点。如果你觉得本文章太基础没什么意义,也给点掌声鼓励鼓励。

上一篇文章用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 的部分源码:

[java] view plain copy
print?
  1. public class CordovaActivity extends Activity {  
  2. ......  
  3.   
  4.  protected CordovaWebView appView;  
  5.   
  6. ......  
  7.   
  8.   
  9.       
  10. /** 
  11.  * Load the url into the webview. 
  12.  */  
  13. public void loadUrl(String url) {  
  14.     if (appView == null) {  
  15.         init();  
  16.     }  
  17.   
  18.     // If keepRunning  
  19.     this.keepRunning = preferences.getBoolean("KeepRunning"true);  
  20.   
  21.     appView.loadUrlIntoView(url, true);  
  22. }  

CordovaWebView是一个接口,它不是一个View,它需要一个像webView一样的容器给它去显示html页面,所以需要通过一个显示容器去实例化它。其中的appView.getView();方法就是用来显示html的真实的View。以下是CordovaActivity 的部分源码:

[java] view plain copy
print?
  1. protected void init() {  
  2.         appView = makeWebView();  
  3.         createViews();  
  4.         if (!appView.isInitialized()) {  
  5.             appView.init(cordovaInterface, pluginEntries, preferences);  
  6.         }  
  7.         cordovaInterface.onCordovaInit(appView.getPluginManager());  
  8.   
  9.         // Wire the hardware volume controls to control media if desired.  
  10.         String volumePref = preferences.getString("DefaultVolumeStream""");  
  11.         if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {  
  12.             setVolumeControlStream(AudioManager.STREAM_MUSIC);  
  13.         }  
  14.     }  
  15.   
  16. ......  
  17.   
  18. protected void createViews() {  
  19.         //Why are we setting a constant as the ID? This should be investigated  
  20.         appView.getView().setId(100);  
  21.         appView.getView().setLayoutParams(new FrameLayout.LayoutParams(  
  22.                 ViewGroup.LayoutParams.MATCH_PARENT,  
  23.                 ViewGroup.LayoutParams.MATCH_PARENT));  
  24.   
  25.         setContentView(appView.getView());  
  26.   
  27.         if (preferences.contains("BackgroundColor")) {  
  28.             int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);  
  29.             // Background of activity:  
  30.             appView.getView().setBackgroundColor(backgroundColor);  
  31.         }  
  32.   
  33.         appView.getView().requestFocusFromTouch();  
  34.     }  
  35.   
  36.     /** 
  37.      * Construct the default web view object. 
  38.      * 
  39.      * Override this to customize the webview that is used. 
  40.      */  
  41.     protected CordovaWebView makeWebView() {  
  42.         return new CordovaWebViewImpl(makeWebViewEngine());  
  43.     }  
  44.   
  45.     protected CordovaWebViewEngine makeWebViewEngine() {  
  46.         return CordovaWebViewImpl.createEngine(this, preferences);  
  47.     }  
  48.   
  49.     protected CordovaInterfaceImpl makeCordovaInterface() {  
  50.         return new CordovaInterfaceImpl(this) {  
  51.             @Override  
  52.             public Object onMessage(String id, Object data) {  
  53.                 // Plumb this to CordovaActivity.onMessage for backwards compatibility  
  54.                 return CordovaActivity.this.onMessage(id, data);  
  55.             }  
  56.         };  
  57.     }  
  58.       
  59. ......  

从以下代码看出,appView的大小已经是设定为全屏幕覆盖了,所有修改appView的大小和位置都很不方便,甚至与原生的控件结合都很难

[java] view plain copy
print?
  1. <span style="font-size:10px;">ppView.getView().setLayoutParams(new FrameLayout.LayoutParams(  
  2.                 ViewGroup.LayoutParams.MATCH_PARENT,  
  3.                 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布局文件:

[html] view plain copy
print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical" android:layout_width="match_parent"  
  4.     android:layout_height="match_parent">  
  5.   
  6.     <Button  
  7.         android:id="@+id/bt1"  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="确定"/>  
  11.     <org.apache.cordova.engine.SystemWebView  
  12.         android:id="@+id/cordovaView"  
  13.         android:layout_width="match_parent"  
  14.         android:layout_height="match_parent"  
  15.         />  
  16.         
  17. </LinearLayout>  

再来看看FirstActivity的实现:

[java] view plain copy
print?
  1. public class FirstActivity extends Activity {  
  2.     /** 
  3.      * 一个加载网页的容器,继承WebView 
  4.      */  
  5.     private SystemWebView systemWebView;  
  6.   
  7.     private CordovaWebView cordovaWebView;  
  8.   
  9.   
  10.     private ConfigXmlParser parser;  
  11.   
  12.     private MyCordovaInterfaceImpl myCordovaInterface;  
  13.   
  14.     private Button b;  
  15.   
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.second);  
  19.   
  20.         b =(Button)findViewById(R.id.bt1);  
  21.   
  22.         systemWebView =(SystemWebView)findViewById(R.id.cordovaView);  
  23.   
  24.         //为初始化CordovaWebView提供参数  
  25.         parser=new ConfigXmlParser();  
  26.   
  27.         //这里会解析res/xml/config.xml配置文件  
  28.         parser.parse(this);  
  29.   
  30.         //实例化CordovaWebView  
  31.         cordovaWebView= new CordovaWebViewImpl(new SystemWebViewEngine(systemWebView));  
  32.   
  33.         //为初始化CordovaWebView提供参数,传入本Activity  
  34.         myCordovaInterface =new MyCordovaInterfaceImpl(this);  
  35.   
  36.         //初始化CordovaWebView  
  37.         cordovaWebView.init(myCordovaInterface, parser.getPluginEntries(), parser.getPreferences());  
  38.   
  39.         //执行加载动作  
  40.         cordovaWebView.loadUrl("file:///android_asset/www/InputData.html");  
  41.   
  42.   
  43.         b.setOnClickListener(new View.OnClickListener() {  
  44.   
  45.             public void onClick(View v) {  
  46.   
  47.                 //调用html的JS方法  
  48.                 cordovaWebView.loadUrl("javascript:javaCallJs()");  
  49.             }  
  50.         });  
  51.     }  
  52.   
  53.     /** 
  54.      *  为初始化CordovaWebView提供参数 
  55.      */  
  56.     private static class MyCordovaInterfaceImpl extends CordovaInterfaceImpl {  
  57.   
  58.         private Activity mActivity;  
  59.   
  60.         public MyCordovaInterfaceImpl(Activity activity) {  
  61.   
  62.             super(activity);  
  63.   
  64.             mActivity = activity;  
  65.         }  
  66.         public Activity getActivity() {  
  67.   
  68.             return mActivity;  
  69.         }  
  70.   
  71.      }  
  72.   
  73.     /** 
  74.      * 接收从SecondActivity传来的数据,并且调用htmlJS方法来显示 
  75.      * @param requestCode 
  76.      * @param resultCode 
  77.      * @param intent 
  78.      */  
  79.     public void onActivityResult(int requestCode, int resultCode, Intent intent) {  
  80.         super.onActivityResult(requestCode, resultCode, intent);  
  81.   
  82.         switch (resultCode) {  
  83.   
  84.             case Activity.RESULT_OK:  
  85.   
  86.                 Bundle b=intent.getExtras();  
  87.                 String str=b.getString("flags");  
  88.                 cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");  
  89.   
  90.                 break;  
  91.             default:  
  92.                 break;  
  93.         }  
  94.   
  95.     }  
  96.   
  97. }  

有没有觉得实例化CordovaWebView的方式很眼熟,其实就是把CordovaActivity实例化appView的方法抽离出来,再给它一个可以自定义修改位置的SystemWebView,那你就可以自由地放置它的位置


以下代码是主动调用html里面的JS方法,通过loadUrl(JavaScript:+jscode);方式,这个jscode可以是一个方法也可以是一串js代码。

[java] view plain copy
print?
  1. b.setOnClickListener(new View.OnClickListener() {  
  2.   
  3.             public void onClick(View v) {  
  4.   
  5.                 //调用html的JS方法  
  6.                 cordovaWebView.loadUrl("javascript:javaCallJs()");  
  7.             }  
  8.         });  

onActivityResult()是用来接收从SecondActivity回传的信息,在上一篇文章里面,我们把它写在插件里面来接收回调,那现在为什么要把onActivityResult()方法写在FirstActivity呢?对于这个问题我也很烦恼,我试了很多次,查找了网上很多资料,看了源码又改过源码,但是SecondActivity回传的信息却一直返回到FirstActivity中,所以我唯有在FirstActivity中接收这个回调Intent,然后主动调用JS方法来达到修改html的效果。但是如果用cordovaActivity加载的时候却不会出现这个问题。(这个问题也因为我能力不够,而且也没有深切理解源码而无法解决,但是我会努力去跟进的!!)

好!反正我的解决方法是在FirstActivity里接收回调信息:
[java] view plain copy
print?
  1. public void onActivityResult(int requestCode, int resultCode, Intent intent) {  
  2.         super.onActivityResult(requestCode, resultCode, intent);  
  3.   
  4.         switch (resultCode) {  
  5.   
  6.             case Activity.RESULT_OK:  
  7.   
  8.                 Bundle b=intent.getExtras();  
  9.                 String str=b.getString("flags");  
  10.                 cordovaWebView.loadUrl("javascript:javaCallBackJs('"+str+"')");  
  11.   
  12.                 break;  
  13.             default:  
  14.                 break;  
  15.         }  
  16.   
  17.     }  
InputData.html
[html] view plain copy
print?
  1. <!DOCTYPE html>  
  2. <html>  
  3. <head>  
  4.     <meta charset="utf-8" />  
  5.     <meta name="format-detection" content="telephone=no" />  
  6.     <meta name="msapplication-tap-highlight" content="no" />  
  7.     <!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes. See https://issues.apache.org/jira/browse/CB-4323 -->  
  8.     <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" />  
  9.   
  10.     <title>InputData</title>  
  11.   
  12.     <script type="text/javascript" src="cordova.js"></script>  
  13.     <script type="text/javascript" charset="utf-8">  
  14.   
  15.     document.addEventListener("deviceready", onDeviceReady, true);  
  16.   
  17.     function onDeviceReady(){  
  18.     //获取输入框和按钮对象  
  19.         var text1 = document.getElementById("name");  
  20.         var text2 = document.getElementById("Number");  
  21.         var text3 = document.getElementById("Age");  
  22.         var btn =document.getElementById("Enter");  
  23.         var output = document.getElementById("output");  
  24.         //给对象加上监听  
  25.         btn.addEventListener('click', onClick);  
  26.   
  27.         function onClick(){  
  28.         //获取输入框值  
  29.             var Name =text1.value;  
  30.             var Number =text2.value;  
  31.             var Age =text3.value;  
  32.             var success = function(message) {  
  33.                 alert("Success" + message);  
  34.                 output.innerHTML = message;  
  35.             };  
  36.             var error = function(message) { alert("Oopsie! " + message); };  
  37.   
  38.             //用插件调用值  
  39.             //dataTransportPlugins.createEvent(Name, Number, Age, success, error);  
  40.              navigator.dataTransportJs.demo(Name, Number, Age, success, error);  
  41.         }  
  42.     }  
  43.     </script>  
  44.     <script type="text/javascript" charset="utf-8">  
  45.         function javaCallBackJs(str){  
  46.   
  47.             var output = document.getElementById("output2");  
  48.             output.innerHTML = str;  
  49.         }  
  50.         function javaCallJs(){  
  51.   
  52.             var output = document.getElementById("output1");  
  53.             output.innerHTML = "this is from Button of FirstActivity"  
  54.         }  
  55.     </script>  
  56. </head>  
  57.   
  58.   
  59. <body>  
  60.   
  61. <p>  
  62.     <label for="name"> Name: </label>  
  63.     <input type="text" name="input" id="name" value=""  />  
  64. </p>  
  65. <p>  
  66.     <label for="name"> Number: </label>  
  67.     <input type="text" name="input" id="Number" value="" />  
  68. </p>  
  69. <p>  
  70.     <label for="name"> Age: </label>  
  71.     <input type="text" name="input" id="Age" value="" />  
  72. </p>  
  73.   
  74. <button id="Enter">Enter</button>  
  75. <p>  
  76.     Button Message : <div id="output1" style="color:red;"></div>  
  77. </p>  
  78. <p>  
  79.     SeconeActivity Message : <div id="output2" style="color:red;"></div>  
  80. </p>  
  81. </body>  
  82. </html>  


下面两个JS方法分别是secondActivity回传时候调用的和按确认Button时候调用的方法:

[html] view plain copy
print?
  1. <script type="text/javascript" charset="utf-8">  
  2.         function javaCallBackJs(str){  
  3.   
  4.             var output = document.getElementById("output2");  
  5.             output.innerHTML = str;  
  6.         }  
  7.         function javaCallJs(){  
  8.   
  9.             var output = document.getElementById("output1");  
  10.             output.innerHTML = "this is from Button of FirstActivity"  
  11.         }  
  12.     </script>  

本例用到的插件类DataTransportPlugin 和 secondActivity代码跟上一篇文章是一样的,这里就不写出来了


最后就看看怎么规范的注册插件,在上一篇文章是通过暴露js接口来引入插件:

[html] view plain copy
print?
  1. <script type="text/javascript" src="js/dataTransportJs.js"></script>  
现在的方法是通过在assets/www/cordova_plugins.js引入定义的js文件

[javascript] view plain copy
print?
  1. cordova.define('cordova/plugin_list'function(require, exports, module) {  
  2. module.exports = [  
  3.     {  
  4.         "file""plugins/cordova-plugin-whitelist/whitelist.js",  
  5.         "id""cordova-plugin-whitelist.whitelist",  
  6.         "runs"true  
  7.     },  
  8.     {  
  9.         "file""plugins/cordova-plugin-whitelist/dataTransportJs.js",  
  10.         "id""org.apache.cordova.dataTransportJs",  
  11.         "merges": [  
  12.                 "navigator.dataTransportJs"  
  13.         ]  
  14.     },  
  15. ];  
  16. module.exports.metadata =   
  17. // TOP OF METADATA  
  18. {  
  19.     "cordova-plugin-whitelist""1.0.0"  
  20. }  
  21. // BOTTOM OF METADATA  
  22. });  
module.exports中第二个实体就是定义的js,其中:
file:是js插件接口的路径,统一都放在plugins文件夹下
id:是定义的一个id
merges:是在html中调用的插件(可以看html中调用的的语句)
dataTransportJs.js代码其实跟上一篇文章中的差不多,只是多了一些定义,如果你会node.js就很容易能理解。
dataTransportJs.js实现代码:
[javascript] view plain copy
print?
  1. //定义一个模块  
  2. cordova.define("org.apache.cordova.dataTransportJs"function(require, exports, module) {  
  3.   
  4. //加载模块  
  5. var exec = require('cordova/exec');    
  6.   
  7.   
  8. module.exports = {    
  9.   
  10.     demo: function(Name, Number, Age, successCallback, errorCallback) {  
  11.         exec(  
  12.                     successCallback,  
  13.                     errorCallback,  
  14.                     'DataTransportPlugin',  
  15.                     'dataTransport',  
  16.                     [{  
  17.                         "Name": Name,  
  18.                         "Number": Number,  
  19.                         "Age": Age,  
  20.                     }]  
  21.                 );  
  22.     },    
  23. };    
  24. });    
最后还是需要在config.xml中定义:
[html] view plain copy
print?
  1. <feature name="DataTransportPlugin">  
  2.         <param name="android-package" value="ivy.cordova.example.plugins.DataTransportPlugin" />  
  3.     </feature>  

看到这里你可能会有个疑问,既然只是用一个SystemWebView,那为什么要用cordova,直接在Activity里面用WebView就好了。是的!我也这么认为!所以到现在我都还没有体会到cordova的好处= =。等项目做完后应该就有结论了!
参考博客:
http://blog.csdn.net/aaawqqq/article/details/20480615
代码下载:
http://download.csdn.net/detail/tangjiarao/9095385

原创粉丝点击