移动应用主要有两种模式:本地应用和移动Web应用。目前以本地应用最为流行,而移动Web应用也日趋流行。这两种模式相当于我们经常说的CS架构和BS架构。
本地应用,简单来说就是通过使用手机操作系统支持的程序语言(例如:iphone的iOS系统使用Objective-C语言,google的android系统则使用Java语言)编写软件,然后安装在手机上的应用软件。本地应用开发可以直接调用手机操作系统的 API(包括UI界面接口、摄像头接口、加速度传感器接口、读写内存地址等等),因此响应速度更快、用户体验更好(界面可制作得很优雅、操作非常流畅)、不受网络的限制。只是目前手机操作系统众多:苹果的iOS、谷歌的Android、微软的WindowPhone 7、诺基亚的Symbian、其他厂商的WebOS、黑莓等,若一款应用软件想在不同的操作系统上运行,则需要针对不同的平台重新开发(有可能还需要根据手机屏幕的大小进行特定设置),这是一件非常痛苦的事情。
简而言之,本地应用具有可以充分发挥设备硬件和操作系统的特性,运行效率高,完全不受网络限制的优势;也具有开发周期较长、成本较高(需要为各种操作系统进行开发)、不同终端的适配度不理想的劣势。
移动Web应用,简单来理解就是针对移动终端优化过的Web 站点,终端用户通过支持Html5、Css3、Javascript标准的Webkit内核浏览器访问部署在服务器的Web应用。因此移动Web应用具有跨平台、多种终端的广泛适配(降低了开发周期和成本)、实时调整与完善、Web开发者可以快速上手等优势;当然限于手机操作系统的安全限制,移动Web应用还是在硬件功能调用方面有所滞后(譬如:目前还不能直接调用手机的加速度传感器、摄像头)、复杂的用户界面效果难以实现等劣势。不过,随着HTML5的不断发展,移动Web应用也将更加强大。
1.2 移动Web应用开发框架
目前主要的移动Web应用开发框架有:Sencha Touch、JQuery Mobile以及PhoneGap。下面简单介绍一下这三个开发框架。
u Sencha Touch:Sencha Touch框架是世界上第一个基于HTML 5的Mobile App框架,它可以让Web App看起来像Native App。美丽的用户界面组件和丰富的数据管理,全部基于最新的HTML 5和CSS3的 WEB标准,全面兼容Android和iOS设备。SenchaTouch相对来说是一个重量级的移动Web应用框架,适合用于开发业务逻辑比较复杂的移动Web应用。优点:针对触摸屏丰富的UI设计支持复杂交互、纯JavaScript搞定布局、版本稳定性能尚可。
u JQuery Mobile:JQuery Mobile 是 jQuery 在手机上和平板设备上的版本,不仅给主流移动平台带来jQuery核心库,而且发布一个完整统一的jQuery移动UI框架,支持全球主流的移动平台,它属于一个轻量级的移动Web应用框架,可以非常便捷的开发出基于Html5的移动网站。优点:超多平台支持、入门简单传统div布局;缺点:UI支持比较简单不适合复杂交互。
u PhoneGap:PhoneGap是一个用基于HTML,CSS和JavaScript的,创建移动跨平台移动应用程序的快速开发平台。它使开发者能够利用iPhone,Android,Palm,Symbian,WP7,Bada和Blackberry智能手机的核心功能——包括地理定位,加速器,联系人,声音和振动等,此外PhoneGap拥有丰富的插件,可以以此扩展无限的功能。PhoneGap是免费的,但是它需要特定平台提供的附加软件,例如iPhone的iPhone SDK,Android的Android SDK等,也可以和DW5.5配套开发。使用PhoneGap只比为每个平台分别建立应用程序好一点点,因为虽然基本代码是一样的,但是你仍然需要为每个平台分别编译应用程序。目前PhoneGap缺陷还是蛮多的,比如运行速度慢,UI反应延时。假以时日,随着技术的发展,问题会得到解决的。
具体选择那种移动Web框架,需要从项目组人员技术及业务需求情况出发进行选择:
1、功能简单,只想让现有网站支持手机和平板电脑等移动设备,可考虑jQuery Mobile框架,入门简单而且支持较多平台。
2、想做客户端而且跨平台、丰富的交互,且项目组有熟悉ExtJS的开发人员,Sencha Touch是个不错的选择。
3、如果需要调用到手机操作系统的API,可以把Sencha Touch(或JQuery Mobile)与PhoneGap进行整合开发。
第二章 SenchaTouch框架的介绍
Sencha Touch框架是世界上第一个基于HTML 5的Mobile App框架,也是目前为止所发现的最强大的应用于移动平台的框架,它将自己定位为框架(Framework)而不是类库(Library),也可以充 分印证这一点。相信随着Sencha Touch的不断发展,移动平台的Web App用户体验设计会得到大幅提升,同时也会对HTML 5和CSS3在移动平台上的普及推广产生很大的促进作用。可以预见,随着HTML 5愈加强大的功能,未来的移动应用将会逐渐向Web App时代迈进。
Sencha Touch是原来的Extjs项目组与jQTouch和Raphael两大项目强强联手后,打造的全新Mobile App框架。
Sencha Touch可以让你的WebApp看起来像Native App。美丽的用户界面组件和丰富的数据管理,全部基于最新的HTML 5和CSS3的 WEB标准,全面兼容Android和iOS设备。
Sencha Touch官方列出的几大特性有:
u 基于最新的WEB标准 – HTML 5,CSS3,JavaScript。整个库在压缩和gzip后大约80KB,通过禁用一些组件还会使它更小。
u 支持世界上最好的设备。Beta版兼容Android和iOS,Android上的开发人员还可以使用一个专为Android定制的主题。
u 增强的触摸事件。在touchstart等标准事件基础上,增加了一组自定义事件数据集成,如tap、swipe、pinch、rotate等。
u 数据集成。提供了强大的数据包,通过Ajax、JSONp、YQL等方式绑定到组件模板,写入本地离线存储。
在查看Sencha Touch自带的Demo以及实际使用中,还可以发现具体不错的功能点:
u HTML 5地理定位
u 重力感应滚动
u 滚动Touch事件
u 为移动优化的表单元素
u JSONP代理(跨域数据读取)
u YQL数据代理(类似查询语言式数据获取)
u 遮罩弹出层
u Multi-Card布局
u CSS3 Transitions
u Tab组件以及滚动列表视图
第三章 SenchaTouch第一个例子
Sencha Touch的开发环境搭建相当简单,在Eclipse中创建一个Web项目,定好项目的目录结构后,引入Sencha Touch的类库。
Sencha Touch的调试工具,我主要是使用google的chrome浏览器(最新版本是chrome15.0.87),下载地址:http://www.google.cn/Chrome。
关于该浏览器及其调试的使用方法可参考:http://www.google.com/support/chrome/bin/static.py?page=guide.cs&guide=29302&hl=cn&printable=1
按我们的通用平台架构,视图层使用了Freemarker模板,因此前台页面一般分为ftl文件和js文件。根据Sencha Touch的特点以及我自己的思考,一个模块一般只需用一个ftl文件,然后在这个文件引入所需用的自定义的js文件(通用的组件js文件,可以考虑放入到freemarker模板中)。
以下就是第一个例子的ftl(stFirstExample.ftl)文件内容:
<@c2.touch title="" cssList=[] jsList=['example/stFirstExample.js']>
</@c2.touch>
cssList是用来引入自定义的css3样式文件,jsList是用来引入所需的js文件。
js(stFirstExample.js)文件内容:
//创建第一个示例的命名空间
Ext.ns('example', 'example.views');
//通过Ext.setup方法来参加一个senchatouch应用页面,setup方法里可以配置很多属性
Ext.setup({
statusBarStyle: 'light',
onReady: function() {
//工具栏-toolbar
varfunBar = {
xtype: 'toolbar',
title: 'ST 第一个例子',
dock: 'top', //工具栏放置的位置(必须的属性):top-上,bottom-下,left-左,right-右
scroll: 'horizontal',
height:30,
items: [{
xtype:'button',
text: '桌面',
ui: 'back',
//iconMask:true,
//iconCls:'home',
style:btStyle,
handler:function(){
window.location = prefix + '/index.action';
}
}]
};
//主界面
varviewport = new Ext.Panel({
fullscreen:true,
monitorOrientation:true,
dockedItems:[funBar],
items:[{
html:'hello kitty......'
}]
});
}
});
可以看到,stFirstExample.js 的第一行代码创建了两个命名空间:example和example.views。
第二行代码调用了 Ext.setup() 方法,用以建立一个触控设备的 Web 页面,该方法可以为我们的应用设置不同的启动属性和行为:
l icon,设置该应用默认的图标;
l tabletStartupScreen,该属性设置在平板电脑上的启动图标;
l phoneStartupScreen,该属性设置在智能手机上的启动图标;
l glossOnIcon,该属性设置是否在默认图标上呈现光环效果;
l onReady,该方法会在页面加载完毕,浏览器中的 DOM 模型已经建立完成时被调用。由于为了保证程序在运行时所依赖的JavaScript 文件都已经加载完毕,我们一般将应用启动的逻辑置于该方法内,类似于 Java 程序的 main 方法。
在onReady方法中,有一个地方我们需用注意的:
l Ext.Panel对象的dockedItems属性,通过它可以在panel中放置工具栏等组件,可选值有:top-上,bottom-下,left-左,right-右。fullscreen属性为true(默认为false)则强制该panel充满整个屏幕。monitorOrientation属性为true则可以让panel面板监听屏幕方向发生变化时候的事件。
l dockedItems属性里的对象,必须由docked属性,用以指定放置的位置。
当然js文件也可以通过另外一种方式创建应用:
//通过Application来创建一个应用
var FirstApp = new Ext.Application({
name: 'firstApp',
useLoadMask: true,
launch: function () {
//工具栏-toolbar
varfunBar = {
xtype: 'toolbar',
title: 'ST 第一个例子',
dock: 'top', //工具栏放置的位置(必须的属性):top-上,bottom-下,left-左,right-右
scroll: 'horizontal',
height: 30,
items: [{
xtype: 'button',
text: '桌面',
ui: 'back',
//iconMask:true,
//iconCls:'home',
style: btStyle,
handler: function(){
window.location= prefix + '/index.action';
}
}]
};
//主界面
var viewport = newExt.Panel({
fullscreen: true,
monitorOrientation:true,
dockedItems: [funBar],
items: [{
html: 'hellokitty......'
}]
});
}
});
Ext.Application实例的初始化,意味者一个sencha touch应用的建立,这个类的实例化后,会自动创建一个全局的变量FirstApp,并且同时建立了如下的命名空间:
firstApp
firstApp.views
firstApp.stores
firstApp.models
firstApp.controllers
而launch的方法只会运行一次。
第四章 SenchaTouch布局介绍
在我们的Web应用开发中,页面的排版、布局很重要,用户就是通过页面操作来完成日常工作的。如果界面布局不合理、操作不方便,用户也不会对系统有好的印象、甚至有可能影响一个项目的成败。我自己的经验是,在开发某个功能模块时,除了仔细属性该模块的功能需求和业务需求外,还会在草稿纸上简单的把该功能的布局画出来(如果美工已经制作有页面模型外)。例如:
Sencha Touch的布局类似Extjs中的布局,常用的有:BoxLayout、HBoxLayout、VBoxLayout、FitLayout、CardLayout。
4.1 BoxLayout、HBoxLayout、VBoxLayout(箱子布局)
BoxLayout是箱子布局,该布局类似于药店里放置中草药的大柜子里一个个小箱子那样,把组件放置在容器中(Container)中。BoxLayout是HBoxLayout和VBoxLayout这两个布局类的父类,一般很少直接使用。
u HBoxLayout是水平箱子布局,即把组件横排的放置在容器中。
代码清单:
var viewport = newExt.Panel({
fullscreen:true,
//width: 500,
//height:200,
margin: '0 0 0 0',
layout: {
type: 'hbox', //指定layout布局方式为HBoxLayout
align: 'stretch' //布局里的‘小容器’拉伸,类似window桌面图片那样,拉伸到整个页面大
},
items: [{
flex: 1, //所占宽度的比率
//height: 200,
style: 'border:1pxred solid', //自定义样式
margin: '0 20 0 0', //设置边框距离
items: [{
xtype:'button',
text:'第一',
margin:6
}]
},{
flex: 2,
//height: 200,
style: 'border:1pxred solid',
margin: '0 20 0 0',
html: '<divstyle="border:1px red dashed;margin:6px;">第二个小箱子</div>'
},{
flex: 3,
//height: 200,
style: 'border:1pxred solid',
items: [{
xtype:'button',
text:'第三',
margin:6
}]
}]
});
有两个属性需要关注一下:
l align: 'stretch',该属性是设置容器里‘小容器’的对齐方式。
l flex属性是设置‘小容器’的宽度比率,具体的计算方式请参看文档。
当然还有其他的属性,例如:style、margin、padding,这些属性主要是设置样式的。
u VBoxLayout垂直箱子布局,即把组件垂直的放置在容器中。
代码清单:
var viewport = newExt.Panel({
fullscreen: true,
//width: 500,
//height:200,
margin: '0 0 0 0',
layout: {
type: 'vbox', //指定layout布局方式为VBoxLayout
align:'stretch' //布局里的‘小容器’拉伸
},
items: [{
flex: 1, //所占宽度的比率
//height: 200,
style: 'border:1pxred solid',
margin: '0 0 10 0',
items: [{
xtype:'button',
text:'第一',
margin: 6
}]
},{
flex: 2,
//height: 200,
style: 'border:1pxred solid',
margin: '0 0 10 0',
html: '<divstyle="border:1px red dashed;margin:6px;">第二个小箱子</div>'
},{
flex: 3,
//height: 200,
style: 'border:1pxred solid',
items: [{
xtype:'button',
text:'第三',
margin:6
}]
}]
});
4.2 FitLayout(自适应布局)
FitLayout是布局的基础类,对应面板布局配置项的名称为fit,使用fit布局将使面板子元素自动充满容器,如果在当前容器中存在多个子面板则只有一个会被显示。
代码清单:
var viewport = newExt.Panel({
fullscreen: true,
//width: 500,
//height:200,
margin: '0 0 0 0',
layout: 'fit', //指定layout布局方式为FitLayout
items: [{
style: 'border:1pxred solid',
html: '<div style="border:1pxred dashed;margin:6px;">第一个小箱子</div>'
},{
style: 'border:1pxblue solid',
html: '<div style="border:1pxred dashed;margin:6px;">第二个小箱子</div>'
}]
});
4.3 CardLayout(卡片式布局)
CardLayout在sencha touch中是最常用的布局,模仿本地应用的页面转换效果主要通过它来体现出来。它是扩展自FitLayout布局,对应面板布局配置项的名称为card。该布局会包含多个子面板,但任何时候都只有一个子面板处于显示状态,这种布局经常用来制作向导和标签页。
各个字面板之间切换的途径是调用setActiveItem方法,该方法接收一个子面板对象或id、索引作为参数。
代码清单:
//工具栏-toolbar
var funBar = {
xtype: 'toolbar',
title: 'CardLayout例子',
dock: 'top', //工具栏放置的位置(必须的属性):top-上,bottom-下,left-左,right-右
scroll: 'horizontal',
height: 30,
items: [{
xtype: 'button',
text: '桌面',
ui: 'back',
//iconMask:true,
//iconCls:'home',
style: btStyle,
handler: function(){
window.location = prefix + '/index.action';
}
},{
xtype: 'button',
text: '第一个子面板',
style: btStyle,
handler: function(){
changeItem('p1');
}
},{
xtype: 'button',
text: '第二个子面板',
style: btStyle,
handler: function(){
changeItem('p2');
}
}]
};
//主界面
var viewport = newExt.Panel({
fullscreen: true,
margin: '0 0 0 0',
layout: 'card', //指定layout布局方式为CardLayout
activeItem: 0,
dockedItems: [funBar],
items: [{
id: 'p1',
style: 'border:1pxred solid',
html: '<divstyle="border:1px red dashed;margin:6px;">第一个小箱子</div>'
},{
id: 'p2',
style: 'border:1pxblue solid',
html: '<divstyle="border:1px red dashed;margin:6px;">第二个小箱子</div>'
}]
});
//切换子面板
var changeItem = function(id){
viewport.setActiveItem(id, 'slide');
};
};
用户与系统的交互,绝大部分是通过表单来进行。Sencha Touch的表单与Extjs的表单类似,只是新增了一些html5的元素,例如:url输入框、email输入框、search输入框、Number输入框、slider、toggle等。
表单元素有以下这些,其中有很多都是大家所熟悉的,在此就不做多说明,主要是说明一些新的表单元素及其常用属性,其他(包括组件的方法、事件等)的请大家参照api文档:
l Text文本输入框
简单的文本输入框,xtype为textfield(注意与extjs的不同哦)。常用的属性有:
id:组件的唯一id,id属性是全部组件都有的。
name:组件的名称,name属性也是全部组件都有的。
label:组件显示的标记(注意与extjs的不同哦)。
labelAlign:label放置的位置,可选值有:left、right、top、bottom。
placeHolder: 输入框为空值时自动显示的值,相当于extjs的emptyText属性。
maxLength:输入框可以输入内容的最大长度。
autoCapitalize:是否开启首字母大写(默认为false):true-是,false-否。
useClearIcon:是否使用清除图标:true-当输入框值改变后,右边会显示清除的小图标。
required:标识为必填,只是做一个标识,表单提交时不会做验证。
l Password密码输入框
密码输入框,xtype类型为passwordfield。
l Select下拉框
下拉框组件,xtype类型为selectfield。主要的属性:
displayField:下拉框显示的字段名
valueField:下拉框值的字段名(该字段所指向的值将会被传递到后台)
options:下拉选项对象数组,对象的成员名称必须与displayField和valueField的相一致。需要注意的是当select组件设置了store属性,则本属性的值将被忽略。
store:提供给select组件下拉选项的store对象实例,通过该属性可以实现级联下拉选择。
l DatePicker日期选择
日期选择组件,xtype类型为datepickerfield。主要的属性:
picker:用来创建日期选择器的配置对象或直接使用一个日期选择器实例。例如:
picker: {
yearFrom: 1910, //设置开始年份
cancelButton: '取消', //设置取消按钮上的文字
doneButton: '完成', //设置完成按钮上的文字
slotOrder: ['year', 'month', 'day'] //设置日期选择器上显示年月日的顺序
}
l Checkbox多选框
多选框组件,xtype类型为checkboxfield。
l Radio单选框
单选框组件,xtype类型为radiofield。
l Email电子邮件输入框
邮件输入框组件,xtype类型为emailfield,是文本输入框组件的子类。该组件暂时没有很特别的属性。
l Url超链接输入框
超链接输入框组件,xtype类型为urlfield,是文本输入框组件的子类。该组件暂时没有很特别的属性。
l Slider滑动选择器
滑动选择器组件,xtype类型为sliderfield,该组件可以让用户通过手指横向滑动来选择值。主要的属性:
minValue:slider的最小值。
maxValue:slider的最大值。
increment:每次滑动的增加值,只能为正整数,默认值为1。
注意:该组件的值不会传递到后台,需要手工调用该组件的getValue()方法获取设置的值,并把这个值设置到一个hidden组件中,以传递给后台程序。可以通过该组件的change事件来检测值的变化情况。
l Toggle开关切换器
开关切换器组件,xtype类型为togglefield,该组件是slider组件的子类,它只设置了两个值:0和1。
该组件和slider组件一样,需要手工调用getValue()方法把值设置到一个hidden组件,才能传递给后台程序。
l Number数字输入框
数组输入框组件,xtype类型为numberfield,是文本输入框组件的子类,但只能输入数字(包括负号与小数点)。
l Spinner微调器
微调器组件,xtype类型为spinnerfield,是number组件的子类。主要的属性:
minValue:spinner的最小值。
maxValue:spinner的最大值。
incrementValue:每次点击-/+按钮时,增加的值。
cycle:循环设置项,该属性为true时,如果值已经到达maxValue设置的值,下一次点击则设置为minValue的值。同样,如果已经达到minValue设置的值,下一次点击则设置为maxValue的值。
l Textarea文本输入域
文本输入域组件,xtype类型为textareafield,负责多行文字的输入。
l Search搜索输入框
搜索输入框组件,xtype类型为searchfield,是文本输入框组件的子类。该组件暂时没有很特别的属性。
1) 常用属性:
l baseParams:Object
发送到后台的可选参数(只有当standardSubmit属性为false时,即标准提交方式时,不会把这个属性的参数值传递到后台程序),该属性的类型为Object,例如:{‘entity.title’:’hello kitty’}。
l dockedItems:Object/Array
放置到formpanel的一个或一系列组件,这些依附组件可以放置在formpanel的上下左右的位置。其典型应用是在panel上放置toolbars或tabbars,该属性的类型为一个对象或一个对象数组。
l standardSubmit:Boolean
是否执行一个标准提交,默认为false(非标准提交),该属性的类型为boolean型。如果需要上传附件,则必须执行标准提交,即该属性设置为true。
l waitTpl: Object
设置一个ajax提交进度的模板,例如:提交后弹出‘正在保存数据,请稍等…’的提示。
l record:Ext.data.Model
该属性为只读属性,加载到form中的model实例,该属性的类型是Ext.data.Model。
2) 常用方法:
u getRecord(): Ext.data.Model
该方法返回当前加载到form中的model实例。
u getValues(Boolean enabled): Object
返回一个包含有全部表单元素及其值的对象,对象的属性与表单元素的name属性一致,对于具有相同name属性的checkbox或radio元素,它的值为一个值数组。参数enabled若为true则只返回哪些disabled属性为false或undefined的元素值,false则返回全部。
u load(Ext.data.Model instance): Ext.form.FormPanel
把一个model实例中的数据加载到对应的form元素中(model实例中的name与form元素中name属性一致)。该方法是loadRecord方法的快捷键。通过该方法,我们可方便的把数据设置到form元素中。
u loadRecord(Ext.data.Model instance) : Ext.form.FormPanel
把一个model实例中的数据加载到对应的form元素中(model实例中的name与form元素中name属性一致)。
u removeAll([Boolean autoDestroy]): Array
该方法可以删除form容器中的全部组件,例如:从新增页面保存后跳转到修改页面,这时需要调用该方法把新增页面的表单及其元素全部删除,否则会出现相同名字和id的表单元素。sencha touch的页面跳转(setActiveItem方法),是隐藏原来的页面的元素,在该页面的最后自动加上后页的html元素。
u reset(): Ext.form.FormPanel
表单重设方法,把表单元素的值设置为表单元素的起始值。
u submit(Object options): Ext.data.Connection
表单提交方法,如果form的standardSubmit属性为true,则执行标准提交,否则执行基于ajax方式把form数据提交到后台。该方法的参数options除非另有说明,一般包含如下的属性值:
url:String,表单提交的目标地址。
method:String,表单提交方式,一般有POST和GET。
params: String/Object,提交到后台的额外参数(即除了表单元素外的参数),如果已设置baseParams属性的话,默认是该属性值。提交时将会调用Ext.urlEncode方法对params值进行编码。
submitDisabled: Boolean,该属性值为true则提交表单中的全部元素,否则只提交disabled为false的表单元素值。
success: Function,表单提交到后台并获得后台程序响应时的回调函数,该函数包含两个参数:form(Ext.FormPanel对象)和result(服务器返回的结果对象),通过该回调函数,我们可根据后台返回的数据进行一定的逻辑处理。
failure: Function,表单提交失败时回调函数,该函数包含两个参数:form(Ext.FormPanel对象)和result(服务器返回的结果对象)。
scope: Object
回调函数的变量访问。
u updateRecord(Ext.data.Model instance, Boolean enabled):Ext.form.FormPanel
把表单元素中的数据更新到model实例中。
表单数据加载,可以通过load方法从一个已有的model实例中把数据设置到表单元素中,也可以通过ajax从后台获取数据,然后通过load方法加载数据到表单元素中。
1) 从model实例中加载
//定义一个model
Ext.regModel('PersonInfo', {
fields: [
{name:'entity.linkman',type:'string'},
{name:'entity.password', type:'string'},
{name:'entity.sex',type:'string'},
{name:'entity.introduce',type:'string'}
]
});
//创建model实例数据
var person = Ext.ModelMgr.create({
'entity.linkman': 'hellokitty',
'entity.password' : 'hellokitty',
'entity.sex': 'm',
'entity.introduce' : '人见人爱 车见车载'
}, 'PersonInfo');
//加载数据到form元素中
Ext.getCmp('frmInfo').load(person);
2) 通过Ajax从后台获取数据并加载到表单中
该方式只是多了通过ajax从后台获取数据的步骤,其他的都与方式一是一样的,在此不多说了。
表单数据提交有两种方式:标准提交和ajax数据提交。为了更好的增强用户的操作体验,我们一般是采取ajax提交方式,即使是附件上传功能,也可以通过提交到一个隐藏iframe方式,模拟ajax提交。
表单数据的提交通常是通过form组件的submit方法提交。
代码清单:
//提交数据
submitForm: function(sendFlag){
//验证数据
if(!this.validate()){
return;
}
var_scope = this;
vartipMask = '正在保存便函,请稍等...';
//是否发送标识
Ext.getCmp('issended').setValue(sendFlag);
if(sendFlag== '1'){
tipMask = '正在发送便函,请稍等...';
}
//弹出正在保存的提示
varcmpMask = Ext.getBody();
loadMask(cmpMask,iconPath, tipMask);
//设置按钮失效
vararrButton = [Ext.getCmp('btnSave'), Ext.getCmp('btnSend')];
enableButton(arrButton,false);
var form= Ext.getCmp('frmInfo');
form.submit({
url: this.saveURL,
method: 'POST',
success: function(thisForm,action){
unLoadMask(cmpMask);
//实体ID
var buId =action.result;
varsaveTip = '成功保存便函信息!';
if(sendFlag== '1'){
saveTip = '成功发送便函信息!';
}
var sa =Ext.Msg.alert('', saveTip);
setTimeout(function(){
sa.hide();
enableButton(arrButton, true);
}, 1500);
//保存附件
_scope.saveAttach(_scope, buId, sendFlag);
},
failure: function(thisForm,action){
unLoadMask(cmpMask);
enableButton(arrButton, true);
if(action.result){
Ext.Msg.alert('',action.result);
}else{
Ext.Msg.alert('', '提交失败,表单数据不完整!');
}
}
});
}
若是需要上传附件,则必须使用标准提交,且需要设置form的enctype属性。该属性的设置,一般是在formpanel的afterrender监听器中设置:
{
xtype: 'form',
id: 'frmAttach',
margin: '0 0 0 0',
standardSubmit: true, //只有标准提交(即不通过ajax方式提交),才可以上传附件
items: [{
xtype: 'hiddenfield',
id: 'attRefId',
name: 'entity.id'
},{
html: '<iframe id="ifrmUpload"name="ifrmUpload" style="display:none"></iframe>'
}, this.renderAttachInfo(this, null)],
listeners: {
afterrender: function(frm){
//设置form的属性
Ext.getDom('frmAttach').enctype = 'multipart/form-data';
Ext.getDom('frmAttach').method = 'POST';
Ext.getDom('frmAttach').target = 'ifrmUpload';
}
}
};
根据Sencha Touch技术框架的特点,前台展示的绝大部分数据都是通过ajax方式获取,譬如列表数据的获取、表单数据的获取等等。列表数据的获取,一般是通过store组件和list组件进行结合;表单数据的获取通常使用Ext.request方式获取。
列表数据的获取代码清单:
//数据列表
renderListData: function(){
//定义变量和函数的作用范围
var _scope = this;
var st = newExt.data.Store({
model: _scope.model, //store组件使用的Ext.data.Model
//sorters: 'crtdt',
autoLoad: true, //是否自动加载数据
proxy: {
type: 'ajax',
actionMethods: 'POST',
url:_scope.url, //数据获取的url地址
extraParams:_scope.extraParams, //请求的额外参数
startParam:'start',
limitParam:'limit',
reader: {
type: 'json', //reader类型-默认jsonReader,
root: undefined,//reader root-默认undefined
}
},
listeners: {
beforeload: function(st,oper){
oper.start = _scope.start; //设置分页起始记录
oper.limit = _scope.limit; //设置每页显示记录数
},
load: function(st,records, successful){
if(successful){
//获取后台返回的记录总数属性
_scope.totalNum =st.getProxy().getReader().rawData.total;
//分页按钮控制
_scope.controlPageButtons();
//回调外部函数-返回结果给外部函数
if(_scope.callbackFn){
_scope.callbackFn({listId:_scope.listId,total:_scope.totalNum});
}
}
}
}
});
//创建并返回list组件
return newExt.List({
id: _scope.listId,
//multiSelect: true,
//simpleSelect: true,
scroll: 'vertical',
store:st, //store组件对象
emptyText: '<divstyle="margin:2px;">'+_scope.emptyText+'</div>',
loadingText: '正在获取数据',
itemTpl: _scope.itemTpl, //列表显示的模板
listeners: {
itemtap: function(lt, idx, item,e){
//点击列表记录时执行的函数
if(_scope.itemTapFn){
var record =lt.getStore().getAt(idx);
_scope.itemTapFn({listId:_scope.listId,record:record}, _scope.fnScope);
}
}
}
});
}
通过Ext.request方式获取数据的代码清单:
//获取数据
fetchData: function(){
var _scope= this;
Ext.Ajax.request({
method: 'POST',
url: prefix + '/doc/letter/manager!fetchEditData.action',
params: {
'entity.id':_scope.entityId //传递给后台的参数
},
success: function(response,opts){
varobjResp = Ext.decode(response.responseText);
if(objResp.success){
varobjData = Ext.decode(objResp.result);
//调用自定义的函数把数据设置到form中
_scope.loadDataToForm(_scope,objData);
}else{
Ext.Msg.alert(objResp.result);
}
},
failure: function(response,opts){
Ext.Msg.alert('很抱歉,由于网络原因获取数据出错!');
}
});
}
鉴于目前移动网络的网速以及手机设备浏览器的处理能力,为了提高应用响应的速度与用户的操作体验,使用sencha touch开发移动应用时需要注意一些情况:
u 后台的数据传输要尽量精简,前台js书写需要尽量简洁。
u 界面要尽量精简以及少用图片,要保证应用的流畅,毕竟移动网络的网速远比不上宽带。
u 应用入口尽量要分模块,因为sencha touch整个应用只有一个入口的话,需要引入大量的js文件,这样可能会导致第一个页面的响应速度很慢(js文件需要下载到浏览器)。
u 一些通用功能尽量组件化,提高组件的重用。