在開始“雜貨鋪”app編程之前,理解 NativeScript app 的文件夾結構是比較重要的。這有助于你了解在哪里放置新文件,又能理解一些在 NativeScript 內部將要發生事情。
那就用你的文本編輯器打開你的app文件夾 `sample-Groceries` ,我們繼續深入。
## [**本節內容**](http://docs.nativescript.org/tutorial/chapter-2#table-of-contents)
* [2.1: Directory structure 目錄結構](#21-目錄結構)
* [2.2: Adding UI components 添加UI組件](#2-2)
* [2.3: Layouts 布局](#2-3)
* [2.4: CSS ](#2-4)
* [2.5: Images 圖片](#2-5)
## **[2.1: 目錄結構](http://docs.nativescript.org/tutorial/chapter-2#21-directory-structure)**
為了便于理解,我們先看雜貨鋪app的外層結構:
> .
> └── sample-Groceries
> ├── app
> │ └── ...
> ├── node\_modules
> │ └── tns-core-modules
> ├── package.json
> └── platforms
> ├── android
> └── ios
這些文件和文件夾的作用如下:
* **app**: 這個文件夾包含你制作app的所有需要的開發資源。在這里面你會花掉你大多數編寫時間。
* **node\_modules**: 這個文件夾包含你app的npm模塊依賴。所有新的 NativeScript 項目都從一個單獨的模塊—— tns-core-modules 。
* **node\_modules\/tns-core-modules**: 這個文件夾包含你app的 NativeScript 模塊,是一系列你要用于制作app的由NativeScript 提供的 JavaScript 模塊。每個模塊都包含特定平臺實現一些功能 的代碼——相機,http請求,文件系統,等等——通過暴露一個平臺無關的API \(比如`camera.takePicture()`\) 。我們一會就會看到一些例子。
* **package.json**: 這個文件包含你app的詳細設置,比如ID,你使用的 NativeScript 版本,以及app使用的npm模塊。 我們會在 [chapter 5](http://docs.nativescript.org/tutorial/chapter5). 講到使用npm模塊的時候詳細看看如何使用這個文件。
* **platforms**: 這個文件夾包含 NativeScript 用于搭建原生ios和安卓應用必須的特定平臺代碼。比如在 `android` 文件夾里你會發現類似項目的 `AndroidManifest.xml` 和 .apk 執行文件。同樣, `ios` 文件夾包含雜貨店的 Xcode 項目和 .ipa 可執行文件。注意,使用Windows的用戶電腦不會有 `ios 文件夾。`
當你開發和運行app時NativeScript CLI為你管理`platforms`文件夾;因此,最好的做法是把 `platforms`文件夾作為生成的代碼。雜貨鋪app包含 `platforms`文件夾 并在 `.gitignore` 文件里從源碼管理排除它。
接下來我們深入了解app文件夾,因為你會在里面花掉你大部分時間:
> . └── sample-Groceries
> ├── app
> │ ├── App\_Resources
> │ │ ├── Android
> │ │ └── iOS
> │ ├── shared
> │ │ └── ...
> │ ├── views
> │ │ └── login
> │ │ ├── login.js
> │ │ └── login.xml
> │ ├── app.css
> │ ├── app.js
> │ └── ...
> └── ...
這些文件和文件夾的作用如下:
* **App\_Resources**:這個文件夾包含特定平臺的資源,比如圖標,splash screens,配置文件等。 當你運行 `tns run時,`NativeScript CLI 把 這些資源 小心注入到 `platforms` 文件夾里合適的位置。
* **shared**: 這個文件夾,特定于雜貨店app,包含那些在你app里需要跨視圖共享的任意文件,你可以找到一些 view model 對象和用于共享配置變量(如 API keys )的 `config.js` 文件。
* **views**: 這個文件夾包含創建你app視圖的代碼,每個視圖在 `views` 里都有一個文件夾。每個視圖由一個 XML 文件,一個 JavaScript 文件,和一個可選的 CSS 文件組成。雜貨店app包含三個視圖所以有三個文件夾。
* **app.css**: 這個文件包含了你app的全局樣式。我們會在 [chapter 2.4](http://docs.nativescript.org/tutorial/chapter-2#24-css). 里詳細探討。
* **app.js**:這個文件設置你應用的啟動模塊和初始化app。
## **[2.2: Adding UI components](http://docs.nativescript.org/tutorial/chapter-2#22-adding-ui-components)** 添加組件 {#2-2}
我們深入到創建appUI的文件,它們位于 `app/views` 文件夾。每個 `app/views` 里的文件夾都包含雜貨店三個頁面之一的代碼: `list`, `login`, 和`register` 。如果你打開 `app/views/login` 文件夾,會看到我們上一章更新的三個文件: `login.css`, `login.js`, 和`login.xml` 。再次打開 `login.xml` 你會看到下面的代碼:
> `<Page>
> <Label text="hello NativeScript" />
> </Page>`
這個頁面目前包含兩個UI組件:一個`<Page>` 和一個 `<Label>` 。為了讓這個頁面看起來更像登錄頁面,我們來增加一些另外的組件,兩個 `<TextField>` 元素和兩個 `Button` 元素。
#### **操作: ** 向`login.xml` **添加 UI 組件**
打開 app/views/login/login.xml 文件并用下面的代碼替換現有的`<Label>`:
`<TextField hint="Email Address" keyboardType="email" autocorrect="false" autocapitalizationType="none" /> `
`<TextField hint="Password" secure="true" /> `
`<Button text="Sign in" /> `
`<Button text="Sign up for Groceries" />`
NativeScript UI組件提供一些屬性讓你設置其行為和表現。你剛才添加的代碼使用了下面的屬性:
* `<TextField>`
* `hint`: 顯示占位字符用來告訴用戶輸入什么。
* `keyboardType`: 用戶目前輸入時的鍵盤類型。`keyboardType="email"` 顯示優化的用于輸入郵件地址的鍵盤。 NativeScript 現在支持 五種類型的[ keyboards](http://docs.nativescript.org/ui/keyboard.html) 。
* `autocorrect`: 一個布爾屬性,用于判斷系統是否自動更正用戶的輸入。如果是郵件地址輸入框,自動更正的行為是不太受歡迎的。(應該是自動更正英文的吧?)
* `autocapitalizationType`: 決定系統如何讓用戶的輸入大小寫適配。 `autocapitalizationType="none"` 關閉所有的大小寫適配。 NativeScript 支持 四種文本域的[ autocapitalization類型。](http://docs.nativescript.org/api-reference/modules/_ui_enums_.autocapitalizationtype.html)
* `secure`:該布爾屬性決定是否掩蓋文本框的文字,這通常在 password 欄使用。
* `<Button>`
* `text`: 控制 button 內嵌現實的文字。
在更改后再次啟動app,你會看到一個孤獨的 `<Button>` 組件出現在屏幕上:

目前你只看到一個單獨的按鈕是因為你還沒有告訴 NativeScript 如何布局你頁面的UI組件。我們來看下如何使用 NativeScript 的**布局**來安排屏幕上的這些組件:
> ### 提示:
>
> NativeScript文檔包含了你用于zhizuoapp的[完整的UI組件和屬性](http://docs.nativescript.org/ui/basics)。你甚至可以[創建自己的UI組件](http://docs.nativescript.org/ui/basics#custom-components)。
## **[2.3: Layouts ](http://docs.nativescript.org/tutorial/chapter-2#23-layouts)**布局 {#2-3}
NativeScript提供幾個不同的布局容器,允許你在你想要的位置精確地放置你的UI組件。
* [Absolute Layout](http://docs.nativescript.org/cookbook/ui/layouts/absolute-layout) 允許你用X-Y坐標放置元素。這個在你要把元素放到特別的位置時很有用,比如要在app的左上角顯示一個 activity indicator widget (動態指示小掛件)。
* [Dock Layout](http://docs.nativescript.org/cookbook/ui/layouts/dock-layout) 用于放置UI元素到外層邊框。例如,一個停靠在屏幕底部的容器會是顯示廣告的好位置。
* [Grid Layout](http://docs.nativescript.org/cookbook/ui/layouts/grid-layout) 讓你把你的界面分割成若干行和列,比較類似HTML里 `<table>` 干的事情。
* [Stack Layout](http://docs.nativescript.org/cookbook/ui/layouts/stack-layout) 讓你橫向或縱向堆碼UI子組件。
* The [Wrap Layout](http://docs.nativescript.org/cookbook/ui/layouts/wrap-layout) 當一行或列填滿時子組件順應到下一行或列。
對于你的login頁面,你需要的就是一個簡單的 `<StackLayout>` 來從上到下堆碼所有的UI組件。在后面的部分,你會使用一些更高級的布局。
### 操作:添加一個 **stack layout** 到login頁面
在 `login.xml里,添加一個<StackLayout>組件`到`<Page>組件里面。` `login.xml` 看起來會是這樣了:
> `<Page>`
>
> `<StackLayout orientation="vertical">`
>
> `<TextField hint="Email Address" keyboardType="email" autocorrect="false" autocapitalizationType="none" />`
>
> `<TextField hint="Password" secure="true" />`
>
> `<Button text="Sign in" />`
>
> `<Button text="Sign up for Groceries" />`
>
> `</StackLayout>`
>
> `</Page>`
stack layout 是個UI 組件,因此,它同你在之前部分用到的 `<TextField>` 和`<Button>` 組件一樣具有屬性。這里, `orientation="vertical"` 屬性告訴 stack layout 垂直安排子組件。
改動之后運行app ,你會看到 login 頁面的UI組件垂直排列了:

既然UI組件有了正確的排列順序,它們就可以用一些顏色和空間來讓app好看一點了。為此我們來看 NativeScript 的另一個特質:CSS。
### **提示:**
* 參考 NativeScript 官方文檔了解更詳盡的 [NativeScript layouts](http://docs.nativescript.org/layouts) 和你可以對它們的各種操作。
* 看看Jen Looper's 在[demystifying NativeScript layouts](https://www.nativescript.org/blog/demystifying-nativescript-layouts) 的文章f深入了解NativeScript layouts in action.的運用。
## **[2.4: CSS](http://docs.nativescript.org/tutorial/chapter-2#24-css)** {#2-4}
NativeScript使用了CSS的一個子集來改變你app的視覺效果。你可以用三種機制為UI組件添加CSS屬性: 應用通用的[ CSS](http://docs.nativescript.org/styling#application-wide-css) \(`app.css`\), 特定頁面的[ CSS](http://docs.nativescript.org/styling#page-specific-css), 和內嵌的[ ](http://docs.nativescript.org/styling#inline-css)`style` 屬性。
### **提示:**
* `在app.css` 里添加CSS規則會對所有頁面生效,特定的CSS文件 \(比如`login.css`\)的CSS規則只會對單一頁面生效。
* 雖然內嵌style便于快速測試——比如 `<Page style="background-color: green;">` ,你一般不要這樣干,因為 style 屬性會讓xml文件代碼凌亂,尤其你在需要生命復雜樣式時。
我們從添加一些 應用通用的 CSS 規則 \(`app.css`\),開始。
### 操作:創建全局樣式
復制以下代碼到 `app.css` 文件:
`Page { background-color: white; font-size: 17; }`
`TextField { margin: 10; padding: 10; }`
`Image { margin-top: 20; margin-left: 0; margin-right: 0; margin-bottom: 80; }`
`Button { margin: 10; padding: 10; }`
如果你此前做過一些web開發,這里的語法一定很熟悉。你通過標簽名選定了四類UI組件 \(Page, TextField, Image, 和 Button\) ,然后以 名稱—值(鍵值對)的形式聲明了若干CSS規則。 NativeScript不支持所有的CSS屬性,因為在原生app里繼續沿用其中一些會帶來性能問題。官方給出了一份[支持的全部CSS屬性的列表。](http://docs.nativescript.org/ui/styling#supported-properties)
我們再來做點改動。雖然你常希望CSS規則對ios和android app一樣有效,但偶爾使用的CSS規則只對一個平臺有作用。比如,ios的 文本域( text fields) 往往自帶邊框,而android的卻不會。我們來看下在 NativeScript 里如何實現特定平臺的style樣式改變。
### 操作:添加特定平臺的CSS
將下面的代碼添加到`app.css文件第一行:`
`@import { url('~/platform.css') };`
### 注意:
**在@ import語句必須先于所有其他的CSS規則條件下, NativeScript 與瀏覽器的表現一致。**
接著,在 `login.xml` 里為 sign up按鈕添加一個 `class="link"` 屬性該按鈕的標記應該是這樣的:
`<Button text="Sign up for Groceries" class="link" />`
我們分解下剛才發生的情況。首先, NativeScript 支持CSS的 `@import` 語句把一個CSS文件導入另一個。新的這行代碼把 `platform.css` 的CSS規則導入到了 `app.css` 。但是,你或許注意到雜貨店沒有一個名為 `platform.css` 的文件—— 只有`app/platform.android.css` 和 `app/platform.ios.css存在。究竟發生什么事了?`
當你執行 `tns run 或 tns livesync,`NativeScript CLI 從app文件夾取出你的代碼放到位于 `platforms/ios` 和 `platforms/android文件夾的`原生工程里。接著是命名轉換出馬:移動文件的時候, CLI聰明地選擇 `.android.*` 和 `.ios.*` 文件。舉個例子, CLI 移動`platform.ios.css 到 platforms/ios` 并將其命名為 `platform.css;同樣`, CLI 移動 `platform.android.css 到 platforms/android`,,并將其命名為`platform.css。`這個轉換提供了一個便捷的方式分發你的代碼來單獨地處理iOS和Android,它支持 NativeScript 里的所有文件——不只是CSS文件。你之后在本教程里會看到更多的例子。
下面是另一個我們要討論的改動,就是你在這個按鈕上添加的 `class` 屬性:
> `<Button text="Sign up for Groceries" class="link" /> `
NativeScript 使用class屬性為UI組件添加CSS類名。這個類名用來給 sign up 按鈕一個輕微區別于 sign in 按鈕的外觀。 你可以根據類名在 `platform.ios.css` 和`platform.android.css`里找到相應的CSS規則:
`/* From platform.android.css */`
`.link { background-color: transparent; }`
`/* From platform.ios.css */`
`.link { border-width: 0; }`
提示: NativeScript 也支持以 `id` 屬性選擇元素。請參考官方文檔 [a full list of the supported selectors](http://docs.nativescript.org/styling#supported-selectors).。
有了這些改動,你會發現app看起來也像模像樣了,而且在ios和android上看起來有明顯不同:

在我們繼續之前,花點時間任意地擺弄app的外觀。你可以嘗試添加一些另外的CSS類名,或者在你的 `login.css` 文件里添加特定頁面的樣式。如果你弄完了,我們接下來向登錄頁面添加一張圖片。
## **[2.5: Images](http://docs.nativescript.org/tutorial/chapter-2#25-images)**圖片 {#2-5}
在 NativeScript 里,你用 `<Image>` UI組件和它的 `src` 屬性向頁面添加圖片。 `src` 屬性 允許你以三種方式指定圖片。第一種方式是指向一個圖片的 URL 地址:
`<Image src="https://www.nativescript.org/images/default-source/landingpages/logo.png" />`
第二種方式是指向一張位于你app的 `app` 文件夾的圖片。比如你有一張圖片位于 `app/images/logo.png` ,你可以這樣調用它:
`<Image src="~/images/logo.png" />`
第三種方式,也是雜貨店采用的,是使用特定平臺的圖片資源。我們添加一張圖片到登錄頁面再好好討論發生了什么。
#### **操作:添加一個LOGO**
`login.xml`, 在現有的 `<StackLayout>` 標簽下面添加`<Image>` 作為其第一個子組件:
`<Image src="res://logo" stretch="none" horizontalAlignment="center" />`
`這里的 res://`語法告訴 NativeScript 使用一個特定平臺的資源,這里是個圖片特定平臺的資源在你app的 `app/App_Resources` 文件夾。如果你看看這里就會發現一些不同的圖片文件, 其中有幾個名叫 `logo.png。`
雖然比直接往 `app` 文件夾放張圖片復雜些,使用平臺特定的圖片讓你更多地能控制圖片在不同尺寸設備上的表現。例如,IOS讓你為不同像素密度的設備提供三張不同的圖片。由此你在 `App_Resources/iOS` 文件夾會找到命名為 `logo.png`, `logo@2x.png`, 和 `logo@3x.png` 的LOGO圖片。對于android,類似的圖片位于 `App_Resources/Android/drawable-hdpi` \(高dpi\), `App_Resources/Android/drawable-mdpi` \(中dpi\),和`App_Resources/Android/drawable-ldpi` \(低dpi\) 。
一旦這些文件就位,NativeScript 框架知道如何選取正確的文件;需要你做的只是用 `res://` 和基文件名指向圖片——例如 `res://logo` 。下面是你的登錄界面在IOS和android上的樣子:

到這里你的UI看起來不錯了,但這個app仍然不能做任何事情。我們來看看你能如何使用 JavaScript 增加一些功能。
### 提示:
> **由社群編寫的 **[NativeScript Image Builder](http://nsimage.brosteins.com/)**可以幫你生成IOS和Android的合適分辨率的圖片。**