Sencha Touch官網所給的例子還是很詳盡的,只要把代碼拷貝粘貼,稍微修改一下,就能用在自己的項目中了,或者仔細看官網給的例子,代碼註釋給的英文也不難,自學那些代碼,完全不需要看什麼視頻和書籍。有學習過Extjs那就更好了,因爲這兩個產品都是Sencha公司的,代碼風格幾乎一樣。
先看看Sencha CMD生成的項目中都有些什麼。
.sencha : 自動生成的配置文件,不知是玩意,先不管。
app : 裏面放着空的模型(model),視圖(view)和存儲(store)文件夾,再等着你編輯文件放進去呢!爲什麼會有這些文件夾,因爲Sencha Touch是以MVC模式來進行開發的。
build : 編譯後的文件存放在這個文件夾裏面,我們編寫完Sencha項目,可以編譯成原生的Android項目或者IOS項目的,用Sencha CMD可以,用phonegap更好,這是後話了。
packages :包文件夾,放什麼包的吧,我也不知道。
resources:這個比較重要,放css和圖片資源的。
touch(sdk):Sencha Touch SDK的拷貝,不要修改這個文件夾裏面的東西。
剩下的都不是文件夾了
app.js 主javascript入口
app.json 配置文件,用來創建你的應用的縮小版本
index.html 你的應用的HTML文件
packager.json 配置文件,用來打包成IOS和Android應用
bootstrap.js 流行的bootstrap框架應用文件
當我們訪問這個項目時,打開的是根目錄下的index.html文件,所以打開這個文件看一下里面的源代碼:
<span style="font-size:24px;"><!DOCTYPE HTML>
<html manifest="" lang="en-US">
<head>
<meta charset="UTF-8">
<title>first</title>
<style type="text/css">
/**
* Example of an initial loading indicator.
* It is recommended to keep this as minimal as possible to provide instant feedback
* while other resources are still being loaded for the first time
*/
html, body {
height: 100%;
background-color: #1985D0
}
#appLoadingIndicator {
position: absolute;
top: 50%;
margin-top: -15px;
text-align: center;
width: 100%;
height: 30px;
-webkit-animation-name: appLoadingIndicator;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: linear;
}
#appLoadingIndicator > * {
background-color: #FFFFFF;
display: inline-block;
height: 30px;
-webkit-border-radius: 15px;
margin: 0 5px;
width: 30px;
opacity: 0.8;
}
@-webkit-keyframes appLoadingIndicator{
0% {
opacity: 0.8
}
50% {
opacity: 0
}
100% {
opacity: 0.8
}
}
</style>
<!-- The line below must be kept intact for Sencha Command to build your application -->
<script id="microloader" type="text/javascript" src=".sencha/app/microloader/development.js"></script>
</head>
<body>
<div id="appLoadingIndicator">
<div></div>
<div></div>
<div></div>
</div>
</body>
</html></span>
非常簡單,只有css樣式代碼和一個引用的js文件,這樣就能豐富地表達我們的例子 ?
按住“ctrl”鍵,點擊development.js。跳轉到裏面看看。這個文件居然在.sencha文件夾裏面的。Javascript對於我來說不是擅長的語言,所以看這些代碼開始一頭霧水。源代碼下面有很多設備的名稱,看來是匹配這些設備,讓體驗更佳的。可是我們所看到的頁面的那些文字在這裏面一點都沒有涉及,難道還有引用文件,仔細一看,不小心還看不到呢,最後看到Ajax技術的異步請求。
而在touch\microloader\development.js中,指向的卻是
前者是CMD編譯產生的,後者是自己編寫項目用的。
進入bootstrap.json裏面
Sencha需要引用的文件從這引用。主入口的js和css文件都提示出來了。進入app.js裏面
這裏面居然也沒有界面展現的內容,直到Ext.Viewport.add(Ext.create('first.view.Main'));這一行。點擊進入Main.js文件,這個文件在app文件夾裏面的視圖層view文件夾裏面。打開
<span style="font-size:24px;">Ext.define('first.view.Main', {
extend: 'Ext.tab.Panel',
xtype: 'main',
requires: [
'Ext.TitleBar',
'Ext.Video'
],
config: {
tabBarPosition: 'bottom',
items: [
{
title: 'Welcome',
iconCls: 'home',
styleHtmlContent: true,
scrollable: true,
items: {
docked: 'top',
xtype: 'titlebar',
title: 'Welcome to Sencha Touch 2'
},
html: [
"You've just generated a new Sencha Touch 2 project. What you're looking at right now is the ",
"contents of <a target='_blank' href=\"app/view/Main.js\">app/view/Main.js</a> - edit that file ",
"and refresh to change what's rendered here."
].join("")
},
{
title: 'Get Started',
iconCls: 'action',
items: [
{
docked: 'top',
xtype: 'titlebar',
title: 'Getting Started'
},
{
xtype: 'video',
url: 'http://av.vimeo.com/64284/137/87347327.mp4?token=1330978144_f9b698fea38cd408d52a2393240c896c',
posterUrl: 'http://b.vimeocdn.com/ts/261/062/261062119_640.jpg'
}
]
}
]
}
});</span>
到此一切都明朗了!
我們打開index頁面,index加載的development.js,適配我們的設備,然後頁面展現進度的過程同時,異步請求bootstrap.json中的js文件和CSS文件,加載Sencha的js文件和樣式文件,加載進根目錄下的app.js文件,觸發first.view.Main,加載Main.js文件,從而真正展現Sencha的頁面出來,Main.js裏面的內容和我們看到的文字是一樣的,所以稍微修改一些內容,就會展現不同的內容出來。
<span style="font-size:24px;">Ext.define('first.view.Main', {
extend: 'Ext.tab.Panel',
xtype: 'main',
requires: [
'Ext.TitleBar',
'Ext.Video'
],
config: {
tabBarPosition: 'bottom',
items: [
{
title: '歡迎',
iconCls: 'home',
styleHtmlContent: true,
scrollable: true,
items: {
docked: 'top',
xtype: 'titlebar',
title: '歡迎來到 Sencha Touch'
},
html: [
"這是個Sencha Touch頁面"
].join("")
},
{
title: '視頻',
iconCls: 'action',
items: [
{
docked: 'top',
xtype: 'titlebar',
title: '請看視頻'
},
{
xtype: 'video',
url: 'http://av.vimeo.com/64284/137/87347327.mp4?token=1330978144_f9b698fea38cd408d52a2393240c896c',
posterUrl: 'http://b.vimeocdn.com/ts/261/062/261062119_640.jpg'
}
]
}
]
}
});</span>
=================================================================================
先不管其他的東西,app.js文件是我們的入口,按照MVC模式來編寫的,你仔細一看,還是能看到模型,控制,視圖分開的風格。Main.js是我們的視圖展示層,只不過需要單獨引用而已。要學習Sencha,對app.js這個文件進行修改,我們就可以簡單地學Sencha Touch了。
下面我們來學習並改一下demo中的源代碼,以list爲例子。
在手機上的表現是這樣的:
源代碼在touch-2.3.0\examples\list中。其中加入了一些個人理解的中文註釋,英語還可以的可看官方的註釋,寫得很簡單明白。
<span style="font-size:24px;">//<debug>
Ext.Loader.setPath({
'Ext': '../../src'
});
//</debug>
/**
* This simple example shows the ability of the Ext.List component.
*
* In this example, it uses a grouped store to show group headers in the list. It also
* includes an indicator so you can quickly swipe through each of the groups. On top of that
* it has a disclosure button so you can disclose more information for a list item.
*/
//define the application
//定義程序,入口
Ext.application({
//define the startupscreens for tablet and phone, as well as the icon
//根據移動設備的分辨率來選擇不同的圖片
startupImage: {
'320x460': 'resources/startup/Default.jpg', // Non-retina iPhone, iPod touch, and all Android devices
'640x920': 'resources/startup/640x920.png', // Retina iPhone and iPod touch
'640x1096': 'resources/startup/640x1096.png', // iPhone 5 and iPod touch (fifth generation)
'768x1004': 'resources/startup/768x1004.png', // Non-retina iPad (first and second generation) in portrait orientation
'748x1024': 'resources/startup/748x1024.png', // Non-retina iPad (first and second generation) in landscape orientation
'1536x2008': 'resources/startup/1536x2008.png', // : Retina iPad (third generation) in portrait orientation
'1496x2048': 'resources/startup/1496x2048.png' // : Retina iPad (third generation) in landscape orientation
},
isIconPrecomposed: false,
icon: {
57: 'resources/icons/icon.png',
72: 'resources/icons/[email protected]',
114: 'resources/icons/[email protected]',
144: 'resources/icons/[email protected]'
},
//require any components/classes what we will use in our example
//需要用到的組件
requires: [
'Ext.MessageBox',
'Ext.data.Store',
'Ext.List',
'Ext.plugin.PullRefresh'
],
/**
* The launch method is called when the browser is ready, and the application can launch.
*
* Inside our launch method we create the list and show in in the viewport. We get the lists configuration
* using the getListConfiguration method which we defined below.
*
* If the user is not on a phone, we wrap the list inside a panel which is centered on the page.
*/
//觸發,英文上說得很淺顯易懂
launch: function() {
//get the configuration for the list
//list界面對象
var listConfiguration = this.getListConfiguration();
//if the device is not a phone, we want to create a centered panel and put the list
//into that
//phone與其他設備是有區別的,源於iphone的分辨率比較特殊
if (!Ext.os.is.Phone) {
//use Ext.Viewport.add to add a new component into the viewport
Ext.Viewport.add({
//give it an xtype of panel
xtype: 'panel',
//give it a fixed witdh and height
width: 350,
height: 370,
//make it centered
centered: true,
//make the component modal so there is a mask around the panel
modal: true,
//set hideOnMaskTap to false so the panel does not hide when you tap on the mask
hideOnMaskTap: false,
//give it a layout of fit so the list stretches to the size of this panel
layout: 'fit',
//insert the listConfiguration as an item into this panel
items: [listConfiguration]
});
} else {
//if we are a phone, simply add the list as an item to the viewport
Ext.Viewport.add(listConfiguration);
}
},
/**
* Returns a configuration object to be used when adding the list to the viewport.
*/
//界面對象,返回到面板裏面填充,從而有內容展現
getListConfiguration: function() {
//create a store instance
//存儲器
var store = Ext.create('Ext.data.Store', {
//give the store some fields
fields: ['firstName', 'lastName'],
//filter the data using the firstName field
sorters: 'firstName',
//autoload the data from the server
autoLoad: true,
//setup the grouping functionality to group by the first letter of the firstName field
grouper: {
groupFn: function(record) {
return record.get('firstName')[0];
}
},
//setup the proxy for the store to use an ajax proxy and give it a url to load
//the local contacts.json file
//異步加載數據,這裏可以改編成與後臺的controller交互,controller從數據庫中獲取數據
proxy: {
type: 'ajax',
url: 'contacts.json'
}
});
return {
//give it an xtype of list for the list component
xtype: 'list',
id: 'list',
// scrollable: {
// indicators: false
// },
//set the itemtpl to show the fields for the store
//從store裏面提取的數據怎麼展現
itemTpl: '{firstName} {lastName}',
//enable disclosure icons
//disclosure: true,
//group the list
//分組
grouped: true,
//enable the indexBar
indexBar: true,
infinite: true,
useSimpleItems: true,
variableHeights: true,
striped: true,
//ui主題
ui: 'round',
//set the function when a user taps on a disclsoure icon
// onItemDisclosure: function(record, item, index, e) {
// //show a messagebox alert which shows the persons firstName
// e.stopEvent();
// Ext.Msg.alert('Disclose', 'Disclose more info for ' + record.get('firstName'));
// },
//bind the store to this list
store: store
};
}
});</span>
看一下存儲器store請求的異步數據,這是一個靜態數據contacts.json文件,這裏我們當然可以用各種後臺技術把關係型數據庫轉換爲json數據傳到這裏來,或者一些key-value數據庫直接傳過來,也可以經過過濾。
<span style="font-size:24px;">[
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Ape", "lastName": "Evilias" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Ape", "lastName": "Evilias" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Ape", "lastName": "Evilias" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Ape", "lastName": "Evilias" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Tommy", "lastName": "Maintz" },
{ "firstName": "Ed", "lastName": "Spencer" },
{ "firstName": "Jamie", "lastName": "Avins" },
{ "firstName": "Aaron", "lastName": "Conran" },
{ "firstName": "Dave", "lastName": "Kaneda" },
{ "firstName": "Michael", "lastName": "Mullany" },
{ "firstName": "Abraham", "lastName": "Elias" },
{ "firstName": "Jay", "lastName": "Robinson" },
{ "firstName": "Zed", "lastName": "Zacharias "}
]</span>
看到這裏,事情就變得很簡單了,按照這個例子稍微改一下就可以用了,至於各個參數的含義,還有其他的各種參數,要參考官網的API文檔纔可以。Sencha Touch在這一點不夠Extjs好,Extjs可以離線看整個API文檔,瀏覽速度非常快,可是Sencha Touch卻要聯網到它的官網上查看,我所在的這個地方瀏覽官網非常慢,不知是不是地區網絡的問題,反正想看一下API文檔非常困難,以後想辦法找個離線的纔好,即使低版本的也好!
我們改這個例子,成爲中文的通訊錄列表。
先修改contacts.json文件,這些都是靜態數據,純粹無腦機械編輯,這應該留給後臺靈活返回數據顯示出來的,不過剛入門,還是以靜態數據爲例子吧!把這個文件放到我們的項目的根目錄下。
[
{ "name": "李四", "age": 23, "address":"中山路1號" },
{ "name": "王五", "age": 26, "address":"中山路12號" },
{ "name": "王五1", "age": 34, "address":"中山路18號" },
{ "name": "王五2", "age": 55, "address":"中山路156號" },
{ "name": "王五3", "age": 34, "address":"解放路34號" },
{ "name": "王五4", "age": 12, "address":"解放路45號" },
{ "name": "王五5", "age": 89, "address":"解放路67號" },
{ "name": "王五6", "age": 45, "address":"解放路93號" },
{ "name": "趙六", "age": 67, "address":"解放路57號" },
{ "name": "王7", "age": 23, "address":"白雲路3號" },
{ "name": "張三", "age": 67, "address":"昆明路78號" },
{ "name": "趙霽", "age": 34, "address":"解放路98號" },
{ "name": "周麗", "age": 56, "address":"解放路175號" },
{ "name": "李賢", "age": 34, "address":"解放路876號" },
{ "name": "孫琦", "age": 56, "address":"解放路32號" },
{ "name": "黃連", "age": 28, "address":"解放路78號" },
{ "name": "張麗", "age": 25, "address":"解放路45號" },
{ "name": "里人", "age": 22, "address":"解放路89號" },
{ "name": "方琪", "age": 23, "address":"解放路23號" },
{ "name": "李燕", "age": 34, "address":"中山路4號" }
]
備份app.js文件,接着改app.js文件內容。不熟悉的話一個個敲,培養一下敲碼的感覺。
雖然是照抄右邊的,可是邊敲邊加深了理解。這裏推薦用intellij這個IDE,因爲不用安裝插件就可以提示Sencha Touch函數、參數等信息了,非常智能!
改的app.js如下:
<span style="font-size:24px;">Ext.application({
startupImage: {
'320x460': 'resources/startup/Default.jpg',
'640x920': 'resources/startup/640x920.png',
'640x1096': 'resources/startup/640x1096.png',
'768x1004': 'resources/startup/768x1004.png',
'748x1024': 'resources/startup/748x1024.png',
'1536x2008': 'resources/startup/1536x2008.png',
'1496x2048': 'resources/startup/1496x2048.png'
},
isIconPrecomposed: false,
icon: {
57: 'resources/icons/icon.png',
72: 'resources/icons/[email protected]',
114: 'resources/icons/[email protected]',
144: 'resources/icons/[email protected]'
},
requires: [
'Ext.MessageBox',
'Ext.data.Store',
'Ext.List',
'Ext.plugin.PullRefresh'
],
launch : function() {
var listConfiguration = this.getListConfiguration();
if (!Ext.os.is.Phone) {
Ext.Viewport.add({
xtype: 'panel',
width: 350,
height: 370,
centered: true,
modal: true,
hideOnMaskTap: false,
layout: 'fit',
items: [listConfiguration]
});
} else {
Ext.Viewport.add(listConfiguration);
}
},
getListConfiguration : function() {
var store = Ext.create('Ext.data.Store',{
fields:['name','age','address'],
sorters:'name',
autoLoad:true,
grouper:{
groupFn:function(record) {
return record.get('name')[0];
}
},
proxy:{
type:'ajax',
url:'contacts.json'
}
});
return {
xtype:'list',
id:'list',
itemTpl:'{name}{age}{address}',
grouped:true,
indexBar:true,
infinite:true,
useSimpleItems:true,
variableHeights:true,
striped:true,
ui:'round',
store:store
}
}
});</span>
在電腦上顯示如下:
在手機上顯示如下: