使用Nativescript和Angular2構(gòu)建跨平臺(tái)APP

前言

“一次構(gòu)建,多處運(yùn)行”,跨平臺(tái)APP帶著這股風(fēng)潮把火燒到了前端,為開發(fā)者帶來無盡的遐想。現(xiàn)有的流行跨平臺(tái)框架有以下:

  1. 基于單WebView的開發(fā)框架。開發(fā)者可以使用現(xiàn)有的最新web技術(shù),開發(fā)出單頁面web應(yīng)用。同時(shí)利用JSBridge,又能獲取原生的API,從而使web應(yīng)用具有了原生應(yīng)用的功能。Cordova+IONIC可以說是這個(gè)潮流的代表,也是跨平臺(tái)APP的先鋒。然而這類跨平臺(tái)應(yīng)用的缺點(diǎn)是不流暢,在安卓手機(jī)上體驗(yàn)較差。
  2. Hybrid方向。也就是原生應(yīng)用配合HTML5技術(shù),讓APP具有了部分跨平臺(tái)的功能。Hybrid也是現(xiàn)在各大互聯(lián)網(wǎng)企業(yè)采用較多的跨平臺(tái)開發(fā)方式。這類APP在體驗(yàn)上優(yōu)于單WebView的APP,并且能夠極大提高開發(fā)效率。然而這種方式離“一次構(gòu)建,多處運(yùn)行”的設(shè)想還是有很大距離,畢竟依然需要針對(duì)不同的移動(dòng)平臺(tái)進(jìn)行原生開發(fā)。
  3. 使用Javascript開發(fā)純?cè)鷳?yīng)用。既然單WebView性能有缺憾,Hybrid技術(shù)棧又比較高,那么Javascript開發(fā)純?cè)鷳?yīng)用就孕育而生。這個(gè)方向的代表有ReactNative, Weex和我們今天的主角--Nativescript

和ReactNative相比,Nativescript最大的特點(diǎn)是可以獲得100%的原生API。也就是說,開發(fā)者可以通過Javascript獲取和原生開發(fā)語言同樣多的原生接口。下面,我們通過實(shí)現(xiàn)一個(gè)簡單的計(jì)算器,來體會(huì)一下Nativescript的開發(fā)思想。同時(shí),我們能體會(huì)到“獲取所有原生API”帶來的巨大好處。
你可以先在這里看到最終的結(jié)果。 注意輸入的數(shù)字的字體會(huì)隨著文本長度逐漸變小,想想這個(gè)功能怎么實(shí)現(xiàn)。

Nativescript計(jì)算器

Why Nativescript?

大家定會(huì)好奇,ReactNative這么火,大家都在談?wù)撍笥幸唤y(tǒng)天下的感覺,為什么還要討論Nativescript呢?這是因?yàn)镽eactNative依然不能真正統(tǒng)一編程語言。ReactNative基于平臺(tái)抽象,不可能暴露100%的原生API,因此需要使用原生編程語言進(jìn)行擴(kuò)展,提高了技術(shù)棧。所以,一個(gè)ReactNative的開發(fā)團(tuán)隊(duì)里面必須同時(shí)具備Javascript,Java還有ObjectiveC開發(fā)技術(shù)。
相比之下,Nativescript的技術(shù)棧則要簡單的多,開發(fā)者只需要使用Javascript就能進(jìn)行開發(fā)。這里列出了幾個(gè)Nativescript的核心功能:

  1. 使用Javascript直接訪問所有原生API
  2. 系統(tǒng)新功能0延時(shí)支持
  3. 第三方原生庫全部支持

準(zhǔn)備

首先需要搭建開發(fā)環(huán)境,請(qǐng)參考官方的文檔。你可以使用下面的命令來檢測(cè)Nativescript是否安轉(zhuǎn)完成:

tns doctor

Nativescript支持純Javascript,同時(shí)也支持Angular2。我們選擇Angular2Typescript進(jìn)行開發(fā),體會(huì)一下Angular2帶來的開發(fā)便利。

創(chuàng)建我們的項(xiàng)目

tns create NSCalculator --ng

這個(gè)命令會(huì)在當(dāng)前目錄下新建NSCalculator文件夾,同時(shí)安裝好所需的第三方依賴。其實(shí)這個(gè)命令還給你新建一個(gè)Demo,運(yùn)行下面的命令,你就會(huì)看到你的第一個(gè)Nativescript應(yīng)用

tns platform add ios
tns run ios --emulator

拆分

使用Visual Studio Code打開文件,有如下的文件夾結(jié)構(gòu)

項(xiàng)目結(jié)構(gòu)

我們的代碼將會(huì)組織在app目錄下,所以只需考慮這個(gè)文件夾。main.ts是整個(gè)應(yīng)用的啟動(dòng)文件,我們現(xiàn)在不需要更改它。我們看到main.ts里面引入了app.component模塊,app.component將會(huì)被改造成我們的計(jì)算器組件。在改造app.component.ts前,我們先考慮一下如何將計(jì)算器拆分成一個(gè)個(gè)組件。
計(jì)算器拆分

首先從功能上對(duì)計(jì)算器進(jìn)行拆分。計(jì)算器由Displayer和Keyboard兩個(gè)部分,Keyboard由Button組成。基于此,我們?cè)赼pp目錄下新增三個(gè)文件夾:Displayer,Keyboard,NSButton。

布局

組件化開發(fā)有兩種常用方式,一種是由大到小開發(fā),也就是Calculator -> Displayer -> Keyboard -> Button,一種是由小到大開發(fā),也就是 Button -> Keyboard -> Displayer -> Calculator。當(dāng)然你也可以混合開發(fā)。本例采用由大到小的開發(fā)方式。
刪除app.component.ts里面的內(nèi)容,用以下代替:

// app.component.ts

import {
    Component,
} from "@angular/core";

@Component({
    selector: "calculator",
    template: `
    <GridLayout rows="auto,*" columns="*">
     <StackLayout row="0" style="font-size:60;color:#fff;height:120;background-color:rgba(0,0,0,0.8);text-align:right;vertical-align:bottom;">
      <Label text="99" ></Label>
     </StackLayout>
     <GridLayout row="1" style="background-color:#fff;text-align:center;">
      <Label text="keyboards" ></Label>
      </GridLayout>
    </GridLayout>
`,
})
export class AppComponent {
    public counter: string = '';
}

運(yùn)行以下命令,你就能看到效果

tns livesync ios --emulator --watch

tns run ios命令不同,這個(gè)命令會(huì)監(jiān)視你的文件變化并自動(dòng)構(gòu)建部署新的應(yīng)用。

Nativescript布局

Nativescript的布局方式和安卓原生的布局方式非常類似。對(duì)于計(jì)算器,我們的Displayer需要一個(gè)固定的高度,Keyboard需要占據(jù)全部剩余的空間。因此,Calculator的第一級(jí)布局使用了GridLayout。rows="auto,*" 表示Gridlayout為兩行,第一行高度由內(nèi)容(auto)決定,第二行高度占據(jù)全部剩余空間(*)。columns="*"表示Gridlayout分為一列布局,這一列的寬度占據(jù)全部空間。
我們?yōu)镈isplayer選擇了StackLayout布局,這是因?yàn)槲覀冃枰袻abel靠右下角對(duì)齊。我們?yōu)镵eyboard選擇了GridLayout布局,這很好理解,因?yàn)槲覀冃枰粋€(gè)5x4的格子用來放置計(jì)算器的按鈕。
熟悉ReactNative的同學(xué)可能知道,ReactNative使用Flex布局,非常便于web開發(fā)者掌握。Nativescript則使用和原生開發(fā)非常相似的布局方式,然而這種布局方式其實(shí)也很容易掌握。
注意我們還使用了行內(nèi)CSS來裝飾Nativescript的組件。Nativescript支持的CSS屬性是web CSS屬性的一個(gè)子集。你可以在這里看到Nativescript支持的CSS種類,總的來說,有限但是夠用。

組件

目前為止我們的APP只有一個(gè)組件AppComponent,這非常不利于項(xiàng)目的擴(kuò)展。幸運(yùn)的是Nativescript支持Angular2.0的全部功能,我們可以使用Angular來實(shí)現(xiàn)Displayer和Keyboard組件。
在app目錄下新建Keyboard文件夾,在Keyboard文件里新增keyboard.component.ts,keyboard.html,以及keyboard.css文件。內(nèi)容如下:

//keyboard.component.ts
import {
  Component,
} from '@angular/core';

@Component({
  selector:'keyboard',
  templateUrl:'Keyboard/keyboard.html',
  styleUrls:['Keyboard/keyboard.css']
})

export class Keyboard{
}

<!--keyboard.html-->
<GridLayout row="1" columns="1*,1*,1*,1*"
   rows="1*,1*,1*,1*,1*">
  <!--第一行-->
  <StackLayout row="0" col="0"> 
    <Label class="keyboard-item gray" text="C"></Label>
  </StackLayout>
  <StackLayout row="0" col="1"> 
    <Label class="keyboard-item gray" text="+/-"></Label>
  </StackLayout>
  <StackLayout row="0" col="2"> 
    <Label class="keyboard-item gray" text="%"></Label>
  </StackLayout>
  <StackLayout row="0" col="3"> 
    <Label class="keyboard-item yellow" text="÷"></Label>
  </StackLayout>

  <!--第二行-->
  <StackLayout row="1" col="0"> 
    <Label class="keyboard-item gray" text="7"></Label>
  </StackLayout>
  <StackLayout row="1" col="1"> 
    <Label class="keyboard-item gray" text="8"></Label>
  </StackLayout>
  <StackLayout row="1" col="2"> 
    <Label class="keyboard-item gray" text="9"></Label>
  </StackLayout>
  <StackLayout row="1" col="3"> 
    <Label class="keyboard-item yellow" text="x"></Label>
  </StackLayout>

  <!--第三行-->
  <StackLayout row="2" col="0"> 
    <Label class="keyboard-item gray" text="4"></Label>
  </StackLayout>
  <StackLayout row="2" col="1"> 
    <Label class="keyboard-item gray" text="5"></Label>
  </StackLayout>
  <StackLayout row="2" col="2"> 
    <Label class="keyboard-item gray" text="6"></Label>
  </StackLayout>
  <StackLayout row="2" col="3"> 
    <Label class="keyboard-item yellow" text="-"></Label>
  </StackLayout>

  <!--第四行-->
  <StackLayout row="3" col="0"> 
    <Label class="keyboard-item gray" text="1"></Label>
  </StackLayout>
  <StackLayout row="3" col="1"> 
    <Label class="keyboard-item gray" text="2"></Label>
  </StackLayout>
  <StackLayout row="3" col="2"> 
    <Label class="keyboard-item gray" text="3"></Label>
  </StackLayout>
  <StackLayout row="3" col="3"> 
    <Label class="keyboard-item yellow" text="+"></Label>
  </StackLayout>

  <!--第五行-->
  <StackLayout row="4" col="0" colSpan="2"> 
    <Label class="keyboard-item gray" text="0"></Label>
  </StackLayout>
  <StackLayout row="4" col="2"> 
    <Label class="keyboard-item gray" text="."></Label>
  </StackLayout>
  <StackLayout row="4" col="3"> 
    <Label class="keyboard-item yellow" text="="></Label>
  </StackLayout>

</GridLayout>
// keyboard.css
.keyboard-item {
  border-width: 0.5;
  border-color: rgb(123,123,123);
  font-family: monospace;
  width:100%;
  height: 100%;
  text-align: center;
}

.gray {
  background-color: rgb(205,205,205);
  color:rgb(22,22,22);
  font-size: 25;
}

.yellow {
  background-color:rgb(242,127,38);
  color:#fff;
  font-size: 30;
}

在Keyboard組件里,我們把HTML模板和CSS都拆分到了獨(dú)立文件里面,又回到了那個(gè)干干凈凈的web世界。而這兩者ReactNative都不能支持!所以在組件復(fù)用性和易用性上,Nativescript其實(shí)技高一籌。
然而現(xiàn)在應(yīng)用界面并沒有任何變化,我們還需要在app.component.ts中引入Keyboard組件。修改后的app.component.ts文件如下

//app.component.ts
import {
    Component,
} from "@angular/core";
//引入外部組件
import {
    Keyboard
} from './Keyboard/keyboard.component';

@Component({
    selector: "calculator",
    template: `
    <GridLayout rows="auto,*" columns="*">
     <!--Dispalyer-->
     <StackLayout row="0" style="font-size:60;color:#fff;height:120;background-color:rgba(0,0,0,0.8);text-align:right;vertical-align:bottom;">
      <Label text="99" ></Label>
     </StackLayout>
     <!--引入Keyboard組件-->
     <keyboard row="1"></keyboard>
    </GridLayout>
`,
directives:[Keyboard] //聲明對(duì)Keyboard組件的依賴
})
export class AppComponent {
    public counter: string = '';
}

現(xiàn)在我們的計(jì)算器有點(diǎn)樣子了:

Keyboard組件

似乎有點(diǎn)簡單。但是請(qǐng)注意,這個(gè)界面是完全的原生界面,完全由原生組件構(gòu)成。然而,由于Nativescript的支持,我們確可以像開發(fā)web應(yīng)用一般輕松的開發(fā)原生應(yīng)用了。
但是現(xiàn)在的Keyboard還不能提供任何功能,Keyboard不能響應(yīng)點(diǎn)擊事件,也不能把數(shù)據(jù)傳遞給Displayer。接下來我們就要為Keyboard加上這些功能。

交互

Nativescript提供了原生的手勢(shì)事件,你可以在應(yīng)用中直接使用。由于touch事件會(huì)返回手指的狀態(tài):向下,移動(dòng),向上等,滿足鍵盤交互的需求,因此我們監(jiān)聽touch事件。
要監(jiān)聽Keyboard的點(diǎn)擊事件,最直觀的答案就是為每個(gè)按鈕設(shè)置touch事件監(jiān)聽器,在這個(gè)計(jì)算器應(yīng)用中我們需要設(shè)置19次!而且我們的監(jiān)聽邏輯不能復(fù)用,雖然他們的功能很相似!這是不可接受的。所以,我們應(yīng)該把按鈕也做成一個(gè)Angular組件,因?yàn)榻M件是可以復(fù)用的。
那么這個(gè)按鈕組件需要哪些功能呢? 當(dāng)點(diǎn)擊按鈕時(shí),按鈕的背景色需要改變,并且需要把這個(gè)點(diǎn)擊事件告知其他組件。
在app目錄下新建NSButton文件夾,同時(shí)在NSButton文件夾里新增一下文件: nsbutton.component.ts, nsbutton.css。

//nsbutton.component.ts
import {
  Component,
  ElementRef,
  ViewChild,
  AfterViewInit,
  Input,
  OnInit
} from '@angular/core';
import {
  Label
} from 'ui/label';
import {
  GestureTypes,
  TouchGestureEventData
} from 'ui/gestures';
import {
  Color
} from 'color';
@Component({
  selector: 'nsbutton',
  template: `<Label class="keyboard-item" #nsbutton [text]="text" (touch)="onTouch($event)"> </Label>`,
  styleUrls:['NSButton/nsbutton.css']
})

export class NSButton implements AfterViewInit{
  @ViewChild('nsbutton') nsBtnRef : ElementRef;
  @Input('text') text: string;
  @Input('normalBg') normalBg:string;
  @Input('activeBg') activeBg:string;
  private nsBtnView : Label;

  ngAfterViewInit() {
    this.nsBtnView = <Label> this.nsBtnRef.nativeElement;
    this.changeBg(this.nsBtnView,this.normalBg || '#D0D0D0');
  }

  changeBg(component:Label,bgColor:string) {
    component.backgroundColor = new Color(bgColor);
  }

