对于一个ionic项目,在主目录下通过以下命令行增加android平台。
cordova platform add android
然后在platforms目录下就会出现一个android文件夹:
之后可以使用配置了android开发环境的eclipse选中上图android目录打开该项目;也可以使用Android Studio打开上图android目录,这时候需要要配置gradle,直接选择确定下载即可(此时建议开启全局代理),注意项目的路径中不能含有非ascii码字符(例如中文),否则gradle会build失败。
本例使用Android Studio来演示,eclipse环境下只要找到对应的文件即可。
打开项目后,项目目录如下:
该项目有两个模块,第一个是CordovaLib,里面是cordova库的代码,我们就不用管了(如果是用eclipse打开这会显示为一个单独的项目)
第二个模块便是我们自己的项目了,就如普通的android项目目录一样。我们在项目主目录WWW目录下的代码都被原封不动的拷贝到assets/www目录下,又额外添加了一些cordova js库,这些文件我们也不用管。
如下图,新建一个org.apache.cordova.hello包,改包名可随意取。然后在改包下新建一个HelloPlugin.java文件:
HelloPlugin的代码如下:
package org.apache.cordova.hello;
import android.widget.Toast;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
/**
* Created by duocai on 2017/4/10.
*/
//所有的自定义plugin都需要继承CordovaPlugin这个类
public class HelloPlugin extends CordovaPlugin {
//然后需要复写这个execute方法
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
throws JSONException {
if (action.equals("hello")) {
String text = "";
for (int i = 0; i < args.length(); i++) {
text += args.getString(i) + "\n"; //这取决于js代码传进来的参数类型。
}
Toast.makeText(this.cordova.getActivity(), text, Toast.LENGTH_LONG).show();
callbackContext.success("success: " + text);//使用回掉的方法,返回信息
//callbackContext.error("dd"); //对应有返回错误的方法
return true;
}
return false;
}
}
execute有三个参数,这个对应js的调用代码会更好理解(详见2.4部分):
cordova.exec(callbackContext.success, callbackContext.error, PluginName, action, args);
其中action就对应action;args在js端就是普通的js数组;callbackContext.success, callbackContext.error分别是成功和失败时的两个回掉函数,前面java代码中:
callbackContext.success("success: " + text);//使用回掉的方法,返回信息
这一行就调用了第一个回掉函数。
PluginName的意义就是指定了当前的java文件(详见2.3本分)
2.1的目录结构中,我们可以看到res/xml目录下有一个config.xml配置文件,打开文件增加配置如下:
<widget id="com.ionicframework.starter" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HelloCordovaname>
<description>
An Ionic Framework and Cordova project.
description>
<author email="[email protected]" href="http://example.com.com/">
Your Name Here
author>
<content src="index.html" />
<access origin="*" />
<preference name="loglevel" value="DEBUG" />
<preference name="webviewbounce" value="false" />
<preference name="UIWebViewBounce" value="false" />
<preference name="DisallowOverscroll" value="true" />
<preference name="SplashScreenDelay" value="2000" />
<preference name="FadeSplashScreenDuration" value="2000" />
<preference name="android-minSdkVersion" value="16" />
<preference name="BackupWebStorage" value="none" />
<feature name="HelloPlugin">
<param name="android-package" value="org.apache.cordova.hello.HelloPlugin" />
feature>
widget>
/**
* Created by duocai on 2016/9/6.
*/
angular.module('ctrl.camera', [])
.controller('CameraCtrl', function($scope) {
var success = function(data) {
alert(data);
}
//参数与前面的java代码对应如下
//js: cordova.exec(callbackContext.success, callbackContext.error, PluginName, action, args);
//java: public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
// 此外 js 多余的 PluginName 在2.3的配置文件中配置
// // PluginName
//
//
cordova.exec(success, null, "HelloPlugin", "hello", ["hello", "world", "!!!"]);
});
其余没有注释的部分是angular controller的语法,就不再赘述。
至此就利用cordova的库完成了通过js代码调用原生java代码。
其中下面小黑框是java代码的输出,上面大黑框是js代码的输出。
我们都知道使用在开发ionic项目时,是不需要上面那样在android studio中使用插件的。这是cordova 的插件进行了进一步的封装,知道了上面的过程后,我们就知道ionic的插件格式要求到底干了什么事。
在开发之前需要做一点准备,每次执行cordova build android 的时候,都会复写platforms/android目录下文件。所以我们将刚刚的android项目目录改名为android plugin development。然后在执行:
cordova platform add android
来增加 android 平台
按上图所示建一个插件目录。src和www无所谓怎么建,但这样建条理更清晰,plugin.xml一定要在其所在的位置。
HelloPlugin.java就是上面的文件直接拷贝过来即可
plugin.xml:
<plugin id="org.cordova.HelloPlugin" version="0.0.1"
xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android">
<name>HelloPluginname>
<description>Descriptiondescription>
<js-module name="HelloPlugin" src="www/HelloPlugin.js">
<clobbers target="HelloPlugin"/>
js-module>
<platform name="android">
<config-file parent="/*" target="res/xml/config.xml">
<feature name="HelloPlugin">
<param name="android-package" value="org.apache.cordova.hello.HelloPlugin" />
feature>
config-file>
<source-file src="src/android/HelloPlugin.java" target-dir="src/org/apache/cordova/hello"/>
platform>
plugin>
HelloPlugin.js
/**
* Created by admin on 2017/4/10.
*/
var exec = require('cordova/exec');
// 符合格式要求可在其它js文件中调用该模块
module.exports = {
show: function (message) {
//基本上等同于前面第2部分所说的cordova.exec()
exec(null,null, "HelloPlugin", "hello", [message]);
}
};
调用插件,依然以前面的CameraCtrl为例
/**
* Created by duocai on 2016/4/10.
*/
angular.module('ctrl.camera', [])
.controller('CameraCtrl', function($scope) {
HelloPlugin.show("hello world !!!") // HelloPlugin在前面的plugin.xml里配置
});
angular升级到angular2,ionic也随之升级。那么针对于ionic2该如何使用自定义插件呢?这里只说使用是因为cordova并没有升级,所以它的工作原理和插件的编写并不会有所改变,只是使用方式要发生变化。那么到底如何使用呢。
下面会以ionic2 tabs样式的初始项目做一个讲解。
当我们使用android studio如上所示打开项目时,目录结构依然不变,不过我们自己写的代码都被编译到assets/www/build/main.js 这个文件中,这个文件很长也很难阅读,我们可以通过搜索直接定位要修改的位置(搜索方法名,类名之类的),例如我搜索ionic2 tabs样式初始项目中ContactPage这个类定位到如下位置:
var ContactPage = (function () {
function ContactPage(navCtrl) {
this.navCtrl = navCtrl;
}
return ContactPage;
}());
接下来在构造函数(构造函数会在页面初始加载时被调用)中添加前面2.4所说的直接调用(前提是要像第2部分所说的配置好)
var ContactPage = (function () {
function ContactPage(navCtrl) {
this.navCtrl = navCtrl;
var success = function(data) {
alert(data);
}
cordova.exec(success, null, "HelloPlugin", "hello", ["hello", "world", "!!!"]);
}
return ContactPage;
}());
之后,编译运行,效果如下图所示,与前面所展示无异。但是现在这个js文件不像是ionic1中的直接拷贝而是编译形成的,难以阅读,也不可能被再复用,所以不推荐使用这种方式。仅供大家了解一下插件进一步是怎么工作的。
封装插件,前面第三部分说的非常清楚,封装方式与前面无异。额外补充一点就是前面好像忘了说明这个插件是怎么用的。使用方式很简单,例如将前面HelloPlugin目录放在当前项目的目录下,然后使用命令行:
ionic plugin add HelloPlugin
命令格式是ionic plugin add 插件目录路径,刚刚这个例子使用相对路径。
添加好插件后,接下来说明如何在ionic2中使用,前面已经说了使用方式有很多种,接下来说明一种推荐的,以ionic2 tabs样式初始项目src/pages/contact/contact.ts这个组件为例,它原始如下所示:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
@Component({
selector: 'page-contact',
templateUrl: 'contact.html'
})
export class ContactPage {
constructor(public navCtrl: NavController) {
}
}
然后为了使用HelloPlugin插件,应当做如下修改:
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
// 利用declare语法声明HelloPlugin对象,不需要初始华。这个语法有点像C++里的声明语法。
declare let HelloPlugin;
@Component({
selector: 'page-contact',
templateUrl: 'contact.html'
})
export class ContactPage {
constructor(public navCtrl: NavController) {
// 这里使用就像前面第三部分所说的一样,但是为了在Typescript中使用HelloPlugin这个全局变量,需要前面的声明,不然typescript编译时会报错,因为找不到HelloPlugin
HelloPlugin.show("hello world !!!")
}
}
然后说面一下HelloPlugin这个变量名字是从哪里来的。看前面3.2.2 plugin.xml配置文件中的如下部分:
<--就是在这里配置的-->
<js-module name="HelloPlugin" src="www/HelloPlugin.js">
<clobbers target="HelloPlugin"/>
js-module>
这种使用插件的方式非常简单,只需在文件的开始部分使用
declare let 对象名
来声明这个对象就可以在typescript中直接使用,你完全可以把他当成是一种import,来引用你在插件中定义的全局对象。所以这是一种推荐的使用方式。
最后效果如下:
我们都知道一个html文件如果引用了多个js文件,那么全局变量在这些文件中是可以直接相互引用的。刚刚的declare所做的事情就是:尽管我没有初始化这个对象,但是我会在其它某个文件中,声明并初始化这个全局对象,所以接下来文件中typescript要绕过对这个对象的检查。
我们看一下编译后main.js文件(前面提到过):
var ContactPage = (function () {
function ContactPage(navCtrl) {
this.navCtrl = navCtrl;
// Test Hello plugin
HelloPlugin.show("hello world !!!");
}
return ContactPage;
}());
然后在整个js文件中,再也找不到第二个HelloPlugin。所以前面的declare let HelloPlugin在typescript中实现就是告知Typescript忽略接下来对Helloplugin的语法检查,将其原封保留即可。
等到编译成js文件,就完全和ionic1是一样的环境了。