Ionic实战项目-跨平台App开发


这里写图片描述
功能技术分析
- 读取朋友圈消息状态
- 朋友列表可以列出朋友信息
- 用户中心请求API,实现用户登录。用户信息的读取功能
用户中心模块
- 用户登录状态判断
- 用户登录信息存储
- Ionic App 中处理API请求
APP周边资源
- 启动界面的快速生成技巧
- APP图表
- IOS平台打包发布
- IOS平台打包发布
- 极光推送模块的集成
Ionic的发音:
这里写图片描述

Ionic的发展
Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。
这里写图片描述
这里写图片描述
这里写图片描述
相关资料:1.Win通用平台概念;2.Ionic官方声明;3.对黑莓APP的支持情况
这里写图片描述
相关资料:Ionic官网 http://ionicframework.com,Ionic IO:http://ionic.io/, Ionic GitHub,Ionic 中国,Ionic Icons(图标) ngCordova插件

  1. 查文档,版本差异
  2. 插件示例
  3. 官方开发环境:Node.js + Atom + Xcode(IOS,Mac) + (Android Studio) + Genymotion(安卓模拟器)
  4. http://ionicframework.com/docs/components/#toast

这里写图片描述
安装ionic

npm i -g ionic@beta 2.0版本
npm i -g ionic 1.0版本
npm install ionic@2.0.0-beta.22
cnpm install -g ionic@beta (淘宝)
node安装文件夹里面有个文件npmrc,添加配置 registry = http://registry.cnpmjs.org(设置全局的npm从国内源加载)

这里写图片描述

ionic start ionicdemo --v2

问题:Please install your Cordova CLI to version >=4.2.0 npm install -g cordova
升级
npm install -g cordova@6.0.0

D:\HtmlBeginner\ionic>ionic info

Your system information:

Cordova CLI: 6.0.0
Ionic CLI Version: 2.2.3
Ionic App Lib Version: 2.2.1
ios-deploy version: Not installed
ios-sim version: Not installed
OS: Windows 8.1
Node Version: v4.4.3
Xcode version: Not installed

模板资料:
这里写图片描述
ionic命令是和1.0版本公用的,所以–v2表明使用2.0版本去初始化项目;
此过程主要安装了必要的 npm modules,并且安装依赖的Cordova组件;
小技巧:如果想使用TypeScript,只要在命令后面添加 –ts参数即可

安装成功;
这里写图片描述

ionic serve 运行项目

根据User-Agent来识别安卓,苹果各种机型

User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Mobile Safari/537.36

Ionic Lab是一个操作ionic的图形软件下载地址,不过最好不要用,因为命令行可以装逼(其实是因为命令行操作更快)
这里写图片描述
小技巧:在创建ionic新项目之前最好安装cordova组件,因为可以生成platforms,iso和Android的配置页面;由于疏忽,本人就忘记安装了,so,可以在通过命令

ionic platform add android  
ionic platform add ios

来部署项目,或者

直接执行 ionic run android命令,会帮我们自动生成android项目
同理 ios项目 可直接运行 ionic run ios

苹果Xcode
这里写图片描述
Android Studio + Genymotion
把android studio和genymotion按好,建立关联,然后点击工具栏中genymotion device manager(安装genymotion插件后生成的红色屏幕小手机图标按钮),运行起来genymotion 模拟器,然后点android studio工具栏绿色箭头按钮(run)就会出来你的genymotion模拟器设备,选择上,并点击ok,选择“Install and continue”或另外一个都可以,你的项目就在genymotion中运行起来了
浏览器ur:http://localhost:8100/?ionicplatform=android或者是ios
这里写图片描述
所有的编码都是在app里面编写的;
hooks(钩子) 把事件塞进去,然后执行。自动化处理,一般用于比较大的项目中
node_modules 所有的依赖,不说了
platforms安卓和ISO的一些项目文件
plugins里面的也是插件(设备,控制器,白名单,键盘)
resources 安卓和ISO图标
www 网站结构(编译后的项目)
gulp 自动化,监控
config.json 自定义一些固定配置供项目读取
ionic.progect 配合gulp使用
package.json (npm i) ->node_moduleas

生命周期钩子
不同时期暴露不同函数来执行
lonic32之前

// 页面被加载完成后调用的函数,切换页面时并不会进行重新加载,因为有cache的存在 
onPageLoaded() {
console.log('page 1: page loaded.');
}

// 页面即将进入的时候
onPageWillEnter() {
// 在这里可以做页面初始化的一些事情
console.log('page 1: page will enter.');
}

// 页面已经进入的时候
onPageDidEnter() {
console.log('page 1: page did enter.');
}

// 页面即将离开的时候
onPageWillLeave() {
console.log('page 1: page will leave.');
}

// 页面已经离开的时候
onPageDidLeave() {
console.log('page 1: page did leave.');
}

// 从 DOM 中移除的时候执行的生命周期
onPageWillUnload() {

}

// 从 DOM 中移除执行完成的时候
onPageDidUnload() {

}

lonic32

事件名称 事件说明
ionViewLoaded 页面加载完毕触发。该事件发生在页面被创建成 DOM 的时候,且仅仅执行一次。如果页面被缓存(Ionic默认是缓存的)就不会再次触发该事件。该事件中可以放置初始化页面的一些事件。
ionViewWillEnter 即将进入一个页面变成当前激活页面的时候执行的事件。
ionViewDidEnter 进入了一个页面且变成了当前的激活页面,该事件不管是第一次进入还是缓存后进入都将执行。
ionViewWillLeave 将要离开了该页面之后变成了不是当前激活页面的时候执行的事件。
ionViewDidLeave 在页面完成了离开该页面并变成了不是当前激活页面的时候执行的事件。
ionViewWillUnload 在页面销毁和页面中有元素移除之前执行的事件。
ionViewDidUnload 在页面销毁和页面中有元素移除之后执行的事件。

  1. 因为启用了缓存(cache),所以ionViewDidLoad只会加载一次
  2. ionViewWillEnter可以做一些页面初始化的事情
  3. ionViewWillUnload 从 Dom中移除的时候执行的生命周期
  4. ionViewDieUnload 从Dom中移除执行完成的时候
    http://ionicframework.com/docs/api/navigation/NavController/
    这里写图片描述
export class AboutPage {

constructor(public navCtrl: NavController) {
}

ionViewDidLoad() {
console.log("页面加载完毕");
}
ionViewWillEnter(){
console.log("页面即将进入时");
}
ionViewDidEnter(){
console.log("页面进入完成");
}
ionViewWillLeave() {
console.log("页面即将离开");
}
ionViewDieLeave() {
console.log("页面离开完成");
}
}

Tab控件
1、Tab控件
图标:http://ionicframework.com/docs/v2/ionicons/

tabs.html
<ion-tabs>
<ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="home" tabBadge="4"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="About" tabIcon="information-circle"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab>
</ion-tabs>

这里写图片描述

<ion-tabs id="mainTabs" selectedIndex="2">
<ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="home" tabBadge="4" tabBadgeStyle="danger"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="About" tabIcon="information-circle"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab>
</ion-tabs>

这里写图片描述
当然我们也可以操作ts来选中
Old
这里写图片描述
这里写图片描述
lonic32
tabs.ts

import { Component } from '@angular/core';

import { AboutPage } from '../about/about';
import { ContactPage } from '../contact/contact';
import { HomePage } from '../home/home';

//依赖注入
import {Tabs} from 'ionic-angular';
import {Injectable,ViewChild} from '@angular/core';

@Component({
templateUrl: 'tabs.html'
})
export class TabsPage {
//拿到ID
@ViewChild('myTabs') tabRef: Tabs;

tab1Root = HomePage;
tab2Root = AboutPage;
tab3Root = ContactPage;

constructor() {

}

ionViewDidEnter(){
//进入完成后,动态选定一个Tab
this.tabRef.select(2);
}

}
  1. theme->variables.scss(定义有颜色)
  2. 默认选中,selectedIndex
    Button控件
    这里写图片描述
    lonic32
<ion-content padding>
<button ion-button >Login</button>
<button ion-button color="danger">DNGERLogin</button>
<button ion-button outline>outline</button>
<button ion-button clear>outline</button>
加图标
<button ion-button icon-left>
<ion-icon name="star"></ion-icon>
Left Icon
</button>

<button ion-button icon-right>
Right Icon
<ion-icon name="star"></ion-icon>
</button>
</ion-content>

这里写图片描述
文档:http://ionicframework.com/docs/api/components/button/Button/
Input控件

<ion-header>
<ion-navbar>
<ion-title>
Contact
</ion-title>
</ion-navbar>
</ion-header>

<ion-content padding>
<ion-list>
<ion-item>
<ion-label stacked>用户名</ion-label>
<ion-input type="text" value="" placeholder="用户名" [(ngModel)]="user.username"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>密码</ion-label>
<ion-input type="password" value="" [(ngModel)]="user.password"></ion-input>
</ion-item>
</ion-list>
<button ion-button block (click)=showFill()>登陆</button>
</ion-content>

文档:http://ionicframework.com/docs/api/components/input/Input/
ts 初始化问题

老式方法
constructor() {
this.user = {};
this.user.username = "";
}

export class ContactPage {
public user={
username : 'parry',
password : ''
}

constructor(public navCtrl: NavController) {
}

showFill(){
console.log(this.user.username);
}

}

Loading控件
目标:登陆加载圆圈,API取数组结束,圈圈结束
NavController依赖注入
老式写法:

import {Component} from '@angular/core';  
import {NavController, LoadingController, AlertController} from 'ionic-angular';

@Component({
templateUrl: 'build/pages/contact/contact.html'
})
export class ContactPage {
public user = {
username: 'parry',
password: ''
};

constructor(private navCtrl: NavController,
private loadingCtrl: LoadingController,
private alertCtrl: AlertController) {
this.navCtrl = navCtrl;
}

showFill() {
alert(this.user.username);
console.log(this.user.password);
}

login() {
/*// 创建 loading 窗口,模拟3秒后登录成功, loading 窗口消失
let loading = Loading.create({
content: '正在登录...',
duration: 3000, //单位是毫秒
});

this.navCtrl.present(loading);

// 真实的逻辑应该是:去请求登录的 API,返回结果后,进行loading窗口的隐藏
setTimeout(() => {
loading.dismiss();
}, 3000)
;*/

if(this.user.username == '' || this.user.username.length <= 3) {
// alert 提醒用户注意用户名的正确性
let alertUserNameError = this.alertCtrl.create({
title: '用户中心',
subTitle: '输入的用户名格式不正确!',
buttons: ['OK']
})
;

alertUserNameError.present();
} else {
let loading = this.loadingCtrl.create({
content: 'Please wait...',
spinner: 'dots',
duration: 3000, //单位是毫秒
})
;

loading.present();

setTimeout(() => {
loading.dismiss();
}, 3000)
;
}

}

}

lonic32
这里写图片描述
lonic32以上

import { Component } from '@angular/core';
import { NavController,LoadingController} from 'ionic-angular';

@Component({
selector: 'page-contact',
templateUrl: 'contact.html'
}
)
export class ContactPage {
public user={
username : 'parry',
password : ''
}

constructor(public navCtrl: NavController,public loadingCtrl: LoadingController) {
this.navCtrl = navCtrl;
}


login(){
console.log(this.user.username);
let loading = this.loadingCtrl.create({
content:"Please wait....",
duration:3000,
// dismissOnPageChanges:true
}
);

loading.present(loading);
}
}

郁闷,版本更新的东西太多了,不过核心没有改。
文档:http://ionicframework.com/docs/api/components/loading/LoadingController/
这里写图片描述
Alert控件

import { Component } from '@angular/core';
import { NavController,LoadingController,AlertController} from 'ionic-angular';

@Component({
selector: 'page-contact',
templateUrl: 'contact.html'
})
export class ContactPage {
public user={
username : 'parry',
password : ''
}
constructor(public navCtrl: NavController,public loadingCtrl: LoadingController,private alertCtrl: AlertController) {
}

login(){
if(this.user.username == '' || this.user.username.length <= 3){
// alert 提醒用户注意用户名的正确性
let alterUserNameError = this.alertCtrl.create({
title:"Ionic Demo",
subTitle:"输入的用户名格式不正确~",
buttons: ["OK"]
});
alterUserNameError.present();
}else{
console.log(this.user.username);
let loading = this.loadingCtrl.create({
content:"Please wait....",
duration:3000,
// dismissOnPageChanges:true
});
loading.present();
}
}

}

文档:http://ionicframework.com/docs/components/#alert
这里写图片描述
Toast控件
这里写图片描述

let toast = this.toastCtrl.create({
message : "您输入的用户名格式不正确~",
duration: 3000
});
toast.present();

文档:http://ionicframework.com/docs/api/components/toast/ToastController/
这里写图片描述
Grid布局
原理是flex布局(弹性盒子布局)
主要是三个属性:width,offset,wrap

<ion-grid>
<ion-row>
<ion-col col-12 offset-3>This column will take 12 columns</ion-col>
</ion-row>
<ion-row>
<ion-col col-6>This column will take 6 columns</ion-col>
</ion-row>
</ion-grid>

width换成了col
全局样式可以加到app.css
文档:http://ionicframework.com/docs/components/#grid .以后就不写文档了
Modals布局
临时的一个框,添加页面手动失败,使用命令添加的页面

ionic g page register

点击函数

//打开注册窗口
openRegisterPage(){
let register = this.modalCtrl.create(Register);
register.present();
}

ToolBar控件
用来做一些头和尾。

import {ViewController } from 'ionic-angular';
export class Register {

constructor(public navCtrl: NavController, public navParams: NavParams,public viewCtrl: ViewController) {
}
dismiss(){
this.viewCtrl.dismiss();
}
<ion-header>

<ion-toolbar color="primary">
<ion-title>用户注册</ion-title>
<ion-buttons start>
<button ion-button (click)="dismiss()">
<span showWhen="ios">取消</span>
<ion-icon name="md-close" showWhen="android,windows"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>

</ion-header>

start:元素定位到左边的内容在ios模式,定位到右边在安卓和win模式。

List控件
列表,比如联系人,微博
*ngFor循环绑定
ts文件,添加数据(一般数据源都是从Api进行获取,只是模拟)

export class AboutPage {
public contacts = [{"contactid":1,"contactname":"张三","contacttext":"1226603540"},
{"contactid":1,"contactname":"张三","contacttext":"1226603540"}];

constructor(public navCtrl: NavController) {

}
itemClick(event,contact){
alert(contact.contactname);
}

html

<ion-content padding>
<ion-list>
<ion-item *ngFor="let contact of contacts" (click)="itemClick(event,contact)">
<ion-avatar item-left>
<img src="../images/
{{contact.contactid}}.png" alt=""/>
</ion-avatar>
<h2>
{{contact.contactname}}</h2>
<p>
{{contact.contacttext}}</p>
</ion-item>

新版本不用添加*,也就是说ngFor=”let contact of contacts”这样写
这里写图片描述
Cords控件
卡片式,主要是适用于一些流式布局。比如新浪微博手机端

<ion-content padding>
<ion-card>
<ion-item>
<ion-avatar item-left>
<img src="../images/6.png" alt="">
</ion-avatar>
<h2>Elon Musk</h2>
<p>来自 iPhone 6s</p>
</ion-item>
<img src="../images/c1.jpeg"/>
<ion-card-content>
<p>ACM,i'm winner.</p>
</ion-card-content>
<ion-item>
<button ion-button color="primary" clear item-left>
<ion-icon name="thumbs-up"></ion-icon>
<div>1111 赞</div>
</button>
<button ion-button color="primary" clear item-left>
<ion-icon name="text"></ion-icon>
<div>600 评论</div>
</button>
<ion-note item-right>
一小时前
</ion-note>
</ion-item>
</ion-card>
</ion-content>

这里写图片描述
Navigation控件
文档:https://ionicframework.com/docs/components/#navigation
这里写图片描述
以前要使用的初始化,现在取消掉了;
老版本不用加get.现在的用法

item = this.navParams.get('item');

navParams使用说明:http://blog.ionic.io/working-with-data-services-vs-nav-params/

Cordova组件
Ionic的常用组件学习好之后,要学习一些Native Component了,这些组件都是通过PhoneCap的Cordova来访问原生的设备功能的。
要想使用Image Picker,首先就要安装Image Picker组件
使用以下命令安装Image Picker

$ ionic plugin add --save https://github.com/Telerik-Verified-Plugins/ImagePicker
$ npm install --save @ionic-native/image-picker

Image Picker组件
不过支持ios和android

ionic build android

这里写图片描述
解决方法:

npm uninstall --save @ionic-native/core
npm install --save @ionic-native/core@latest

文档:http://ionicframework.com/docs/native/image-picker/
bug
这里写图片描述
正式版后改了要每个原生都要独立安装,还要声明
解决:
这里写图片描述
bug:
安卓下:
这里写图片描述
这里写图片描述
解决:

<img src="../img/dongruan.png">

修改为
<img src="img/dongruan.png">

bug:

Gradle version 2.10 is required. Current version is 2.2.1. If using the gradle wrapper, try editing the distributionUrl in D:\HtmlBeginner\ionic\demo\gradle\wr
apper\gradle-wrapper.properties to gradle-2.10-all.zip

解决:

buildscript {
System.properties['com.android.build.gradle.overrideVersionCheck'] = 'true'

repositories {
mavenCentral()
}

Geolocation组件
请求位置,比如美团,百度外卖。

import { Geolocation } from '@ionic-native/geolocation';

...

constructor(private geolocation: Geolocation) {}

...

this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
})
.catch((error) => {
console.log('Error getting location', error);
})
;

let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
// data can be a set of coordinates, or an error (if an error occurred).
// data.coords.latitude
// data.coords.longitude
})
;

这里写图片描述
Geolocation组件
本地通知。push和极光推送

项目实战

  1. 快速生成App图标和启动页面。

首先我们需要下载cordova-plugin-splashscreen。

cordova plugin add cordova-plugin-splashscreen

在老版本,需要在config.xml中添加代码。
文档:http://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/index.html

根据模板图片通过命令来生成

ionic resources --icon
启动页面
ionic resources --splash

这里写图片描述
文档:http://ionicframework.com/docs/v1/cli/icon-splashscreen.html
各种生成图标和启动界面的文档:
这里写图片描述

 2. 使用localStorage存储状态信息

添加一个用户中心的页面(用用户登陆后跳转到用户中心)

ionic g page usercenter

tabs里面默认的就不是登陆页面了,而应该是登陆成功的页面。在登陆成功页面里面判断有没有登陆。如果没有登陆,使用model来弹出登录页面
登陆页面

if (this.user.password == "1") {
localStorage.username = this.user.username;
localStorage.logined = "true";
setTimeout(() => {
loading.dismiss();
this.viewCtrl.dismiss();
}, 1000)
;
} else {
let toast = this.toastCtrl.create({
message: "登陆失败~",
duration: 3000
})
;
toast.present();
}

登陆成功(center)页面

控制器初始化函数
if(localStorage.logined == "true"){
//已经登陆的状态,不跳转,显示用户信息即可

}else{
let modal = this.modalCtrl.create(ContactPage);
modal.present();
}
注销点击函数
logout() {
localStorage.logined = "";
localStorage.username = "";
let modal = this.modalCtrl.create(ContactPage);
modal.present();
}
  1. Modal关闭后父页面刷新的概念和方法:
    center页面,添加头像
 public user = {
username: '',
password: '',
headface: "images/" + localStorage.username + ".png"
}

constructor(public navCtrl: NavController, public navParams: NavParams,public modalCtrl: ModalController,public viewCtrl: ViewController) {

if(localStorage.logined == "true"){
//已经登陆的状态,不跳转,显示用户信息即可
this.user.headface = "images/" + localStorage.username + ".png";
}else{
let modal = this.modalCtrl.create(ContactPage);
modal.present();
}

不过当我们更改头像时,也就是登录名改为其他时,需要手动刷新页面才可以,因为父页面是没有刷新的。(而且reload方式加载页面效率非常低),所以使用modal传递数据。
modal是可以传参数的。

  let profileModal = this.modalCtrl.create(Profile, { userId: 8675309 });
profileModal.present();

首先,我们在登陆页面消失时传递password

this.viewCtrl.dismiss(this.user.username);

然后在center页面使用

logout和初始化进入时都添加该数据
modal.onDidDismiss(data =>{
this.user.headface = "images/" + data + ".png";})
modal.present();

onDidDismiss()
Called when the current viewController has be successfully dismissed(当视图控制器成功解除时调用)

 4. 网络请求

引入HTTP,https://forum.ionicframework.com/t/no-provider-for-http-error-in-ionic-2/85762/14

app.module.ts

...
import { HttpModule } from '@angular/http';

...
imports: [
BrowserModule,
HttpModule,
IonicModule.forRoot(MyApp)
],

And then you can import the Http Module in your provider

import { Http } from '@angular/http';
....
constructor(private http: Http) { }

登陆页面:
http://api.gugujiankong.com/account/Login?email=1@qq.com&password=1

this.http.get("http://api.gugujiankong.com/account/Login?email=" + this.user.username + "&password=" + this.user.password).subscribe(data =>{
//成功之后
localStorage.username = this.user.username;
localStorage.logined = "true";
loading.dismiss();
this.viewCtrl.dismiss(this.user.username);

//error时
},error =>{
let toast = this.toastCtrl.create({
message: "登陆失败~",
duration: 3000
});
toast.present();
})

网页跨域请求问题(打包成APP是没有问题的)
参考:http://enable-cors.org/
5. List中滑动删除数据
使用ItemSliding

 <ion-item-sliding *ngFor="let contact of contacts">
<ion-item (click)="itemClick(event,contact)">
<ion-avatar item-left>
<img src="images/
{{contact.contactid}}.png" alt="" />
</ion-avatar>
<h2>
{{contact.contactname}}</h2>
<p>
{{contact.contacttext}}</p>
</ion-item>

<ion-item-options>
<button ion-button color="danger" (click)="removeContact(contact)">
<ion-icon name="trash"></ion-icon>
删除</button>
</ion-item-options>
</ion-item-sliding>
  removeContact(contact){
//请求API删除
for(var i = 0;i<this.contacts.length;i++){
if(this.contacts[i] == contact){
this.contacts.splice(i,1);
}
}

console.log(contact.contactid)
}
 6. **集成极光推送实现消息推送**

文档:https://github.com/jpush/jpush-phonegap-plugin
这里写图片描述
这里写图片描述

window.plugins.jPushPlugin.init()
 7. **IOS打包与AppStore上架**

这里写图片描述
苹果开发者账号99美元一年
Xcode->Product->Archive打包
这里写图片描述
这里写图片描述
AndroidStudio
这里写图片描述
这里写图片描述

import * as _ from './foo';

Typings
mhartington.io/post/ionic2-external-libraies

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告