sencha touch 实战转曲


内 容 简 介 Sencha 框架是第一个基于 HTML 5 的移动应用框架,可以让 Web 应用看起来像网络应用。美丽的用户界 面组件和丰富的数据管理,全部基于最新的 HTML 5 和 CSS 3 的 Web 标准,全面兼容 Android 和 iOS 设备。 本书从实用开发的角度,详细讲解了 Sencha Touch 的开发过程及基本组件的使用。全书共分 13 章,前 3 章主 要讲解如何搭建 Sencha Touch 的开发环境,第 4 章~第 12 章主要以实例的形式介绍了 Sencha Touch 组件的使 用方法,第 13 章通过模仿一个原生应用程序介绍了 Sencha Touch 是如何开发复杂应用程序的。 本书注重应用,突出实战,示例丰富,适合希望立刻就能上手使用 Sencha Touch 进行开发的新手和有一 定开发经验的人员。 本书封面贴有清华大学出版社防伪标签,无标签者不得销售。 版权所有,侵权必究。侵权举报电话:010-62782989 13701121933 图书在版编目(CIP)数据 Sencha Touch 实战/黄灯桥编著. —北京:清华大学出版社,2014 ISBN 978-7-302-34199-4 Ⅰ. ①S… Ⅱ. ①黄… Ⅲ. ①移动电话机-应用程序-程序设计 Ⅳ. ①TN929.53 中中国版本图书馆 CIP 数据核字(2013)第 246291 号 责任编辑:王金柱 封面设计:王 翔 责任校对:闫秀华 责任印制: 出版发行:清华大学出版社 网 址:http://www.tup.com.cn,http://www.wqbook.com 地 址:北京清华大学学研大厦 A 座 邮 编:100084 社 总 机:010-62770175 邮 购:010-62786544 投稿与读者服务:010-62776969,c-service@tup.tsinghua.edu.cn 质量反馈:010-62772015,zhiliang@tup.tsinghua.edu.cn 印 装 者: 经 销:全国新华书店 开 本:190mm×260mm 印 张:24.75 字 数:634 千字 版 次:2014年1月第1版 印 次:2014年1月第1次印刷 附光盘1张 印 数:1~3000 定 价:59.00 元 产品编号:050822-01 前 言 在编写《Ext JS 权威指南》的时候,曾考虑过是否以相同形式写一本《Sencha Touch 权威 指南》。在《Ext JS 权威指南》出版后,发现《Sencha Touch 权威指南》也准备出版了,于是 打消了这个念头。在此,非常感谢王金柱编辑,他极力鼓励我写一本实战的书,于是就有了本 书的诞生。 在目前的市面上,与 Ext JS 的书全面开花不同,关于 Sencha Touch 方面的书,中文版本 的就只有《Sencha Touch 权威指南》一本。而同样也是移动开发框架的 jQuery Mobile 的中 文书,也有好几本了。不知道是推广问题,还是知名度给 Ext JS 掩盖了,在国外比较热的 Sencha Touch 框架,在国内居然比较冷。或许是中文资料的匮乏,也是一个原因吧。因而, 本书希望能作为一种补充,增加大家对 Sencha Touch 的了解与深入学习如何使用 Sencha Touch 进行开发。 Sencha Touch 其实是与 Ext JS 同架构的框架,会使用 Ext JS 进行开发,基本上就可以使 用 Sencha Touch 进行开发。因而,作为一名 Ext JS 开发人员,学习使用 Sencha Touch 的开发, 可算得上是锦上添花。尤其是在当前移动应用开发比较热的情况下,拥有这样的技能,也算是 进入移动开发领域不错的选择。 希望本书能作为一本抛砖引玉的书,能为大家开发移动领域的应用程序提供一把钥匙。 本书的开发环境 本书的开发环境主要包括以下几点:  操作系统:Window 7  Web 服务器:Window 7 自带的 IIS  开发工具: UltraEdit  浏览器:Chorme 27.0.1453.116 m 为了能不受系统自身环境的影响,在使用 Sencha Cmd 和安卓虚拟机的时候,专门使用虚 拟机搭建了一个干净的 Window 7 系统进行安装和使用。如果在本机安装和使用 Sencha Cmd 和安卓虚拟机碰到处理不了的错误的时候,建议也使用虚拟机搭建一个干净的 Window 7 系统 再进行安装和使用,以避免自身环境的影响。 本书面向的读者 本书主要是以示例的形式来介绍 Sencha Touch 的开发过程和组件的使用方法,实用性比 较强,因而适合那些希望立刻就能上手使用 Sencha Touch 进行开发的读者。当然,对于已经 Sencha Touch 实战 ·II· 熟悉 Sencha Touch 开发的老手来说,本书也可作为参考。 如何阅读本书 本书是一本实战性很强的书,因而,在阅读的时候,希望读者能搭建好相应的平台,边阅 读边实践,在完成本书阅读的同时,也亲身体验一下 Sencha Touch 的开发过程。 联系作者 希望本书能给每位读者带来一点收益,如果对本书有任何意见和建议,或者有任何技术上 的问题,请与笔者联系。笔者非常希望得到大家的意见和建议以提高创作水平,非常乐意和大 家一起探讨和分享有关 Sencha Touch 开发上的问题,甚至更广泛的 Web 开发问题。如果想联 系笔者,请发邮件到 huangdengqiao@outlook.com,或者发消息到微博 http://weibo.com/gerneal。 如果想了解最新的 Ext JS 和 Sencha Touch 动态,或者笔者的最新博文,可访问笔者的博客 http://blog.csdn.net/tianxiaode 或 http://dqhuang.blog.51cto.com/。 致谢 在本书的出版过程中,得到了清华大学出版社编辑的大力支持,尤其是王金柱编辑,正是 在他的鼓励和支持下,才完成了本书的创作,在此,再次表示衷心的感谢。 编者 2013.10 目 录 第 1 章 Sencha Touch 概述 .................................................................................................1 1.1 Sencha Touch 简介.....................................................................................................1 1.1.1 发展历史 ........................................................................................................1 1.1.2 下载地址 ........................................................................................................2 1.1.3 关于许可协议 ................................................................................................3 1.1.4 Sencha Touch 包的内容.................................................................................4 1.2 API 文档.....................................................................................................................4 1.2.1 使用在线 API 文档........................................................................................4 1.2.2 使用本地 API 文档........................................................................................7 1.3 本书的一些术语 ........................................................................................................7 1.4 一些常见的配置项 ....................................................................................................8 1.5 类的命名规则 ............................................................................................................8 1.6 JSON ..........................................................................................................................9 1.7 关于调试..................................................................................................................10 1.8 小结..........................................................................................................................10 第 2 章 MVC模式 .............................................................................................................. 11 2.1 MVC 模式概述........................................................................................................ 11 2.2 组件查询的机制 ......................................................................................................12 2.2.1 组件管理器:Ext.ComponentManager .......................................................12 2.2.2 组件的查询方式 ..........................................................................................14 2.2.3 直接使用 id 查询组件 .................................................................................16 2.2.4 组件中的查询 ..............................................................................................17 2.3 控制器......................................................................................................................19 2.3.1 模型(models)...........................................................................................20 2.3.2 stores.............................................................................................................20 2.3.3 视图(views).............................................................................................21 2.3.4 引用(refs) ................................................................................................21 2.3.5 控制(control)...........................................................................................22 2.3.6 路由(routes).............................................................................................23 2.3.7 init 方法........................................................................................................24 Sencha Touch 实战 ·IV· 2.3.8 launch 方法...................................................................................................24 2.4 小结..........................................................................................................................24 第 3 章 第一个应用程序 .....................................................................................................25 3.1 Sencha Cmd v3.........................................................................................................25 3.1.1 下载 ..............................................................................................................25 3.1.2 安装 ..............................................................................................................26 3.1.3 使用 Sencha Cmd 来生成第一个应用程序 ................................................28 3.2 FirstApp 应用程序...................................................................................................33 3.2.1 目录结构 ......................................................................................................34 3.2.2 首页(index.html) .....................................................................................35 3.2.3 配置文件:app.json.....................................................................................39 3.2.4 应用程序启动文件:app.js .........................................................................44 3.2.5 Ext.viewsport 对象.......................................................................................47 3.2.6 样式文件:app.css.......................................................................................47 3.3 存在的问题..............................................................................................................47 3.4 效果测试..................................................................................................................48 3.5 应用程序的生成 ......................................................................................................54 3.6 打包应用程序 ..........................................................................................................59 3.6.1 打包配置文件:packager.json.....................................................................59 3.6.2 打包 ..............................................................................................................65 3.7 小结..........................................................................................................................77 第 4 章 主界面与布局.........................................................................................................78 4.1 第一个应用程序的主界面与布局 ..........................................................................78 4.2 布局..........................................................................................................................80 4.2.1 卡片布局:Ext.layout.Card .........................................................................80 4.2.2 停靠布局:Ext.layout.wrapper.BoxDock、Ext.layout.wrapper.Dock........96 4.2.3 自适应布局:Ext.layout.Fit.........................................................................97 4.2.4 盒子布局:Ext.layout.HBox、Ext.layout.VBox ........................................97 4.3 界面布局中常用的组件 ..........................................................................................97 4.3.1 容器:Ext.Container ....................................................................................97 4.3.2 标题栏:Ext.TitleBar...................................................................................98 4.3.3 分段按钮:Ext.SegmentedButton ...............................................................98 4.3.4 导航视图:Ext.navigation.viewsviews .......................................................99 4.3.5 标签面板:Ext.tab.Panel .............................................................................99 4.3.6 滑动视图:Ext.carousel.Carousel .............................................................102 4.4 小结........................................................................................................................107 目 录 ·V· 第 5 章 数据层..................................................................................................................108 5.1 概述........................................................................................................................108 5.2 Ext.Ajax..................................................................................................................108 5.3 跨域问题................................................................................................................ 112 5.4 创建模型................................................................................................................ 113 5.4.1 字段:Ext.data.Field.................................................................................. 114 5.4.2 定义 id 值 ................................................................................................... 115 5.4.3 可以自动生成 id 的类 ............................................................................... 115 5.4.4 添加验证:Ext.data.validations................................................................. 116 5.4.5 模型之间的关系 ........................................................................................ 117 5.5 代理........................................................................................................................ 118 5.5.1 Ext.data.reader.Reader ................................................................................ 119 5.5.2 Ext.data.writer.Writer..................................................................................123 5.5.3 扩展代理以实现格式化、标准化、统一化.............................................125 5.6 store ........................................................................................................................126 5.6.1 创建 store....................................................................................................126 5.6.2 加载本地数据 ............................................................................................128 5.6.3 数据操作 ....................................................................................................129 5.6.4 自动同步 ....................................................................................................134 5.6.5 排序 ............................................................................................................134 5.6.6 中文排序的问题 ........................................................................................135 5.6.7 过滤 ............................................................................................................136 5.6.8 分组 ............................................................................................................136 5.6.9 分页 ............................................................................................................136 5.6.10 树状数据 ..................................................................................................137 5.7 小结........................................................................................................................137 第 6 章 模板与数据视图 ...................................................................................................138 6.1 基本模板:Ext.Template.......................................................................................138 6.2 高级模板:Ext.XTemplate....................................................................................140 6.2.1 自动填充功能 ............................................................................................141 6.2.2 使用判断语句 ............................................................................................142 6.2.3 使用子模板 ................................................................................................143 6.3 数据视图:Ext.dataview.Dataviews......................................................................144 6.3.1 基本的数据视图使用 ................................................................................144 6.3.2 多选模式 ....................................................................................................146 6.3.3 带组件的数据视图 ....................................................................................146 6.4 选择器:Ext.picker.Picker.....................................................................................153 Sencha Touch 实战 ·VI· 6.4.1 基本用法 ....................................................................................................154 6.4.2 没有工具条的选取效果 ............................................................................156 6.4.3 多插槽的选择器 ........................................................................................157 6.5 列表:Ext.dataview.List ........................................................................................167 6.5.1 基本用法 ....................................................................................................167 6.5.2 分组的列表 ................................................................................................170 6.5.3 使用索引条的列表 ....................................................................................171 6.5.4 具有 Disclosure 功能的列表 .....................................................................172 6.5.5 翻页插件:Ext.plugin.ListPaging .............................................................173 6.6 嵌套列表:Ext.dataview.NestedList .....................................................................175 6.7 小结........................................................................................................................180 第 7 章 表单 .....................................................................................................................181 7.1 表单面板................................................................................................................181 7.1.1 setValues 和 getValues 方法.......................................................................181 7.1.2 setRecord、updateRecord 和 getRecord 方法...........................................184 7.1.3 表单的提交 ................................................................................................185 7.2 在表单内对元素进行分组:Ext.form.FieldSet....................................................187 7.3 表单字段................................................................................................................187 7.3.1 文本字段:Ext.field.Text...........................................................................188 7.3.2 数字字段:Ext.field.Number.....................................................................189 7.3.3 多行文本字段:Ext.field.TextArea...........................................................189 7.3.4 隐藏字段:Ext.field.Hidden......................................................................189 7.3.5 密码字段:Ext.field.Password ..................................................................189 7.3.6 电子邮件字段:Ext.field.Email ................................................................190 7.3.7 网址字段:Ext.field.Url ............................................................................190 7.3.8 搜索字段:Ext.field.Search.......................................................................190 7.3.9 复选字段:Ext.field.Checkbox .................................................................190 7.3.10 单选字段:Ext.field.Radio ......................................................................191 7.3.11 微调字段:Ext.field.Spinner ...................................................................191 7.3.12 滑块字段:Ext.field.Slider ......................................................................192 7.3.13 切换字段:Ext.field.Toggle.....................................................................193 7.3.14 日期选择字段:Ext.field.DatePicker......................................................193 7.3.15 选择字段:Ext.field.Select......................................................................194 7.4 记事本....................................................................................................................199 7.5 小结........................................................................................................................214 目 录 ·VII· 第 8 章 其他的一些常用组件............................................................................................215 8.1 面板:Ext.Panel.....................................................................................................215 8.2 工具栏:Ext.Toolbar .............................................................................................217 8.2.1 按钮:Ext.Button.......................................................................................217 8.2.2 在工具栏的组件之间设置空白间隔:Ext.Spacer ...................................219 8.3 信息窗口:Ext.MessageBox .................................................................................220 8.4 操作列表:Ext.ActionSheet..................................................................................220 8.5 图片:Ext.Img .......................................................................................................223 8.6 动画功能:Ext.Anim.............................................................................................223 8.7 小结........................................................................................................................224 第 9 章 音频和视频 ..........................................................................................................225 9.1 音频:Ext.Audio....................................................................................................225 9.2 视频:Ext.Video ....................................................................................................226 9.3 “我的音乐”播放器 ............................................................................................226 9.3.1 前期工作 ....................................................................................................226 9.3.2 创建模型和 store........................................................................................226 9.3.3 设计主界面 ................................................................................................232 9.3.4 “歌手”标签页 ........................................................................................233 9.3.5 分类标签页 ................................................................................................240 9.3.6 播放列表标签页 ........................................................................................241 9.3.7 播放标签页 ................................................................................................244 9.4 小结........................................................................................................................258 第 10 章 原生 API ............................................................................................................259 10.1 原生 API 概述......................................................................................................259 10.2 获取设备连接信息和设备信息 ..........................................................................259 10.3 使用通知..............................................................................................................260 10.4 获取通讯录..........................................................................................................261 10.5 SQLite ..................................................................................................................261 10.6 相机功能..............................................................................................................261 10.6.1 基本用法 ..................................................................................................262 10.6.2 上传图片 ..................................................................................................264 10.7 获取地理位置 ......................................................................................................266 10.8 获取方向变化 ......................................................................................................267 10.9 小结......................................................................................................................269 Sencha Touch 实战 ·VIII· 第 11 章 地图 ...................................................................................................................270 11.1 创建应用程序 ......................................................................................................270 11.2 基本配置 ..............................................................................................................270 11.3 地图的设置 ..........................................................................................................271 11.4 定位 ......................................................................................................................273 11.5 搜索地点功能 ......................................................................................................275 11.6 更多的功能 ..........................................................................................................279 11.7 小结 ......................................................................................................................280 第 12 章 多配置的应用程序..............................................................................................281 12.1 基本流程..............................................................................................................281 12.2 定义配置文件 ......................................................................................................281 12.3 配制 Phone 界面 ..................................................................................................283 12.4 配置 Tablet 主界面 ..............................................................................................283 12.5 完成新增功能 ......................................................................................................288 12.6 编辑和删除功能 ..................................................................................................290 12.7 小结......................................................................................................................292 第 13 章 综合应用——创建“我的商店”应用程序 .........................................................293 13.1 京东商城的主要界面 ..........................................................................................293 13.2 创建应用程序 ......................................................................................................294 13.3 主界面..................................................................................................................294 13.4 首页标签页..........................................................................................................295 13.4.1 滑动视图第一页 ......................................................................................297 13.4.2 产品列表视图 ..........................................................................................303 13.4.3 产品信息视图 ..........................................................................................312 13.4.4 掌上秒杀列表 ..........................................................................................326 13.4.5 功能按钮 ..................................................................................................326 13.4.6 搜索按钮 ..................................................................................................335 13.4.7 “逛”功能 ..............................................................................................335 13.4.8 滑动视图第二页 ......................................................................................338 13.5 搜索标签页..........................................................................................................340 13.6 分类标签页..........................................................................................................357 13.7 购物车..................................................................................................................367 13.8 “我的商店”标签页 ..........................................................................................376 13.9 “更多”标签页 ..................................................................................................386 13.10 小结....................................................................................................................386 第 10 章 原生 API 现在,相机已经成为移动设备的标准配置了,无论在何时何地,几乎都会看到人们举起手 机在拍照。对 于 流行的框架,自然不能缺少该部分的应用。本章将介绍如何在应用程序使用相 机等原生 API。 10.1 原生 API 概述 一般来说,使用 Sencha Touch 创建的、基于 Web 的应用程序,出于安全原因,是不允许 访问设备的,因而,要访问原生 API 就必须将应用程序打包成本地应用程序,像原生应用程 序一样,通过 API 来访问设备。 在 Sencha Touch 中,提供了该功能。在 2.2 版本中,提供了获取设备连接信息 ( Ext.device.Connection )、 获取设 备信息( Ext.device.Device )、 获取设 备 通 讯 录 ( Ext.device.Contacts )、使用相机功 能(Ext.device.Camera )、 获取地 理 位置信息 ( Ext.device.Geolocation )、 获取方 向变化 ( Ext.device.Orientation )、 使 用 通 知 功 能 (Ext.device.Notification)和使用本地 SQLite(Ext.device.SQLite)等功能。 这里要清楚一点,要使用 Sencha Touch 提供的原生 API,就必须将应用程序打包为本地应 用程序。将应用程序打包成本地应用程序的方法在 3.6 节已经介绍过了。 这些原生设备的类都是单例模式的,也就是说,是实例,不再需要使用 new 或 Ext.create 来创建实例。要使用原生 API 功能,调用这些类的属性和方法就可以了。 10.2 获取设备连接信息和设备信息 Ext.device.Connection 和 Ext.device.Device 的主要功能是获取设备的信息,对于实际的应 用程序来说,使用量不大,不过用来演示原生 API 是不错的选择。下面使用第一个应用程序 来演示基本的原生 API 的使用。示例文件可在随书光盘\ ch10\FirstApp 下找到。 首先要做的是把 index.html、app.js 和 main.js 另存为 UTF-8 格式。在脚本文件(js 文件) 的顶部加上以下代码: //@charset UTF-8 这段代码的作用是让 Sencha Cmd 以 UTF-8 的编码格式去编译文件。如果不加入这段代码, 编译后应用程序中的中文都是乱码。 下面打开主界面 Main.js,把第二个标签页去掉,将第一个标签页内的 html 配置项修改为 Sencha Touch 实战 ·260· 以下代码: html: [ '连接状态:'+ ( Ext.device.Connection.isOnline() ? '在线' : '离线'), '连接类型:' + Ext.device.Connection.getType(), '设备名称: ' + Ext.device.Device.name, '设备平台: ' + Ext.device.Device.platform, '设备的 UUID: ' + Ext.device.Device.uuid ].join("
") 代码中使用到了 Ext.device.Connection 和 Ext.device.Device 的一些基本功 能。 Ext.device.Device 的 isOnline 方法可用来返回设备网络连接的状态,值为 true,说明设备在线, 否则,为离线状态。方法 getType 则用来返回网络连接的类型,如果设备状态为离线,则返回 null 值。不要忘记在 requires 中加入对 Ext.device.Connection 和 Ext.device.Device 的引用。 代码完成后,就可使用 Sencha Cmd 进行编译了,具体方法可参阅 3.6 节。编译后,把应 用程序安装到安卓虚拟机并运行,将看到如图 10-1 所示的效果。 不要尝试在浏览器中打开带有原生 API 的应用程序,会提示错误的。由于这个原 因,要调试带有原生 API 的应用程序比较困难。 图 10-1 使用原始 API 获取设备信息和连接信息 10.3 使用通知 通过 Ext.device.Notification 可以使用原生的通知功能,如信息提示框和震动功能。要显示 信息提示框,可使用 show 方法,该方法与使用 Ext.MessageBox 的 show 方法区别不大。要使 原生 API 第10章 ·261· 用震动功能,则需要调用 vibrate 方法。 这里说明一下,震动功能在虚拟机上调试不了,因而需要直接在设备上进行调试。 10.4 获取通讯录 使用 Ext.device.Contacts 可以获取设备上的通讯录,一般要配合 store 来使用。调用 getContacts 方法就可以返回通讯录中的数据了。Ext.device.Contacts 只能获取数据,不能写入 数据,因而使用会受到一定的限制。 在使用前,还需要自己了解返回的数据结构,这也带来了一点不方便。在安卓虚拟机上, 获取不了数据,因而演示不了。大家如果有兴趣开发与通讯录相关的应用,可以根据 API 的 示例代码在虚拟机上进行调试,这个不算太困难。 10.5 SQLite Ext.device.SQLite 是在版本 2.2.0 中加入的,因为是刚加入的,所以文档不完整,具体用 法还待研究。 10.6 相机功能 通过 Ext.device.Camera 可以使用设备的相机功能,是非常实用的功能。 要使用相机功能,调用 Ext.device.Camera 的 capture 方法就可以了,该方法的第一个参数 为配置对象,主要的配置项包括以下几种。  success:照相成功后的回调函数。该回调函数的唯一参数是一个字符串,该字符串可 以是通过 base64 编码的图像字符串,或者是图片的地址,这取决于配置项 destination 的定义,destination 为 data 时,为 base64 编码的字符串,为 file 时,为图片地址。  failure:发生错误时的回调函数。  quality:返回回调函数的图片品质,值为百分比值。  source:获取照片的源,值可以是 album(提示用户从专辑中选择图片)、camera(提 示用户照相)和 library(提示用户从库中选择图片)。  scope:作用域。  destination:返回的图片的形式,值可以是 data(base64 编码的字符串)和 file(图片 文件的地址)。  encoding:图片的编码格式,值可以是 jpg 或 png。  width:图片的宽度。  height:图片的高度。 Sencha Touch 实战 ·262· 10.6.1 基本用法 下面通过一个简单的示例来了解相机的使用方法,该示例是在 10.2 节的示例上修改而成 的。下面切换到主界面 Main.js 中,添加标题为“相机”的标签页,代码如下: { title: '相机', scrollable: true, } 因为不知道需不需要滚动,因而最好加上滚动条。 在标签页内的顶部显示三个按钮,用来测试从三个不同的源获取图片的方式。在 主 区域放 置一个图片组件,用来显示获取的图片。在 图 片组件下放一个多行文本,用来显示获取的图片 数据。具体的代码如下: { xtype: 'toolbar' , docked: 'top', items:[ { text: 'album+data', }, { text: 'camera+data', }, { text: 'library+file', } ] }, { xtype: 'image', id: 'myimage', width:200, height:200, margin:'10px auto', src: 'resources/icons/no-photo.jpg', }, { 原生 API 第10章 ·263· xtype: 'textareafield', id: 'mytextarea', label: '返回值' } 通过按钮的文本就可以知道,第一按钮将从 album 中选择图片,而返回的数据格式是 base64 格式的字符串;第二个按钮将从 camera 中获取图片,数据格式与第一个按钮一样;第 三个按钮将从 library 中选择图片,而返回的数据将是图片的地址。 图片组件和多行文本字段都添加了 id 配置项,这样可方便查找按钮。 下面来完成第一个按钮的操作,代码如下: handler: function(cmp){ Ext.device.Camera.capture({ success: function(image) { Ext.getCmp('mytextarea').setValue(image); Ext.getCmp('myimage').setSrc('data:image/png;base64,'+image); }, failure: function(){ Ext.getCmp('mytextarea').setValue(arguments.toString()); }, quality: 75, source: 'album', destination: 'data', width:200, height: 200, encoding: 'png' }); } 单击按钮后,直接调用 Ext.device.Camera 的 capture 方法。在配置对象中,定义了 success 回调函数,在函数内,会使用 setValue 方法将返回的数据显示在多行文本字段内。要显示图片, 必须加上前缀“data:image/png;base64,”,这个前缀表示 Data URI scheme1格式的一种,表示是 base64 格式的 png 图片。 在配置对象中还定义了 failure 回调函数,用来显示错误信息,由于 API 中没有具体的参 数,因而这里只好使用 arguments 对象,并将它转换为字符串显示到多行文本字段中。 在配置对象中,余下的配置项还包括:返回图片的质量将是原图片的 75%,图片来源是 album,返回的数据格式是 data,宽度和高度都是 200,因为这里使用了 png 格式,因而要求 图片的返回格式必须是 png。 1 详细信息可参阅 http://en.wikipedia.org/wiki/Data_URI_scheme。 Sencha Touch 实战 ·264· 单击第二个按钮的操作与第一个按钮的操作类似,复制过来就行了,需要修改的地方包括: 将“data:image/png;base64,”中的 png 修改为 jpeg,将 source 的值修改为 camera,将 encoding 的值修改为 jpg(删除也可以,默认是 jpg 格式的)。 第三个按钮要修改的地方包括:将图片的前缀去掉,因为返回的是图片的地址。将 source 的值修改为 library,将 destination 的值修改为 file,并删除 encoding 配置项。 下面要做的就是编译应用程序,并安装到虚拟机上。在虚拟机上依次单击三个按钮后,将 看到如图 10-2 所示的效果。从图中可以看到,以 file 格式返回会产生 User has canceled operation 的错误,因而,大家在使用的时候还是先测试好再考虑使用该方法的功能。 图 10-2 从不同的源获取图片的效果 10.6.2 上传图片 使用相机功能最多的当然是分享图片了,因而,怎么上传图片是需要考虑的问题。由于 iOS 的 Safari 不支持上传2,因而在 Sencha Touch 中并没有提供文件字段,需要想办法解决。 余下的方法可以自己写 HTML 代码,加入到表单中,或者使用 HTML 5 的文件上传功能组件。 不过这些方式如果在 Safari 中还是不起作用,就需要用其他的方法解决。 前面已经讲过,可以让图片以数据的形式返回,而这样的数据使用 Ajax 提交是绝对没 有问题的。那么,在后台怎么处理这样的数据呢?方法一是直接将数据保存到数据库中,使 用的时候直接将数据绑定到 IMG 元素的 src 属性就可以了,不过这要考虑数据的字段长度问 题,图片大的话,长度还是很可观的。方法二就是将 base64 编码再转换成图片就可以了, 这个简单。 下面演示如何使用方法二来上传图片。 2 详细信息请参阅 http://www.sencha.com/forum/showthread.php?182932。 原生 API 第10章 ·265· 复制 10.6.1 节按钮 3 的定义代码,粘贴为第 4 个按钮,将 text 配置项的值修改为“上传图片”, 将 destination 的值修改为 data。最后修改 capture 方法内的 success 回调函数的代码为以下代码: Ext.Ajax.request({ url: 'http://192.168.0.100:8080/data/upload.asp', params:{data: image}, success:function(response, opts){ try{ var obj = Ext.decode(response.responseText); if(obj.success){ Ext.getCmp('mytextarea').setValue(obj.data); Ext.getCmp('myimage').setSrc('http://192.168.0.100:8080/'+obj.data); }else{ Ext.getCmp('mytextarea').setValue(obj.msg); } }catch(err){ Ext.getCmp('mytextarea').setValue(response.responseText); } }, failure:function(response, opts){ Ext.getCmp('mytextarea').setValue(response.responseText); } }); 代码在图片返回后会直接调用 Ext.Ajax 的 request 方法提交图片。这里一定要注意提交的 地址,因为应用程序要编译成本地应用程序,因而一定要用绝对地址标明把数据提交到哪里。 由于在虚拟机上调试比较困难,因而要尽可能地添加调试代码,并把错误显示到视图中。 成功提交后,可返回相对地址,也可返回绝对地址。代码中,返回的是相对地址,所以要 加上域名才能找到图片。一定要记住,图片是上传到服务器端的,并不在本地,因而要添加服 务器的访问地址或域名。 由于开发 Web 应用程序习惯使用多行文本字段显示错误信息,因而这里也使用了 这种方式,试过才知道这绝对是个错误做法,要想看到完整错误信息,在虚拟机 上很难实现,因而最好的方式是将多行文本修改为容器,使用 setHtml 方法在页 面中直接查看会方便很多。 还有就是,记得检查保存目录是否能保存文件,最好是先不用原生 API,调试好 能正确上传文件后再在虚拟机上调试。没有 base64 编码的图片?直接用图片生成 编码就好了,这个简单。 Sencha Touch 实战 ·266· 编译并将应用程序安装到虚拟机,然后运行应用程序,并上传一个文件,将看到如图 10-3 所示的效果。屏幕实在太小,显示不到第四个按钮。 图 10-3 上传图片后的效果 10.7 获取地理位置 使用 Ext.device.Geolocation 可以获取当初所处的地理位置,还可跟踪位置的变化。在 Ext.device.Geolocation 中,getCurrentPosition 方法用来返回当前位置,而 watchPosition 用于跟 踪位置的变化。 方法 watchPosition 的主要配置项包含以下几个。  allowHighAccuracy:在移动设备中,有蜂窝、WiFi 和 GPS 等三种定位技术可用,其 中蜂窝和 WiFi 定位技术精度低,但节能;而 GPS 技术精度高,但耗能。因而,在低 精度能满足要求的情况下,一般会使用蜂窝和 WiFi 技术来定位,以便节能。如果应 用程序需要高精度定位,则需要通过设置来改变。在 Ext.device.Geolocation 中,可以 使用 allowHighAccuracy 配置项来获取高精度的位置信息,该配置项默认值为 false, 不使用高精度;如果需要,可以将该配置项设置为 true。  frequency:使用 watchPosition 方法获取位置信息的频率,默认值为 10000,也就是 10 秒获取一次。  maximumAge:一般情况下,位置信息会从缓存中获取,该配置项的作用就是控制缓 存的位置信息的更新时间不能迟于该配置项指定的时间,例如,该配置项的值为 10, 则表示返回的位置信息的更新时间不能迟于 10s。该配置项的默认值为 0,也就是不从 缓存返回位置信息,而是返回即时的位置信息。  timeout:位置信息更新的超时时间。 原生 API 第10章 ·267·  success:返回位置信息成功后的回调函数。  failure:返回位置信息发生错误后的回调函数。 10.8 获取方向变化 使用 Ext.device.Orientation 可以获取移动设备的方向变化,这对于某些应用程序是非常有 用的,如赛车游戏,就可根据方向的变化,计算出游戏中赛车的左右平移的距离,从而能控制 赛车的方向。那么这些方向是怎么定义的呢?具体的详细信息可参阅 W3C 的文档 《DeviceOrientation Event Specification》3。这里根据文档中的图做简单的说明。 首先是关于坐标系的定义,如图 10-4 所示。在图 10-4 中,以水平放置的设备的中心点为 原点,沿 XYZ 三个方向构成三维坐标系。以该坐标系为基准,当设备在这三个方向上发生改 变时,就会产生三个变化值 alpha、beta 和 gamma。而这三个值可以在 Ext.device.Orientation 绑定的 orientationchange 事件中通过返回事件对象的属性获取,应用程序就可根据这三个返回 值进行换算,实现功能。 图 10-4 移动设备的坐标系 那么,alpha、beta 和 gamma 这三个值分别代表什么呢?如图 10-5 所示,当设备绕 z 轴逆 时针旋转时,它的偏转角度就是 alpha 的值,范围是 0~360。如图 10-6 所示,当设备绕 x 轴逆 时针旋转时,它的偏转角度就是 beta 的值,范围是-180~180。如图 10-7 所示,当设备绕 y 轴 逆时针旋转时,它的偏转角度就是 gamma 的值,范围是-90~90。 3 文档访问地址:http://dev.w3.org/geo/api/spec-source-orientation.html#deviceorientation。 Sencha Touch 实战 ·268· 图 10-5 设备绕 z 轴逆时针旋转时偏转的角度即为 alpha 值 图 10-6 设备绕 x 轴逆时针旋转时偏转的角度即为 beta 的值 图 10-7 设备绕 y 轴逆时针旋转时偏转的角度即为 gamma 的值 现在已经知道了 alpha、beta 和 gamma 返回的是角度值,因而要将它转换为距离值,还需 要进行一系列的三角运算。 原生 API 第10章 ·269· 10.9 小结 在本章中,介绍部分原生 API 的使用方法,尤其是关于相机的一些基本使用方法,希望 能有助于大家的开发。因为 API 需要转换,因而在框架这方面会有不少错误,在开发的时候 一定要注意。因为调试起来太困难,所以要开发需要使用原生 API 的应用程序时,要考虑清 楚。在下一章将绍 Sencha Touch 的地图功能。 第 11 章 地图 在移动设备上使用最多的,除了相机、多媒体功能外,就是地图功能了。要 Sencha 公司 自己去开发一套全新的地图功能加入到 Touch 框架中,目前来说可能性不大,因而,最好的办 法当然是利用互联网上可以使用的地图 API 来实现,这就是谷歌地图 API 了。 11.1 创建应用程序 先使用 Sencha Cmd 命令创建名为 MyMaps 的应用程序。本示例程序在光盘\ ch11\MyMaps 目录下。 应用程序创建后,按照以下步骤完成前期工作: 使用已经修正了中文排序等错误的汉化框架源代码覆盖 touch 目录下的文件。 将 index.html、app.js 和 Main.js 等文件另存为 UTF-8 格式,以避免中文乱码。 修改 index.html,将 title 修改为“我的地图”。 根据应用程序去调整 app.json 的配置,在该项目中不需要。 替换项目中的图标和图片,在该项目中也不需要。 11.2 基本配置 要使用谷歌地图的 API,首先要引用。打开 index.html 文件,在 HEAD 部分加入以下代码 引用谷歌地图的 API: 现在打开主界面 Main.js 文件,将扩展修改为容器。将 requires 中的 Ext.Video 删除,加入 对工具栏和 Ext.Map 的引用,然后将配置项 tabBarPosition 删除。将 items 里面的标签页全部 删除,并加入以下代码: items: [ { xtype: 'toolbar', docked: 'top' }, { 地图 第11章 ·271· xtype: 'map' } ] 在这里,定义一条工具栏,停靠在容器顶部,然后是定义了一个 Ext.Map 组件。如果现在 去测试页面加载应用程序,将看不到任何东西,原因是 Ext.Map 组件没有高度。为了能让 Ext.Map 组件填满整个屏幕,因而需要在容器内使用自适应布局,在 items 配置项上添加“layout: 'fit',”后,在测试页面加载应用程序将看到如图 11-1 所示的效果。从图中可以看到,现在的地 点离我们有点远,因而最好能显示附近的位置。 图 11-1 地图应用的效果 11.3 地图的设置 要将当前所在位置设置为就近位置,就需要使用定位服务器去获取当前位置,但使用该服 务需要时间,如果网络条件不好,还可能获取不到,因而,最有效的方法是读取上次的位置。 在应用程序第一次运行的时候,上 一 次的当前位置是肯定没有的,因而需要找一个初始点,在 这里设置为广州(23.166876,113.263321)。 如何使用配置,在前面的章节已经介绍了,这里只需要把模型和 store 复制到相应的目录 就行了。 接下来要考虑是什么时候将当前位置设置为保存的位置。这 里有两个选择,一个是在初始 化地图之前,将它作为配置设置好;另一个是等待谷歌地图完全初始化后,再调用方法来调整。 第一种方法需要在初始化子组件之前处理,可以选择在 beforeInitialize 方法内处理。第二种方 Sencha Touch 实战 ·272· 法要通过监听 painted 事件来实现。由于第二种方法有一个跳转的过程,不是最好的选择,因 而最好的方式还是第一种方法。 下面根据 9.2.7 节的方法,先在主界面的 config 配置对象内添加两个属性,代码如下: settingsstore: null, currentPosition: null 这两个属性的目的就是从“设置”store 中取得当前位置的值。 属性 currentPosition 的 updater 方法与 loop 属性差不多,复制过来修改关键字就行了。属 性 currentPosition 的关键字是 CurrentPosition。 接下来,先把 config 配置对象中的地图组件的定义删除,因为要将这修改到 beforeInitialize 方法内处理。 现在,在主界面中添加 beforeInitialize 方法,代码如下: beforeInitialize: function(){ var me = this; me.setSettingsstore(Ext.getstore('Settings')); var store= me.getSettingsstore(), position = store.getById('CurrentPosition'); if(!position){ me.setCurrentPosition({ latitude: 23.166876, longitude: 113.263321 }); }else{ me.setCurrentPosition(position.data.value); } position = me.getCurrentPosition(); me.add({ xtype: 'map', mapOptions:{ center : new google.maps.LatLng(position.latitude, position.longitude) } }) } 在代码中先设置了属性 settingsstore 的值,然 后从 store 寻找当前位置值,如果不存在,就 调用 setCurrentPosition 方法设置默认位置。这 里使用对象来保存经纬度,参考了 setMapCenter 方法的格式,符合习惯的格式。如果“设置”store 中已经存有当前位置值,就将该值赋值给 currentPosition 属性。接下来调用 add 方法将地图组件添加到容器内。 地图 第11章 ·273· 在地图组件的定义中使用了 mapOptions 配置项,这与谷歌地图 API 中的地图选项1是一样 的。这里使用 center 选择,也就是设置地图的中心点,它的值为 google.maps.LatLng 对象的实 例,创建实例时,需要把纬度和经度作为参数传递给它。 还有两个常用的选项是 zoom 和 mapTypeId。选项 zoom 用来设置地图的缩放级别,默认 的级别是 12,在 这里,将它添加到定义中,并设置为 16。选项 mapTypeId 用来设置地图类型, 值可以是 ROADMAP(默认值,普通的 2D 地图)、SATELLITE(拍摄的地图)、HYBRID(带 地图项的拍摄地图)和 TERRAIN(带自然地形的地图),在这里,将该选项添加到定义中, 并设置值为 google.maps.MapTypeId.HYBRID,前 缀 google.maps.MapTypeId 是必须的,这其实 相当于 C 语言中的枚举值。 现在在测试页面加载应用程序,将看到如图 11-2 所示的效果。 图 11-2 设置了中心点、缩放等级和地图类型的谷歌地图 11.4 定位 要获取当前位置,可以使用上一章介绍的设备定义功能,还可以使用 W3C 定义的 《Geolocation API Specification》2。W3C 定义的规范已经封装在 Ext.util.Geolocation 对象中, 该对象在使用之前需要实例化。下面来演示如何使用 Ext.util.Geolocation 定位。 在 beforeInitialize 方法 me.add 语句后面添加以下代码: me.geo = Ext.create('Ext.util.Geolocation'); me.geo.on('locationupdate',me.onLlocationupdate,me); 1 详细信息请参阅:https://developers.google.com/maps/documentation/javascript/tutorial?hl=zh-CN。 2 详细信息请参阅:http://dev.w3.org/geo/api/spec-source.html。 Sencha Touch 实战 ·274· me.geo.on('locationerror',me.onLocationerror,me); 代码为 Ext.util.Geolocation 的实例绑定了 locationupdate 和 locationerror 两个事件。当定位 信息有更新时会触发 locationupdate 事件,而更新发生错误的时候会触发 locationerror。 现在的情况下,Ext.util.Geolocation 的实例会自动不停地进行更新,是非常耗能的一个功 能,因而在做设计的时候要考虑这个情况。如果不希望自动更新,则可在创建实例时添加配置 项 autoUpdate,设置它的值为 false 来取消自动更新,又或者在创建实例后,调用 setAutoUpdate 来取消自动更新。 Ext.util.Geolocation 的其他一些配置项,如精度等,与设备的定位功能是一样的。 下面来完成 locationupdate 方法,代码如下: onLlocationupdate: function(geo){ this.setCurrentPosition({latitude: geo.getLatitude(), longitude: geo.getLongitude()}); }, 在代码中直接使用最新的位置信息更新了 currentPosition 属性。在 Ext.util.Geolocation 中, 需要使用 getLatitude 来返回更新的纬度,使用 getLongitude 方法返回更新的经度。 接着完成 locationerror 方法,代码如下: onLocationerror: function(geo, bTimeout, bPermissionDenied, bLocationUnavailable, message){ if(bTimeout){ console.log('timeout'); }else{ console.log(message); } } 目前的代码只作为调试使用,在实际使用的时候可根据具体情况将一些错误信息显示给用户。 现在,可以自动更新当前位置了,但用户正在应用程序时,怎么处理呢?如果进行实时更 新,当用户在浏览地图时,会打断用户当前的进程,因而最好的方式是在工具栏上放按钮,当 用户单击该按钮的时候将地图的中心点切换到当前位置。 下面在工具栏上添加一个按钮,代码如下: items:[ {iconCls: 'home'} ] 下一步要考虑的是在控制器中为按钮绑定 tap 事件,还是在类中直接定义 handler。为 了 多 加练习,这里选择控制器。在 app\controller 目录下创建名为 Main.js 的脚本文件,并加入控制 器的基本定义代码。 地图 第11章 ·275· 在控制器中,先为主视图和地图组件添加引用,代码如下: refs: { main: 'main', map: 'map' }, 因为这两个组件都只有一个,因而使用 xtype 搜索就行了。 下面为按钮绑定 tap 事件,代码如下: control: { 'button[iconCls=home]': { tap: 'onHomeTap' } }, 这里要利用按钮特有属性 iconCls 来查找按钮。 在 onHomeTap 事件中,要实现的是将屏幕的中心设置为属性 currentPosition 指向的坐标, 代码如下: onHomeTap: function(){ var me =this, map = me.getMap(), position = me.getMain().getCurrentPosition(); map.getMap().panTo(new google.maps.LatLng(position.latitude, position.longitude)); } 没有使用 setMapCenter 方法时该方法有时不起作用,因而这里直接使用谷歌地图的 API。 代码中,第一个 getMap 方法返回的是 Ext.Map 对象的实例,第二个 getMap 方法返回的 是 Ext.Map 对象实例中的谷歌地图对象,方 法 panTo 就是谷歌地图对象的方法。从这里可以了 解到,要使用好 Ext.Map,还是应先熟悉谷歌地图的 API。 好了,现在单击 Home 按钮就可以回到当前位置了。 11.5 搜索地点功能 下面要为地图添加搜索功能。谷歌地图 API 已经提供了非常完善的搜索功能,我们要做 的是怎么把谷歌地图 API 与 Sencha Touch 组件组合起来。在这里要实现的是通过单击搜索按 钮弹出搜索输入框,余下的就交给谷歌地图 API 处理。 因为要做弹出效果,所以要先从面板扩展出一个视图供按钮使用。在 app\view 目录下创 建 Search.js 的脚本文件,并加入以下定义代码: Sencha Touch 实战 ·276· Ext.define('MyMaps.view.Search', { extend: 'Ext.Panel', xtype: 'searchview', requires: [ 'Ext.field.Text' ], config: { items:[ {xtype: 'textfield', placeHolder: '请输入搜索文字', width: 200 } ] } }); 代码中,在面板里面定义了搜索输入框。接下来要做的是让这个搜索框与谷歌地图 API 关 联起来。根据谷歌地图 API 的要求,要使用文本字段的 INPUT 元素,而要获得 INPUT 元素, 只有在组件渲染后,要在 painted 事件或 show 事件中处理。在 这 里选择 painted 事件,代码如下: listeners:{ painted: { single: true, fn: 'onPainted' } } 代码规定了该事件只执行一次。 因为谷歌地图 API 还需要使用到谷歌地图对象,而该对象却在主视图内,最好的办法是 在 MyMaps.view.Search 内定义属性,在创建实例后,使用 setter 将属性与谷歌地图对象关联起 来。在监听事件的代码下添加 map 属性的定义,代码如下: map: null 接下来要完成的是 onPainted 方法,代码如下: onPainted: function(cmp, eOpts){ var me = this; input = me.down('textfield').getComponent().input.dom, searchBox = new google.maps.places.SearchBox(input), map = me.getMap(), markers = []; 地图 第11章 ·277· google.maps.event.addListener(searchBox, 'places_changed', function() { var places = searchBox.getPlaces(); for (var i = 0, marker; marker = markers[i]; i++) { marker.setMap(null); } markers = []; var bounds = new google.maps.LatLngBounds(); for (var i = 0, place; place = places[i]; i++) { var image = { url: place.icon, size: new google.maps.Size(71, 71), origin: new google.maps.Point(0, 0), anchor: new google.maps.Point(17, 34), scaledSize: new google.maps.Size(25, 25) }; var marker = new google.maps.Marker({ map: map, icon: image, title: place.name, position: place.geometry.location }); markers.push(marker); bounds.extend(place.geometry.location); } map.fitBounds(bounds); }); google.maps.event.addListener(map, 'bounds_changed', function() { var bounds = map.getBounds(); searchBox.setBounds(bounds); Sencha Touch 实战 ·278· }); } INPUT 元素的获取比较迂回,要先使用 down 方法找到文本字段,然后使用 getComponent 返回文本字段下与 INPUT 元素有关的对象,该对象下 input 属性指向的就是 INPUT 元素的 Ext.Element 对象,而 Ext.Element 对象的 dom 属性指向的就是 INPUT 元素的 DOM 节点了。 从 var 语句之后开始的代码,都是从谷歌地图 API 中复制过来的3。至于谷歌地图 API 为 什么要这样,已经超出了本书的范围,就不赘述了。 接下来,在主界面的工具栏上放置一个搜索按钮,代码如下: {iconCls: 'search'} 接着在主界面的控制器中为搜索按钮绑定 tap 事件,代码如下: 'button[iconCls=search]':{ tap: 'onSearchTap' } 在 onSearchTap 事件中,主要操作已经很熟悉了,就是判断搜索面板是否已经创建,如果 为否,就创建它,然后将其作为按钮的提示显示,代码如下: onSearchTap: function(cmp){ var me =this, view = me.searchview; if(!view){ view = Ext.create('MyMaps.view.Search'); view.setMap(me.getMap().getMap()); me.searchview = view; } var hidden = view.isHidden(); if (hidden || hidden === null) { view.showBy(cmp); }else{ view.hide(); } } 这里使用了 searchview 属性来指向搜索视图。创建视图后,调用 setMap 方法将搜索视图 的 map 属性指向了主视图的谷歌地图对象。这里记得要写两次 getMap 方法才能获取到谷歌地 3 详细信息请参阅:https://developers.google.com/maps/documentation/javascript/examples/places-searchbox?hl=zh-CN。 地图 第11章 ·279· 图对象,第一次获取的是 Ext.Map 对象。 如果在搜索视图内添加标题,然后添加关闭按钮来关闭搜索视图,那太难看了,因而,这 里采用了再次单击搜索按钮就隐藏搜索视图的办法。为了实现这个功能,就必须调用 isHidden 方法来返回视图的隐藏状态,如果视图是隐藏的,会返回 true,否则会返回 false。这里一定要 小心,由于 Sencha Touch 属性的怪异行为,在第一次调用 isHidden 方法的时候。会返回初始 值 null,因而在判断是否隐藏的时候,需要加上 null 的判断。如果是隐藏的或只是 null,则调 用 showBy 显示视图。否则,调用 hide 方法隐藏视图。 在开始测试前,还要修改谷歌地图的引用链接,因为要加入地点库,使用以下代码替换 index.html 中的代码: 现在,在测试页面加载示例,单击搜索按钮,并输入深圳,选择“中国广东省深圳宝安”, 将看到如图 11-3 所示的效果。由于输入框中的清除按钮把列表显示的宽度占用了,把清除按 钮去掉效果会好点,而更好的方式当然是修改谷歌地图 API 中列表层的宽度。 图 11-3 搜索地点功能 11.6 更多的功能 利用谷歌地图 API,还可以为地图应用程序添加更多的功能,要做得好,真的要发挥想象 力。在这只是起启发作用,如果想测试更多的功能,大家可以继续将这个示例完善下去,如添 加路线选择等功能。 Sencha Touch 实战 ·280· 11.7 小结 在本章,不但了解了如何使用 Sencha Touch 提供的地图功能,还了解了如何将谷歌地图 API 与 Sencha Touch 组件结合使用。使用 Ext.Map 其实不难,难的是要熟悉谷歌地图 API,需 要多花时间。在下一章,将介绍如何根据设备类型使用不同的配置文件。 第 12 章 多配置的应用程序 由于手机和平板屏幕的大小不同,可视范围也不同。如果将专为手机设计的应用程序直接 应用到平板上,就会浪费显示空间,造成操作不便。而如果将专为平板设计的应用程序直接应 用到手机上,则会因显示区域太小而显示不全。以上这两种情况,用户都不太愿意接受,因而 最好的方式就是为手机和平板打造不同的界面,让应用程序根据设备选择不同的配置进行显 示。本章的主要内容就是通过记事本应用程序来讲述如何搭建多配置的应用程序。 12.1 基本流程 多配置的应用程序的基本流程:在应用程序开始运行时,也就是调用 Ext.application 方法 时,应用程序会根据配置项 profiles 加载全部配置文件,配置文件初始化后,会根据自身的验 证条件验证配置是否符合当前设备,如果符合,就开始运行配置文件内的 launch 方法,开始 运行应用程序。 因此,要设计多配置的应用程序,首先要做的是定义配置文件。 12.2 定义配置文件 要定义配置文件,就要先改造 app.js,并从 Ext.app.Profile 派生配置文件。下面来完成这 个工作。本示例的程序文件在光盘\ch12\Note 目录下。 打开记事本的 app.js 文件,然后在 Ext.application 方法内加入配置文件的引用,代码如下: profiles: ['Phone', 'Tablet'], 这里有一个约定,在 profiles 里定义的配置文件,会到路径 app\ profile 下加载文件,因而 配置文件的命名规则基本是“应用程序名称.profile.实际类名”,如当前两个配置文件的类名为 Note.profile.Phone 和 Note.profile.Tablet。 Note.profile.Phone 和 Note.profile.Tablet 必须从 Ext.app.Profile 派生。在配置文件中,起主 要作用的是方法 isActive,只有该方法返回 true 时,才会执行配置文件中的 launch 方法。这里 要注意的是,配置文件内的 launch 方法执行后,会执行 Ext.application 方法中的 launch 方法, 如何安排好应用程序的界面初始化是要考虑的问题。最简单的方式当然是不在 Ext.application 的 launch 方法内执行任何东西,只使用配置项文件内的 launch 方法来初始化界面。 由于配置文件是根据 isActive 方法的返回值来确定执行哪个配置文件的,因而在定义该方 法时要特别小心,注 意 别出现两个或两个以上配置文件的 isActive 方法都返回 true 的情况,从 Sencha Touch 实战 ·282· 而造成界面初始化错误。一般情况下,需要判断的是设备类型,因而可以使用 Ext.os 对象的 deviceType 属性来返回设备类型。如果还需要区分系统,则可使用 Ext.os 对象的 is 方法来判 断系统类型。 在配置文件中,同样可以使用 views、contrlers、models 和 stores 等配置项来引用相应的 视图、控制器、模型和 store。在配置文件中的定义与在 Ext.application 中的定义主要区别是文 件的加载目录不同。配置文件中的定义会把配置文件的名称或 name 配置项的定义作为子目录 名称。如对于配置文件 Phone,如果没定义 name 配置项,如果要加载文件中引用的主视图, 就会在 app\view\phone 目录下加载;如果定义了 name 配置项,值为 abc,则会到 app\view\abc 目录下加载文件。当然视图、控制器、模型或 store 的类名也需要根据路径的不同而更改类名。 这样做的目的,就是为了能将不同平台的文件很好地划分开来,便于维护。 下面在 app\profile 目录下创建名为 Phone.js 的文件,用来定义 Note.profile.Phone,代码如下: Ext.define('Note.profile.Phone', { extend: 'Ext.app.Profile', isActive: function() { return Ext.os.deviceType == 'Phone'; }, launch: function(){ } }); 代码中使用了 deviceType 属性来决定配置文件的执行条件,这里是 phone。 将该文件另 存为 Tablet.js ,并将代码中的 Phone 替 换 为 Tablet 就定义好了 Note.profile.Tablet。现在可以测试一下是否能正确执行相应的配置文件了。方法很简单,就是 在访问地址后加上“?deviceType=[Phone 或 Tablet]”,如果 deviceType 的值为 Phone(注意,P 必须为大写),则会执行配置文件 Phone,如果 deviceType 的值为 Tablet(注意,T 必须为大写), 则会执行配置文件 Tablet。 在测试前,还需要在 launch 方法内添加一些代码,以便区分执行了哪个配置文件。把 app.js 的 launch 方法中的代码剪切并复制到两个配置文件中的 launch 方法内,并屏蔽调用 add 方法 的语句,添加以下语句: Ext.viewsport.setHtml(Ext.os.deviceType); 现在,可以在浏览器中进行测试了,如果 deviceType 的值为 Phone,则会在浏览器左上角 看到文字 Phone,如果 deviceType 的值为 Tablet,则会在浏览器左上角看到文字 Tablet。这说 明应用程序已经可以根据 deviceType 属性的不同而执行不同的配置文件了。 多配置的应用程序 第12章 ·283· 12.3 配制 Phone 界面 基于 Phone 的界面在之前已经完成了,只需要修改类名和目录就可以运行了。由于模型和 store 在两个配置下都是一样的,因而不需要做任何调整。 首先在 app\view 目录下创建 phone 的子目录,把原来在 app\view 目录下的 4 个脚本文件 复制到 phone 目录下,然后在类名中添加 phone,以适应目录结构。 接着是把 app\controller 目录下的 4 个脚本文件按照 app\view 目录下的处理方式进行处理。 接着,就是把 app.js 中的配置项 controllers 和 views 配置项复制到 Note.profile.Phone 的 config 配置对象中。在 Note.profile.Phone 的定义文件中,屏蔽刚才的测试代码,恢复调用 add 方法的代码,再把 Note.view.Main 修改为 Note.view.phone.Main。 还要把主控制器 Main.js 中的 views 配置项修改为: views: [ 'phone.Form' , 'phone.Details' , 'phone.Menu' ] 因为视图都在 phone 目录下,引用时需要添加对应的前缀,不然就会从 app\view 目录下 加载文件,而 app\view 目录下并不存在这些文件,从而造成错误。 好了,现在配置 Phone 的整个应用程序就完成了。下面要为配置 Tablet 编写界面了。 12.4 配置 Tablet 主界面 由于面板的可视区域比较大,因而主界面可以定义为左右两部分,左边显示数据列表,右 边显示每条记录的详细信息。 下面,在 app\view\tablet 目录下创建名为 Main.js 的脚本文件,并加入以下定义: Ext.define('Note.view.tablet.Main', { extend: 'Ext.Container', xtype: 'main', requires: [ 'Ext.TitleBar', 'Ext.dataview.List' ], config: { } }); 注意类名要与目录对应。由于要分左右两部分,因而要使用水平盒子布局将容器划分为两 Sencha Touch 实战 ·284· 个部分,左边将使用数据列表显示记录的标题,右边部分将显示详细信息,代码如下: layout: 'hbox', items:[ { xtype: 'list', itemTpl: '{title}', store: 'Notes', width: 300, items: { docked: 'top', xtype: 'titlebar', title: '记事本' } }, { styleHtmlContent: true, scrollable: true, flex: 1, items:[ { docked: 'top', xtype: 'titlebar', title: '' } ] } ] 数据列表的定义与配置 Phone 的时候区别不大,只是设置了宽度。而 详细信息视图设置了 flex,将填满余下的空间,另外“新增”按钮和“返回”按钮没有了。下面要做的是,当列表 有数据时,初始显示要让列表选择第一行,详细信息则显示第一行的详细信息。 在完成 Tablet 配置的主控制器之前,先考虑一下这样的情况,无论是哪个配置下的主控 制器,是否都会有相同的引用和相同的事件配置呢?如果有,那么,有多少个配置就需要重 复编写多少个这部分的引用时的事件绑定。通过类的继承,可以轻松实现代码的重用。把 app\controller\phone 目录下的 Main.js 文件复制到 app\controller 目录,然后将定义代码修改 如下: Ext.define('Note.controller.Main', { 多配置的应用程序 第12章 ·285· extend: 'Ext.app.Controller', config: { refs: { main: "main", }, control: { 'button[iconCls=add]': { tap: 'onAdd' }, 'list': { itemtaphold: 'onItemTapHold' } }, views: [ ] }, launch: function () { }, onAdd: function () { }, onItemTapHold: function(cmp, index, target, record, e, eOpts ){ } }); 主要修改包括类名,这里没有了子目录 phone。在配置中可共用的部分包括对主界面的引 用以及新增按钮的 tap 事件。要注意,这里的 onAdd 方法不需要添加代码,因 为配置不同的可 能处理也不同,需要在子类通过重写来实现不同的处理。 现在,修改 Phone 配置的主控制器 Main.js。首先要修改的是扩展,在这里要从 Note.controller.Main 扩展,而不是 Ext.app.Controller。接着就是把重复的 refs 定义和新增按钮 Sencha Touch 实战 ·286· 的事件绑定删除,这样就修改完了。测试一下 Phone 配置,会发现一切运行正常。 将 Note.controller.phone.Main 的定义文件另存到 app\controller\tablet 目录,文件名还是 Main.js。将类名修改为 Note.controller.tablet.Main,这样,就有了 Tablet 配置的主控制器。把 列表绑定的事件全部删除,把 views 中的定义也删除,除了 onAdd 方法和 launch 方法, 其他 的方法都删除,方法 onAdd 内的代码也要删除。 好了,Tablet 配置的主控制器就基本完成。现在,要完成选择第一个记录的问题,选择的 方法与 9.3.7 节的方式差不多,都是要判断是视图先显示还是 store 先加载。因而,先在 launch 方法内为 store 绑定 refresh 方法,代码如下: onRefesh: function(){ var me = this, main = me.getMain(), painted = main.isPainted(), store= Ext.getstore('Notes'), count = store.getCount(); if( painted & count >0 ){ var record = store.getAt(0), view = main.down('container[flex=1]'); main.down('list').select(record); view.setRecord(record); view.down('titlebar').setTitle(record.data.title); view.setHtml(record.data.content); } }, 代码先通过容器的 isPainted 事件返回容器是否已经渲染,如果已经渲染,且 store 的记录 总数大于 0,就可以进行选择了。由于主视图本身也是容器,因而要找到底下的容器,必须通 过属性 felx 来查找。记录和显示详细信息的容器都找到后,就要调用数据列表的 select 方法选 择记录了,然后设置显示详细信息容器的显示内容以及它内部标题栏的标题。 接下来绑定主视图的 show 事件,代码如下: main: { show: 'onShow' }, 接着完成 onShow 方法,代码如下: onShow: function(){ var store= Ext.getstore('Notes'); if(store.isLoaded()){ me.onRefesh(); 多配置的应用程序 第12章 ·287· } }, 如果 store 已经加载,就调用 onRefesh 方法,否则不做任何处理,等待 store 加载完成后 触发 refresh 事件再更新显示。 现在,顺便把数据列表的 itemtap 事件也完成了。先绑定事件,代码如下: 'list': { itemtap: 'onItemTap' } 再完成 onItemTap 方法,代码如下: onItemTap: function(cmp , index, target, record, e, eOpts){ var me = this, main = me.getMain(), view = main.down('container[flex=1]'); view.setRecord(record); view.down('titlebar').setTitle(record.data.title); view.setHtml(record.data.content); } 现在在浏览器打开测试页面,先切换到平板和横向,然 后 加载应用程序。注意要将测试地 址的 deviceType 修改为 Tablet。应用程序加载后,将看到所示的效果,第一条记录已经选择了, 而且详细信息容器也显示了第一条记录的详细信息。 图 12-1 Tablet 配置下的主界面 Sencha Touch 实战 ·288· 12.5 完成新增功能 现在界面上还没有新增按钮,放在数据列表的标题栏左边比较合适。下面在数据列表的标 题栏中添加新增按钮,代码如下: items:[ {iconCls: 'add' , align: 'left'} ] 由于在 Note.controller.Main 中已经为新增按钮绑定了事件,因而不需要再绑定,只需要重 写 onAdd 方法就行了。 现在考虑编辑视图的问题。在 Phone 配置中编辑视图类似弹出的对话框,占满整个屏幕来 显示表单的。而在 Tablet 配置中,还是可以使用该策略,因而,编辑视图其实是可以共用的。 要实现视图共用,只需要把视图放在 app\view 目录下就行了。现在,把 Form.js 文件由 app\view\phone 目录剪切到 app\view 目录,并将类名修改为 Note.view.Form。 接着,修改 Note.controller.phone.Main 中的 views 配置项的定义,将 phone.Form 修改为 Form。在方法 onAdd 内,Ext.create 方法中的类名也需要修改回来。 现在最大的问题是控制器,编辑视图的控制器必须像编辑视图一样将 Form.js 文件复制到 app\controller 目录下,并修改类名。存在的问题是,不能在配置文件中引用这个控制器,因为 控制器文件已经不在配置文件对应的目录了。能找到控制器文件的地方,就是 app.js,因而, 需要把控制器的引用放到 app.js 中。 把控制器文件的引用加回 app.js,在 Phone 配置下运行,一切正常,说明修改没有问题。 现在,研究 Phone 配置下的 onAdd 方法,会发现编辑视图是添加到主界面中的,这不符 合 Tablet 配置的情况。在 Tablet 配置中,需要将它添加到 viewsport 中。而其他代码基本是一 样的,因而可以直接复制到 Tablet 配置的主控制中,并修改为以下代码: var me=this, main= me.getMain(), noteform = me.noteform; if(!noteform){ noteform = Ext.create("Note.view.Form"); me.noteform = noteform; Ext.viewsport.add(noteform); } noteform.setValues({id:'', title:'', content:''}); noteform.down('titlebar').setTitle('新增记事'); noteform.show(); 因为编辑视图不在主视图内,因而需要使用 noteform 属性来指向编辑数据。其次,编辑 多配置的应用程序 第12章 ·289· 视图需要添加 viewsport。由于不是卡片布局,不能使用 setActiveItem 方法,必须使用 show 方 法来显示编辑视图。 在编辑视图的控制器中,是否已经注意到了,现在关闭编辑视图都是使用 setActiveItem 方法,这不符合 Tablet 配置下的情况。那么,是否又要拆分成两个控制器呢?如果按正规做法, 确实是需要的,不过,也有其他办法,那就是调用按钮的 up 方法,去寻找主视图(选择符: main),如果能找到,说明需要使用 setActiveItem 方法来切换视图,否则使用 hide 方法关闭编 辑视图,修改后的代码如下: onSave: function(cmp){ var form=cmp.up('formpanel'), values= form.getValues(), parent = cmp.up('main'); if(values.title==""){ Ext.Msg.alert('信息','请输入标题。'); }else{ if(values.id==""){ var note = Ext.create('Note.model.Note', values); note.save(); Ext.getstore('Notes').load(); if(parent){ main.setActiveItem(main.down('list')); }else{ cmp.up('noteform').hide(); } }else{ record= form.getRecord(); form.updateRecord(record); record.save(); if(parent){ main.setActiveItem(main.down('list')); }else{ cmp.up('noteform').hide(); } } } }, onCancel: function(cmp){ Sencha Touch 实战 ·290· var me=this, main = me.getMain(), parent = cmp.up('main'); if(parent){ main.setActiveItem(main.down('list')); }else{ cmp.up('noteform').hide(); } } 代码中用粗体显示的,就是修改过的代码。由于不能通过列表返回 store,因此修改成直 接使用 getstore 方法来返回 store 了。 好了,Tablet 配置的新增功能就完成了。 12.6 编辑和删除功能 在Phone配置中,是使用itemtaphold事件来显示操作菜单实现编辑和删除的,但这在Tablet 配置中有点繁琐。在数据列表的标题栏右边显示删除按钮,在显示详细信息的容器的标题栏右 边显示编辑按钮是比较好的方案。现在,在这两个标题栏分别添加这两个按钮,就不在此列出 代码了。 下面切换到 Tablet 配置的主控制器,为编辑按钮和删除按钮绑定 tap 事件,代码如下: 'button[iconCls=delete]': { tap: 'onDeleteTap' }, 'button[text=编辑]': { tap: 'onEditTap' } 下面先来完成 onEditTap 方法,该 方 法与编辑 Phone 配置的主要区别是要判断显示详细信 息的容器是否已绑定记录,如果没有,就要提示用户先选择一个记录。如果已绑定,则执行过 程与 Phone 配置就没什么区别了。具体代码如下: onEditTap: function(cmp){ var me=this, view = cmp.up('container[flex=1]'), rec=view.getRecord(); if(rec){ var noteform = me.noteform; if(!noteform){ 多配置的应用程序 第12章 ·291· noteform = Ext.create("Note.view.Form"); me.noteform = noteform; Ext.viewsport.add(noteform); } noteform.setRecord(rec); noteform.show(); }else{ Ext.Msg.alert('信息', '请选择一条记录再编辑。'); } } 与 onAdd 方法一样,由于编辑视图不是在主视图中的,因而需要使用 noteform 属性来指 向编辑视图。显示也需要使用 show 方法。如果记录不存在,则要显示提示信息,提示用户选 择一条记录。 在这里有一个问题,就是编辑完成,单击保存按钮后,由于显示详细信息的视图已经显示, 但是,并没有代码去更新编辑后的显示,因而要切换到编辑视图的控制器,在编辑记录时,编 辑视图调用的 hide 方法关闭前,加入以下代码来刷新详细信息的显示: view = this.getMain().down('container[flex=1]'); view.down('titlebar').setTitle(record.data.title); view.setHtml(record.data.content); 删除记录与 Phone 配置基本上没有什么区别,代码如下: onDeleteTap: function(cmp){ var me=this, view = me.getMain().down('container[flex=1]'), rec=view.getRecord(); Ext.Msg.confirm('删除','确定删除记事:'+ rec.data.title+'?', function(buttonId, value , opts){ var me = this; if(buttonId=="yes"){ var record = me.getRecord(); record.erase(); } }, view ) }, 除了不用关闭操作菜单,视图的选择不一样外,其他代码基本上是一致的。 Sencha Touch 实战 ·292· 至此,整个 Tablet 配置的界面就完成了,多配置的记事本也完成了。 12.7 小结 本章主要通过一个示例学习了如何编写多配置的应用程序,是开发移动类应用程序必然会 使用到的,除非应用程序已经明确只服务于一种类型的设备。在设计多配置的应用程序时,最 主要的是如何提取共用部分,以减少代码的编写。如果不怕麻烦,后台代码也配合的话,也可 以定义不同的模型和 store,不过为了尽可能简单,最好还是使用统一的模型和 store,按使用 字段最多的方式配置就可以了。这样,服务器就可以只按最多字段的方式返回数据,多 余的字 段,store 会自动过滤掉。 在下一章,将通过一个综合示例来结束本书的实战之旅。
还剩44页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

yabuli88

贡献于2017-06-19

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf