ionic2/3 预处理安卓返回键和监听返回键执行自定义操作(订阅-发布模式)

971 阅读3分钟

概述

  1. 这个解决方案的预处理部分,参考了完美处理安卓硬件返回按钮这篇文章,并在这个基础上添加监听返回键执行自定义操作等。
  2. 预处理点击返回键关闭键盘、返回上一页、最小化 app,然后进一步处理 ActionSheet、Alert、Menu 、Select 等打开的时候的问题。

效果预览

安装

  1. 把 demo/src/core/common/back.service.ts 文件拷贝到到你的项目中。 demo 地址

  2. 安装 App Minimize 插件。

ionic cordova plugin add cordova-plugin-appminimize

npm install --save @ionic-native/app-minimize
  1. 在你的项目 app.module.ts 添加 BackService 和 AppMinimize 供应商

import { AppMinimize } from '@ionic-native/app-minimize';

import { BackService } from ...;

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [
    AppMinimize,
    BackService
  ]
})
export class AppModule { }

BackService

一个监听返回键,触发自定义操作的服务

接口成员

state

有未执行的自定义操作返回 true,无未执行的自定义操作返回 false

subscribe(calback)

添加自定义返回键操作

Param Type Details
calback function 自定义操作的函数

unsubscribe()

移除一个自定义返回键操作

publish()

发起一个自定义返回键操作

clear()

清除所有所有自定义操作

使用

有些项目的第一页不是 Tabs,而是需要登录后,才跳转到 Tabs,所以我们需要在两个地方写预处理,首先给 MyApp 添加预处理方法,然后给 Tabs 添加预处理

预处理

给 MyApp 添加预处理方法

在 app.html 添加模板变量 #myContent

<ion-nav [root]="rootPage" #myContent></ion-nav>

在 app.component.ts 添加以下代码

import { Platform, Keyboard, NavController } from 'ionic-angular';
import { AppMinimize } from '@ionic-native/app-minimize';
import { BackService } from ...;

export class MyApp {
  @ViewChild('myContent')
  navCtrl: NavController;

  constructor(
    private platform: Platform,
    private keyboard: Keyboard,
    private appMinimize: AppMinimize,
    private backService: BackService
  ) {
    platform.ready().then(() => {
      ...
      this.registerBack();
    });
  }

  // 注册返回键
  private registerBack() {
    this.platform.registerBackButtonAction(() => {
      if (this.keyboard.isOpen()) {
        // 关闭键盘
        this.keyboard.close();
      } else if (this.backService.state) {
        // 触发自定义返回事件
        this.backService.publish();
      } else if (this.navCtrl.canGoBack()) {
        // 返回上一页
        this.navCtrl.pop();
      } else {
        // 最小化app
        this.appMinimize.minimize();
      }
    }, 101);
  }
}

给 Tabs 添加预处理方法

在 tabs.ts 添加以下代码

import { Component, ViewChild } from '@angular/core';
import {
  IonicPage,
  NavController,
  Platform,
  Keyboard,
  Tabs
} from 'ionic-angular';
import { AppMinimize } from '@ionic-native/app-minimize';
import { BackService } from ...;

...

export class TabsPage {

  ...

  @ViewChild(Tabs)
  tabs: Tabs;

  constructor(
    private navCtrl: NavController,
    private platform: Platform,
    private keyboard: Keyboard,
    private appMinimize: AppMinimize,
    private backService: BackService
  ) {}

  ionViewDidLoad() {
    this.registerBack();
  }

  // 注册返回键
  private registerBack() {
    this.platform.registerBackButtonAction(() => {
      // 关闭键盘
      if (this.keyboard.isOpen()) {
        return this.keyboard.close();
      }

      // 触发自定义返回事件
      if (this.backService.state) {
        return this.backService.publish();
      }

      const activeVC = this.navCtrl.getActive();
      const page = activeVC.instance;

      if (!(page instanceof TabsPage)) {
        if (!this.navCtrl.canGoBack()) {
          // 最小化app
          return this.appMinimize.minimize();
        }

        return this.navCtrl.pop();
      }

      const tabs = page.tabs;
      const activeNav = tabs.getSelected();

      if (!activeNav.canGoBack()) {
        // 关闭Menu
        if (this.menuCtrl.isOpen()) {
          return this.menuCtrl.close();
        }

        // 最小化app
        return this.appMinimize.minimize();
      }

      return activeNav.pop();
    }, 102);
  }
}

进一步处理

通过重载 ActionSheet、Alert、Loading、Modal、Popover 等的 create 方法来监听返回键。

ActionSheet

  1. 创建一个 action-sheet.service.ts,然后添加一下代码
import { Injectable } from '@angular/core';
import {
  ActionSheetController,
  ActionSheet,
  ActionSheetOptions
} from 'ionic-angular';
import { BackService } from './back.service';

@Injectable()
export class ActionSheetService {
  constructor(
    private actionSheetCtrl: ActionSheetController,
    private backService: BackService
  ) {}

  create(options?: ActionSheetOptions): ActionSheet {
    const actionSheet = this.actionSheetCtrl.create(options);

    // onWillDismiss的时候,取消订阅返回键
    actionSheet.onWillDismiss(() => {
      this.backService.unsubscribe();
    });

    this.backService.subscribe(() => {
      // 点击返回键,关闭
      actionSheet.dismiss();
    });

    return actionSheet;
  }
}
  1. 在你的项目 app.module.ts 添加 ActionSheetService 供应商

import { ActionSheetService } from ...;

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [
    ActionSheetService
  ]
})
export class AppModule { }
  1. 调用 ActionSheet 的地方,把 AlertController 改成 ActionSheetService 即可
import { ActionSheetService } from ...;

export class MyPage {
  constructor(
    private alertCtrl:ActionSheetService
  ) {}


  presentActionSheet() {
    const actionSheet = this.alertCtrl.create({
      title: 'ActionSheet',
      buttons: [
        ...
        {
          text: 'Cancel',
          role: 'cancel',
          handler: () => {
            ...
          }
        }
      ]
    });

     actionSheet.onDidDismiss(() => {
      ...
    });

    actionSheet.present();
  }

Alert、Loading、Modal、Popover的实现方法跟 ActionSheet 一样

特殊处理

使用ion-select和ion-datetime需要做特殊处理。

ion-select

添加 模板引用变量 并绑定事件 click 事件

<ion-select [(ngModel)]="gender" #genderSelect (click)="openSelect()">
  <ion-option value="f">Female</ion-option>
  <ion-option value="m">Male</ion-option>
</ion-select>

然后添加以下代码


import { ViewChild } from '@angular/core';
import { Select } from 'ionic-angular';
import { BackService } from ...;

export class MyPage {

  ...

  @ViewChild('genderSelect') genderSelect: Select;

  constructor(private backService: BackService) {}

  ionViewDidLoad() {
    this.genderSelect.ionBlur.subscribe(() => {
      this.backService.unsubscribe();
    });
  }

  openSelect() {
    this.backService.subscribe(() => {
      this.genderSelect.close();
    });
  }
}

ion-datetime

添加 模板引用变量 并绑定事件 click 事件

<ion-item>
  <ion-label>Date</ion-label>
  <ion-datetime
    [(ngModel)]="myDate"
    #datePicker
    (click)="openDatePicker()"
  ></ion-datetime>
</ion-item>

然后添加以下代码


import { ViewChild } from '@angular/core';
import { DateTime } from 'ionic-angular';
import { BackService } from ...;

export class MyPage {

  ...

  @ViewChild('datePicker') datePicker: DateTime;

  constructor(private backService: BackService) {}

  ionViewDidLoad() {
    this.datePicker.ionBlur.subscribe(() => {
      this.backService.unsubscribe();
    });
  }

  openDatePicker() {
    this.backService.subscribe(() => {
      this.datePicker.close();
    });
  }
}