1. 基础知识
1.1 为什么选择NativeScript
1.1.1 什么是NativeScript
NativeScript可以用javascript来写Android和iOS的应用,如下图所示NativeScript的代码与网页开发的代码很相似,都是用CSS写样式,javascript写业务逻辑,不同的是NativeScript使用XML来描述页面结构(NativeScript封装了自己的UI库)。
1.1.2 移动端开发方式的对比
如下图所示, 移动应用程序可分为四大类:native, hybrid, cross compiled, and just-in-time (JIT) compiled
1.1.3 NativeScript的优点
更少的shim代码(处理Android和iOS的不同)、一次写入(Android和iOS共用一套代码)、随处部署等等。
1.1.4 NativeScript能构建什么样的应用
因为NativeScript应用程序直接运行在设备上,并由运行在应用程序内部的JavaScript虚拟机解释,这意味着NativeScript应用程序不受访问本机设备api或硬件的限制,因此任何应用程序都可以编写为NativeScript应用。理论上是这样的,但是NativeScript应用程序是在JavaScript虚拟机中运行的,所以在应用程序和裸机之间有一个额外的(尽管很小)抽象层。要从设备中提取每一点性能,所以它不适合构建图形密集的游戏。
1.1.5 NativeScript是怎么工作的
NativeScript Runtime
:NativeScript Runtime是连接JavaScript代码和Android和iOS原生API之间的接口代码。就像浏览器制造商教他们的JavaScript虚拟机如何使用DOM和windows对象一样,NativeScript Runtime也教JavaScript虚拟机如何使用本机设备底层的API。NativeScript Core Modules
: NativeScriptCore Modules是一组库,这些库是用来构建应用程序并指示NativeScript运行时在设备上做什么。核心模块由不同的库组成,如UI组件(按钮、列表视图、标签)、导航和应用程序。JavaScript virtual machine
:理解并执行JavaScript的代码,但是不知道怎么与设备交互,所以NativeScript开发团队编写了接口代码(称为NativeScript Runtime和NativeScript Core Modules)来教JavaScript虚拟机有关Android和iOS等移动设备API的知识。NativeScript CLI
:NativeScript CLI抽离了本地工具和SDK的复杂性,为我们提供了一组与平台无关的命令来构建和部署应用程序。
1.2 第一个NativeScript的应用
- 参考的Full Setup搭建环境
- 在下载Android Studio然后按照配置Android的模拟器
- 打开命令行工具,运行
tns create HelloWorld --template tns-template-hello-world
- 进入目标文件夹
cd [filename]
- 运行命令
tns run android --emulator
,即可在Android模拟器上运行代码
2. 构建应用
2.1 剖析NativeScript应用程序
2.1.1 探索NativeScript应用程序的结构
|- app //开发目录 |- App_Resources //放置Android和iOS平台特殊的配置,例如App的图标等。 |- Android |- iOS |- app.css //全局的CSS样式,app运行时载入 |- app.js //启动文件,里面说明了从哪个页面启动应用 |- bundle-config.js //用于配置webpack(如果已安装了) |- main-page.js //写业务逻辑 |- main-page.xml //写页面代码 |- main-view-model.js //相当于MVVM框架的vm层 |- package.json //描述应用程序的特征和依赖项 |- references.d.ts //为编辑器提供TypeScript的智能提示|- node_modules //依赖的库文件|- platforms //由NativeScript自动生成和维护|- package.json //描述应用程序的特征和依赖项复制代码
// package.json{ "description": "NativeScript Application", //提供应用程序、功能和用途的简要说明 "license": "SEE LICENSE IN", //将协作开发人员指向您的许可文件,以描述其他人必须对您的应用程序代码作出贡献、修改、更改和重新分发哪些权限(可选) "readme": "NativeScript Application", //指向应用程序的README文件 "repository": " ", //应用程序公共或私有代码存储库的位置(可选) "nativescript": { //一个特定于nativescript的部分,带有应用程序的标识符,Android和iOS平台使用它惟一地标识应用程序 "id": "org.nativescript.myapp" }, "dependencies": { //npm使用的外部库和应用程序所依赖的库版本的列表 "nativescript-theme-core": "~1.0.2", "tns-core-modules": "3.1.0" }}复制代码
2.2 页面和导航
2.2.1 创建多页面应用
- 按照之前的操作创建HelloWorld项目
- 进入app文件夹,删除文件夹下的main-page.js, main-page.xml, main-view-model.js, 在app文件夹下创建view文件夹放页面,每个页面单独创建文件夹,文件夹应该与页面命名一致,文件目录如下:
|- app |- views |- home |- home.css |- home.xml |- home.js |- about |- about.css |- about.xml |- about.js复制代码
- 修改项目启动文件app.js,将app启动页面改成home页面
require("./bundle-config");var application = require("application");application.start({ moduleName: "views/home/home" }); //将moduleName的键值修改成home页面的路径复制代码
- 在home.xml文件中写入如下代码
//页面中所有其他元素的容器,类似于网页开发里面的body标签 复制代码//布局元素,类似于网页开发里面的div标签 //在屏幕上显示文本
- 在about.xml文件中写入如下代码
复制代码 //textWrap为true时会自动换行
2.2.2 多页面之间的导航
- 在home.xml文件中写入如下代码
复制代码 //tap属性告诉NativeScript在单击按钮时调用onTap这个JavaScript函数
- 在home.js文件中写入如下代码
var frameModule = require("ui/frame"); //获取NativeScript框架中导航模块的引用function onTap() { frameModule.topmost().navigate("views/about/about "); //使用frame模块导航到about页面}exports.onTap = onTap; //必须导出该函数,以便NativeScript运行时可以从UI访问它复制代码
2.2.3 在页面导航间应用转换动画
如下表所示是各个平台支持的过渡动画
var frames = require("ui/frame");function onTap() { var navigationEntry = { moduleName: "views/about/about", transition: { name: "slideBottom" //将想要应用的过渡动画写在这里 } }; frames.topmost().navigate(navigationEntry);}exports.onTap = onTap;复制代码
2.3 理解页面布局
参考进行学习
2.4 写app的样式
参考进行学习
3. 改善应用
3.1 数据绑定
3.1.1 普通数据的双向绑定
- 在home.xml文件中写入如下代码
复制代码 //在{ {}}内写入需要绑定的变量
- 在home.js文件中写入如下代码
var observableModule = require("data/observable");var viewModule = require ("ui/core/view"); exports.onLoaded = function(args){ var page = args.object; var pet = new observableModule.Observable(); //pet对象是一个可观察的对象,它将绑定到页面上的所有元素 page.bindingContext = pet; //将pet设置为页面的绑定上下文,将其设置为用于绑定的页面级可观察对象 pet.set("Name", "Riven"); //也可以写成下面的形式 //var pet = new observable.fromObject({ // Name: "Riven" //}); //page.bindingContext = pet;}复制代码
3.1.2 列表数据的双向绑定
- 在home.xml文件中写入如下代码
复制代码 //pages是一个可观察数组 //pages中的每一项会被渲染一次 //pages每一项上的title
- 在home.js文件中写入如下代码
var observable = require("data/observable");var observableArray = require("data/observable-array");exports.onLoaded = function(args) { var page = args.object; var filledPage = new observable.Observable({ title: "Riven's Page" }); var home = new observable.Observable({ pages: new observableArray.ObservableArray(filledPage) //生成可观察对象数组 }); page.bindingContext = home;};复制代码
可以将数据绑定的内容封装成单独的pageName-view-model.js文件,方便多个页面共用一个view model.
3.2 NativeScript与设备硬件的交互
3.2.1 文件系统模块
var fileSystemModule = require("file-system"); //要使用文件系统模块,需要导入它 exports.onLoaded = function(){ var fileName = "myFile.json"; var file = fileSystemModule.knownFolders.documents().getFile(fileName); //使用documents文件夹存储应用程序需要的离线文件 var data = { name: "Brosteins", type: "filesystemexample"}; var jsonDataToWrite = JSON.stringify(data); file.writeText(jsonDataToWrite); //将数据写入文件系统 console.log("Wrote to the file: " + jsonDataToWrite); var jsonDataRead = file.readTextSync(); //使用对文件的引用来读取数据。数据可以同步读取,也可以异步读取 console.log("Read from the file: " + jsonDataRead); file.remove(); //删除该文件};复制代码
3.2.2 相机
安装相机的插件:npm install nativescript-camera --save
var camera = require("nativescript-camera");var image = require("image-source");exports.onAddImageTap = function (args) { var page = args.object; var scrapbookPage = page.bindingContext; camera.requestPermissions(); //要使用照相机需要获得许可 camera .takePicture() //返回一个promise .then(function (picture) { //当promise解析后,调用then()函数,传递图片 image.fromAsset(picture).then(function (imageSource) { scrapbookPage.set("image", imageSource); //创建要绑定到视图的图像源对象 }); });}复制代码
如果保存图片,需要先用 image.toBase64String("png") 将图片的二进制数据转换成base64字符串然后再保存起来。
3.2.3 GPS定位
安装定位的插件:tns plugin add nativescript-geolocation
var camera = require("nativescript-camera");var image = require("image-source");var geolocation = require("nativescript-geolocation"); exports.onAddImageTap = function (args) { var page = args.object; var scrapbookPage = page.bindingContext; if (!geolocation.isEnabled()) { //在使用位置服务之前,应该检查是否启用了它,并请求启用它 geolocation.enableLocationRequest(); } camera .takePicture({ width: 100, height: 100, keepAspectRatio: true }) .then(function (picture) { image.fromAsset(picture).then(function (imageSource) { scrapbookPage.set("image", imageSource); }); geolocation.getCurrentLocation().then(function (location) { //获取位置数据会自动提示用户请求权限 scrapbookPage.set("lat", location.latitude); //返回的位置的纬度值 scrapbookPage.set("long", location.longitude); //返回的位置的经度值 }); });};复制代码
3.3 创建具有主题的专业UI
参考进行学习
3.4 改善用户的体验
3.4.1 用modal构建更专业的UI
- 主页面的xml文件如下
复制代码
- 主页面的js文件如下
var page;exports.onLoaded = function(args) { page = args.object; var scrapbookPage = page.navigationContext.model; page.bindingContext = scrapbookPage;};exports.onBirthDateTap = function(args) { var modalPageModule = "views/selectDate-page"; var context = { birthDate: page.bindingContext.birthDate }; var fullscreen = true; page.showModal( modalPageModule, context, function closeCallback(birthDate) { //关闭modal时的回调函数,可以将modal页面的数据传递回来 page.bindingContext.set("birthDate", birthDate); }, fullscreen );};复制代码
- modal页面的xml
复制代码
- modal页面的js
var observableModule = require("data/observable"); var model; var closeCallback; exports.onLoaded = function(args) { var page = args.object; model = new observableModule.fromObject({ date: new Date(Date.now()) }); page.bindingContext = model;};exports.onShownModally = function(args) { closeCallback = args.closeCallback; }; exports.onDoneTap = function(args) { closeCallback(model.date); };复制代码
3.4.2 适配平板电脑
可以创建 page-name.land.minWH600.xml
,page-name.land.minWH600.js
,page-name.land.minWH600.css
文件单独写平板端的页面。
3.5 部署前的配置
3.5.1 修改app图标
1. Android
Android的app图标放在 App_Resources目录下的drawable-*的各个文件夹中,也就是不同分辨率的设备用相应的图标
App_Resources/ AndroidManifest.xml
文件中有关于app的各项设置 复制代码
2. iOS
在 App_Resources\iOS\Assets.xcassets\AppIcon.appiconset
文件下放入用入在这个下生成的iOS需要的适配各种设备的图标
3.5.2 修改app名字
1. Android
在 App_Resources/Android/values/strings.xml
里面修改app的名字
复制代码 Pet Scrapbook //在这里改应用程序的名称Pet Scrapbook //使用时app的名字
2. iOS
在App_Resources/iOS/Info.plist
里面修改app的名字
CFBundleDisplayName ${PRODUCT_NAME} //在这里修改名字,可以把名字直接写在标签里复制代码
3.5.3 修改app版本号
1. Android
在App_Resources/ AndroidManifest.xml
里配置
//用户在谷歌商店可以看到的版本号复制代码
2. iOS
在App_Resources/iOS/Info.plist
里面修改app的版本号
CFBundleVersion 1.0.0 //版本号复制代码
CFBundleShortVersionString 1.0.0 //构建号复制代码
3.5.4 修改app适配机器
1. Android
在App_Resources/ AndroidManifest.xml
里配置
//支持约7+英寸的屏幕复制代码
Android屏幕大小和相应的屏幕分辨率DPIs如下表所示
2. iOS
在App_Resources/iOS/Info.plist
里适配各种设备
UISupportedInterfaceOrientations //适配iPhonesUIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad //适配iPads复制代码 UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
3.5.5 修改app启动页面
1. Android
drawable-nodpi
目录下的splash_screen.xml
配置了app的启动页面
复制代码 //启动屏幕的背景图 //启动屏幕中心的logo图片
启动屏幕的背景图和中心的logo图配置的方法和app的图标是相同的
2. iOS
- 在
App_Resources\iOS\Assets.xcassets\LaunchScreen.AspectFill.imageset
里放启动屏幕的背景图 - 在
App_Resources\iOS\Assets.xcassets\LaunchScreen.Center.imageset
里放启动屏幕中心的logo图片 可以在这个生成适配各种设备的图片
4. 结语
- 本文参考:官网书籍
- 更多学习资源: