您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Cordova Android Native混合开发值传递
 
作者:tangjiarao 来自于:CSDN 发布于 2015-11-3
  5420  次浏览      18
 

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

上一篇文章用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的好处= =。等项目做完后应该就有结论了!

   
5420 次浏览       18
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   

Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合

领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...