本人只是菜鸡一枚,写博客只是为了做一下笔记,年纪大了。如果有说得不准确或者不好的地方请指出!!虚心请教大神指点。如果你觉得本文章太基础没什么意义,也给点掌声鼓励鼓励。
上一篇文章用cordova的plugin使得html可以调用Activity并且获取Activity回调的数据,前文连接:但是上一篇文章出现以下一些问题:
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); } |
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); } }; } ...... |
从以下代码看出,appView的大小已经是设定为全屏幕覆盖了,所有修改appView的大小和位置都很不方便,甚至与原生的控件结合都很难
<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> |
再来看看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; } } } |
有没有觉得实例化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()"); } }); |
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.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> |
最后就看看怎么规范的注册插件,在上一篇文章是通过暴露js接口来引入插件:
<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 }); |
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中定义:
<feature name="DataTransportPlugin"> <param name="android-package" value="ivy.cordova.example.plugins.DataTransportPlugin" /> </feature> |
看到这里你可能会有个疑问,既然只是用一个SystemWebView,那为什么要用cordova,直接在Activity里面用WebView就好了。是的!我也这么认为!所以到现在我都还没有体会到cordova的好处=
=。等项目做完后应该就有结论了!
|