  onTouch(event) {
    this.onTouchEvent(event.action);
  }

  onTouchEvent(type:string) {
    switch(type) {
      case 'down':
      case 'move':
        this.changeBg(this.nsBtnView,this.activeBg || '#A3A3A3');
      break;
      default:
        this.changeBg(this.nsBtnView,this.normalBg || '#D0D0D0');
      break;
    }
  }
}

請(qǐng)注意,這里我們沒有使用ngStyle的方式來改變按鈕的背景,而是用了更底層一級(jí)的API。主要是為了像你展示在Nativescript中使用原生API是多么輕松的一件事。

//nsbutton.css
.keyboard-item {
  border-width: 0.5;
  border-color: rgb(123,123,123);
  font-family: monospace;
  width:100%;
  height: 100%;
  text-align: center;
  font-size: 25;
}

然后,需要在Keyboard組件里引入NSButton。修改keyboard.component.ts和keyboard.html文件如下:

//keyboard.component.ts
import {
  Component,
} from '@angular/core';
import {
  NSButton
} from '../NSButton/nsbutton.component';

@Component({
  selector:'keyboard',
  templateUrl:'Keyboard/keyboard.html',
  styleUrls:['Keyboard/keyboard.css'],
  directives:[NSButton],
})

export class Keyboard{
}
//keyboard.html
<GridLayout row="1" columns="1*,1*,1*,1*"
   rows="1*,1*,1*,1*,1*">
  <!--第一行-->
  <GridLayout row="0" col="0"> 
    <nsbutton text="C"></nsbutton>
  </GridLayout>
  <GridLayout row="0" col="1"> 
    <nsbutton text="+/-"></nsbutton>
  </GridLayout>
  <GridLayout row="0" col="2"> 
    <nsbutton text="%"></nsbutton>
  </GridLayout>
  <GridLayout row="0" col="3" style="color:#fff;"> 
    <nsbutton text="÷" normalBg="#F27F26" activeBg="#B65F1C"></nsbutton>
  </GridLayout>

  <!--第二行-->
  <GridLayout row="1" col="0"> 
    <nsbutton text="7"></nsbutton>
  </GridLayout>
  <GridLayout row="1" col="1"> 
    <nsbutton text="8"></nsbutton>
  </GridLayout>
  <GridLayout row="1" col="2"> 
    <nsbutton text="9"></nsbutton>
  </GridLayout>
  <GridLayout row="1" col="3" style="color:#fff;"> 
    <nsbutton text="x" normalBg="#F27F26" activeBg="#B65F1C"></nsbutton>
  </GridLayout>

  <!--第三行-->
  <GridLayout row="2" col="0"> 
    <nsbutton text="4"></nsbutton>
  </GridLayout>
  <GridLayout row="2" col="1"> 
    <nsbutton text="5"></nsbutton>
  </GridLayout>
  <GridLayout row="2" col="2"> 
    <nsbutton text="6"></nsbutton>
  </GridLayout>
  <GridLayout row="2" col="3" style="color:#fff;"> 
    <nsbutton text="-" normalBg="#F27F26" activeBg="#B65F1C"></nsbutton>
  </GridLayout>

  <!--第四行-->
  <GridLayout row="3" col="0"> 
    <nsbutton text="1"></nsbutton>
  </GridLayout>
  <GridLayout row="3" col="1"> 
    <nsbutton text="2"></nsbutton>
  </GridLayout>
  <GridLayout row="3" col="2"> 
    <nsbutton text="3"></nsbutton>
  </GridLayout>
  <GridLayout row="3" col="3" style="color:#fff;"> 
    <nsbutton text="+" normalBg="#F27F26" activeBg="#B65F1C"></nsbutton>
  </GridLayout>

  <!--第五行-->
  <GridLayout row="4" col="0" colSpan="2"> 
    <nsbutton text="0"></nsbutton>
  </GridLayout>
  <GridLayout row="4" col="2"> 
    <nsbutton text="."></nsbutton>
  </GridLayout>
  <GridLayout row="4" col="3" style="color:#fff;"> 
    <nsbutton text="=" normalBg="#F27F26" activeBg="#B65F1C"></nsbutton>
  </GridLayout>

</GridLayout>

現(xiàn)在,我們的按鈕組件就有了一個(gè)漂亮的點(diǎn)擊動(dòng)畫。


點(diǎn)擊動(dòng)畫

不過Keyboard的點(diǎn)擊事件還不能通知給Displayer,我們接著來增加這個(gè)功能,修改nsbuttom.component.ts, keyboard.component.ts, app.component.ts如下:

//nsbuttom.component.ts
import {
  Component,
  ElementRef,
  ViewChild,
  AfterViewInit,
  Input,
  OnInit
} from '@angular/core';
import {
  Label
} from 'ui/label';
import {
  GestureTypes,
  TouchGestureEventData
} from 'ui/gestures';
import {
  Color
} from 'color';
@Component({
  selector: 'nsbutton',
  template: `<Label class="keyboard-item" #nsbutton [text]="text" (touch)="onTouch($event)"> </Label>`,
  styleUrls:['NSButton/nsbutton.css']
})

export class NSButton implements AfterViewInit{
  @ViewChild('nsbutton') nsBtnRef : ElementRef;
  @Input('text') text: string;
  @Input('normalBg') normalBg:string;
  @Input('activeBg') activeBg:string;
  @Input('onBtnClicked') onBtnClicked: Function;
  private nsBtnView : Label;

  ngAfterViewInit() {
    this.nsBtnView = <Label> this.nsBtnRef.nativeElement;
    this.changeBg(this.nsBtnView,this.normalBg || '#D0D0D0');
  }

  changeBg(component:Label,bgColor:string) {
    component.backgroundColor = new Color(bgColor);
  }

  onTouch(event) {
    this.onTouchEvent(event.action);
  }

  onKeyUp() {
    if(this.onBtnClicked) {
      this.onBtnClicked && this.onBtnClicked(this.text);
    }
  }

  onTouchEvent(type:string) {
    switch(type) {
      case 'down':
      case 'move':
        this.changeBg(this.nsBtnView,this.activeBg || '#A3A3A3');
      break;
      case 'up':
        this.onKeyUp();
      default:
        this.changeBg(this.nsBtnView,this.normalBg || '#D0D0D0');
      break;
    }
  }
}
//keyboard.component.ts
import {
  Component,
  Input,
  OnInit
} from '@angular/core';
import {
  NSButton
} from '../NSButton/nsbutton.component';

@Component({
  selector:'keyboard',
  templateUrl:'Keyboard/keyboard.html',
  styleUrls:['Keyboard/keyboard.css'],
  directives:[NSButton],
})

export class Keyboard implements OnInit{

  @Input('onKeyBoardClicked') onKeyBoardClicked: Function;

  ngOnInit() {
    this.onBtnClicked = this.onBtnClicked.bind(this);
  }

  public onBtnClicked(text:string):void {
    this.onKeyBoardClicked && this.onKeyBoardClicked(text);
  }
}
//app.component.ts
import {
    Component,
    OnInit,
    ElementRef,
    ViewChild,
    AfterViewInit,
} from "@angular/core";
import {
    Label
} from 'ui/label';
import {
  Keyboard
} from './Keyboard/keyboard.component';
import {
  device,
  platformNames
} from 'platform';

@Component({
    selector: "calculator",
    template: `
            <GridLayout rows="auto,*" columns="*">
            <!--Dispalyer-->
            <StackLayout row="0" style="font-size:60;color:#fff;height:120;background-color:rgba(0,0,0,0.8);text-align:right;vertical-align:bottom;">
                <Label [text]="counter" #displayer></Label>
            </StackLayout>
            <!--Keyboard-->
            <keyboard row="1" [onKeyBoardClicked]="onKeyBoardClicked"></keyboard>
            </GridLayout>
    `,
    directives:[Keyboard]
})
export class AppComponent implements OnInit{
    public counter: string = '';
    @ViewChild('displayer') displayerRef: ElementRef;
    private displayerView : Label;

    ngOnInit() {
            this.onKeyBoardClicked = this.onKeyBoardClicked.bind(this);
    }

    public onKeyBoardClicked(text:string):void {
            this.counter += text;        
    }
}

這樣我們的Keyboard點(diǎn)擊就能實(shí)時(shí)顯示在Disapler上面了。
這個(gè)交互我們完全依賴于Angular2為我們提供的單向綁定。再強(qiáng)調(diào)一次,Nativesript支持所有的Angular2功能,這真的會(huì)簡化我們的開發(fā)。


鍵盤交互

原生API

對(duì)比我們的計(jì)算器和ios原生計(jì)算器,我們發(fā)現(xiàn)原生計(jì)算器的Displayer會(huì)自動(dòng)調(diào)整字體大小,以保證顯示的數(shù)字完全展示。得益于Nativescript,我們也能通過Javascript調(diào)用這套API,實(shí)現(xiàn)相同的效果。修改app.comonent.ts如下:

//app.component.ts
import {
    Component,
    OnInit,
    ElementRef,
    ViewChild,
    AfterViewInit,
} from "@angular/core";
import {
    Label
} from 'ui/label';
import {
  Keyboard
} from './Keyboard/keyboard.component';
import {
  device,
  platformNames
} from 'platform';

@Component({
    selector: "calculator",
    template: `
            <GridLayout rows="auto,*" columns="*">
            <!--Dispalyer-->
            <StackLayout row="0" style="font-size:60;color:#fff;height:120;background-color:rgba(0,0,0,0.8);text-align:right;vertical-align:bottom;">
                <Label [text]="counter" #displayer></Label>
            </StackLayout>
            <!--Keyboard-->
            <keyboard row="1" [onKeyBoardClicked]="onKeyBoardClicked"></keyboard>
            </GridLayout>
    `,
    directives:[Keyboard]
})
export class AppComponent implements OnInit, AfterViewInit{
    public counter: string = '';
    @ViewChild('displayer') displayerRef: ElementRef;
    private displayerView : Label;

    ngAfterViewInit() {
        this.displayerView = <Label> this.displayerRef.nativeElement;
        this.setIOSLabelAutoFont(this.displayerView);
    }

    private setIOSLabelAutoFont(elem: Label) {
    if(device.os === platformNames.ios){
      elem.ios.numberOfLines = 1;
      elem.ios.minimumFontSize = 20;
      elem.ios.adjustsFontSizeToFitWidth = true;
    }
  }

    ngOnInit() {
            this.onKeyBoardClicked = this.onKeyBoardClicked.bind(this);
    }

    public onKeyBoardClicked(text:string):void {
            this.counter += text;        
    }
}

elem.ios.numberOfLines = 1, elem.ios.minimumFontSize = 20, elem.ios.adjustsFontSizeToFitWidth = true 是三個(gè)IOS的UILabel原生的api。在Nativescript中,我們可以使用Javascript直接調(diào)用他們,Nativescript會(huì)幫我們處理好Javascript到原生的映射,包括數(shù)據(jù)類型的轉(zhuǎn)換,不需要寫任我的ObjectiveC代碼。
如此,我們就實(shí)現(xiàn)了在文章最開始展示的交互。** 這個(gè)交互,在ReactNative中,是無法直接實(shí)現(xiàn)的! **這也是Nativescript相比于ReactNative的強(qiáng)大之處

總結(jié)

得益于Nativescript可以訪問100%的原生API, 我們不需要寫任何原生代碼,就能獲得和原生代碼一樣的能力。這可以說是Nativescript相比于其他平臺(tái),比如React Native, 最大的優(yōu)勢(shì)。在現(xiàn)階段,Nativescript應(yīng)該是最成熟的,性能最好的跨平臺(tái)APP構(gòu)建方案。
github代碼在這里:https://github.com/eeandrew/NSCalculator

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,165評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,435評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,943評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,175評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,713評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,599評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,788評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,034評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評(píng)論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,408評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,747評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容