# [**Architecture and Navigation**](http://docs.nativescript.org/core-concepts/navigation#architecture-and-navigation)結構和導航
NativeScript 由代表獨立應用界面的頁面構成。頁面是 `Page` 模塊的 `page` 類的實例。要在頁面間導航切換,你可以使用 `Frame` 模塊的 `Frame` 類的方法。
> **提示:你可以在一個單獨頁面里使用 \*\***[tab view](http://docs.nativescript.org/api-reference/classes/_ui_tab_view_.tabview.html)**\*\* 并在每個tab里使用不同的用戶子界面來代替多頁面。**
* Page management
* Define page
* Set home page
* Navigation
* The topmost frame
* Navigate by page name
* Navigate using a function
* Navigate and pass context
* Navigate and set bindingContext to the page
* Navigate without history
* Clear history
* Navigation transitions
* Navigate back
* Modal pages
* Supporting multiple screens
* Screen size qualifiers
* Platform qualifiers
* Orientation qualifiers
## [**Page management**](http://docs.nativescript.org/core-concepts/navigation#page-management)頁面管理
### [**Define page**](http://docs.nativescript.org/core-concepts/navigation#define-page)定義頁面
頁面代表你應用的不同界面。每個頁面都是 `Page` 模塊的 `page` 類的一個實例 。每個類實例繼承了保存用戶界面的根視覺元素的內容屬性。nativescript提供實例化你的頁面的兩種方法。
**在XML里面創建頁面**
您可以單獨定義頁面的用戶界面聲明和代碼。
要應用這種方法,為每個頁面創建一個XML文件來保持頁面的布局。你的代碼將位于一個JS或TS文件里。XML和JS或TS文件名稱必須匹配。
### [**Example 1: Create page with XML.**](http://docs.nativescript.org/core-concepts/navigation#example-1--create-page-with-xml)用XML創建頁面
> ### **XML**
>
> ---
>
> `<!-- main-page.xml-->`
>
> `<Page loaded="onPageLoaded">`
>
> `<Label text="Hello, world!"/>`
>
> `</Page>`
>
> ### **JS**
>
> ---
>
> `// main-page.js`
>
> `function onPageLoaded(args) {`
>
> `console.log("Page Loaded");`
>
> `}`
>
> `exports.onPageLoaded = onPageLoaded;`
**在代碼里創建頁面**
要使用這種方法,你需要創建一個叫做 `createPage` 的函數用來返回一個頁面的實例。 NativeScript 把 `createPage` 作為一個生產環境函數。
### [**Example 2: Create page via code.**](http://docs.nativescript.org/core-concepts/navigation#example-2--create-page-via-code)通過代碼創建頁面
> ### **JS**
>
> ---
>
> `var pagesModule = require("ui/page");`
>
> `var labelModule = require("ui/label");`
>
> `function createPage() {`
>
> `var label = new labelModule.Label();`
>
> `label.text = "Hello, world!";`
>
> `var page = new pagesModule.Page();`
>
> `page.content = label; return page;`
>
> `}`
>
> `exports.createPage = createPage;`
### [**Set home page**](http://docs.nativescript.org/core-concepts/navigation#set-home-page)設置首頁
每個應用必須有個單獨的入口——首頁。
要加載應用的首頁,你需要用需要的 模塊名稱 傳遞 `NavigationEntry路由入口` 給 start\(\) 方法。 NativeScript 會用指定的名稱尋找XML文件,解析它并按照文件的描述繪制UI。之后,如果 NativeScript 找到同名的 `JS` 或 `TS` 文件,機會執行文件里的業務邏輯。
> ### JS
>
> ---
>
> `var application = require("application");`
>
> `application.start({ moduleName: "main-page" });`
## [**Navigation導航**](http://docs.nativescript.org/core-concepts/navigation#navigation)
`Frame`類代表負責在不同頁面間導航的邏輯單元。通常,每個App都有一個根級別的frame—頂層frame。
要在頁面間導航切換,你可以使用頂層frame實例的 `navigate` 方法。
此外,每個 `Page` 實例會在 `frame` 屬性里攜帶導航至它的frame對象的信息。這允許你也使用frame屬性來導航。
### [**頂層frame**](http://docs.nativescript.org/core-concepts/navigation#the-topmost-frame)
頂層frame是你app UI的根級容器,你可以使用它在你的app里來導航。你能通過使用 Frame 模塊的 `topmost()` 方法來獲得這個frame的引用。
> **JS**
>
> ---
>
> `var frameModule = require("ui/frame");`
>
> `var topmost = frameModule.topmost();`
這里有進行導航的一些方法,使用哪個要基于你app的需求而定。
### [**Navigate by page name通過頁面名稱導航**](http://docs.nativescript.org/core-concepts/navigation#navigate-by-page-name)
可能通過指定要導航到的頁面的文件名\(不帶.\*,只是文件名喲\),是導航的最簡單的方法了。
> **JS**
>
> ---
>
> `topmost.navigate("details-page");`
### [**Navigate using a function使用函數導航**](http://docs.nativescript.org/core-concepts/navigation#navigate-using-a-function)
更加動態的導航方法可以是通過提供一個函數,該函數返回你要導航到的頁面的實例。
### [**Example 3: How to navigate to a page dynamically created via code.**](http://docs.nativescript.org/core-concepts/navigation#example-3--how-to-navigate-to-a-page-dynamically-created-via-code)
> **JS**
>
> ---
>
> `var factoryFunc = function () {`
>
> `var label = new labelModule.Label();`
>
> `label.text = "Hello, world!";`
>
> `var page = new pagesModule.Page();`
>
> `page.content = label;`
>
> `return page;`
>
> `};`
>
> `topmost.navigate(factoryFunc);`
### [**Navigate and pass context導航并傳遞上下文**](http://docs.nativescript.org/core-concepts/navigation#navigate-and-pass-context)
當你導航到另一個頁面時,你可以用一個 `NavigationEntry` 對象來傳遞上下文到這個頁面。對比其他導航方法而言,這個方法提供了更好的對導航的控制。例如,通過 `NavigationEntry` 你可以實現導航動畫。
### [**Example 4: 如何在不同頁面間傳遞內容.**](http://docs.nativescript.org/core-concepts/navigation#example-4--how-to-pass-content-between-different-pages)
> **JS**
>
> ---
>
> `var navigationEntry = {`
>
> `moduleName: "details-page",`
>
> `context: {info: "something you want to pass to your page"},`
>
> `animated: false`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
### [**導航并設置頁面的綁定上下文**](http://docs.nativescript.org/core-concepts/navigation#navigate-and-set-bindingcontext-to-the-page)
當你導航時,你可以設置綁定上下文到一個頁面。
### [**Example 5: 當導航到一個頁面時如何自動地提供 **](http://docs.nativescript.org/core-concepts/navigation#example-5--how-to-provide-bindingcontext-automaticlly-while-navigating-to-a-page)`bindingContext`[** **](http://docs.nativescript.org/core-concepts/navigation#example-5--how-to-provide-bindingcontext-automaticlly-while-navigating-to-a-page)
> **JS**
>
> ---
>
> `// To import the "ui/frame" module and "main-view-model":`
>
> `var frame = require("ui/frame");`
>
> `var main_view_model = require("./main-view-model");`
>
> `// Navigate to page called “my-page” and provide "bindingContext"`
>
> `frame.topmost().navigate({`
>
> `moduleName: "my-page",`
>
> `bindingContext: new main_view_model.HelloWorldModel()`
>
> `});`
#### **示例**
在下面這個例子里,這個 master-details app包括兩個頁面。 main 頁面包含一個實物的列表。 details 頁面則顯示當前選擇的實物的信息。
當你導航到 details 頁面,你傳送了被選擇的實物的一個主key或ID。
### [**Example 6: 導航到詳情頁面并為選擇項目傳遞內容**](http://docs.nativescript.org/core-concepts/navigation#example-6--navigate-to-the-details-page-and-pass-the-content-for-selected-item)
> **JS**
>
> ---
>
> `function listViewItemTap(args) {`
>
> `// Navigate to the details page with context set to the data item for specified index`
>
> `frames.topmost().navigate({`
>
> `moduleName: "cuteness.io/details-page",`
>
> `context: appViewModel.redditItems.getItem(args.index)`
>
> `});`
>
> `}`
通過 **onNavigatedTo** 回調函數, 你顯示這個實物的信息。
### [**Example 7: 綁定從主頁面收到的內容**](http://docs.nativescript.org/core-concepts/navigation#example-7--bind-the-content-received-from-main-page)
> **JS**
>
> ---
>
> `function pageNavigatedTo(args) {`
>
> `var page = args.object;`
>
> `page.bindingContext = page.navigationContext;`
>
> `}`
### [**Navigate without history不帶歷史記錄導航**](http://docs.nativescript.org/core-concepts/navigation#navigate-without-history)
你可以導航到一個頁面時不添加這個導航到歷史記錄。設置 `NavigationEntry` 的 `backstackVisible` 屬性為 false。如果該屬性設置為false,頁面會顯示,但一旦從該頁面導航出去則不能再導航回來了。
### [**Example 8: 頁面導航,不保存導航歷史記錄.\_\_>**](http://docs.nativescript.org/core-concepts/navigation#example-8--page-navigation-without-saving-navigation-history__)
> **JS**
>
> ---
>
> `var navigationEntry = {`
>
> `moduleName: "login-page",`
>
> `backstackVisible: false`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
### [**Clear history清空導航歷史記錄**](http://docs.nativescript.org/core-concepts/navigation#clear-history)
你可以導航到一個新頁面并決定完全清空全部導航歷史。設置 `NavigationEntry` 的 `clearHistory` 屬性為 true。這會阻止用戶退回到先前訪問過的頁面。如果你有一個多頁面身份驗證過程,并且一旦用戶成功登錄并重定向到起始頁面,你要清除身份驗證頁面時,這是相當有用的。
### **[Example 9: 使用](http://docs.nativescript.org/core-concepts/navigation#example-9--prevent-user-from-going-back-using-clearhistory-property)** `clearHistory`**[屬性阻止用戶回退](http://docs.nativescript.org/core-concepts/navigation#example-9--prevent-user-from-going-back-using-clearhistory-property)**
> **JS**
>
> ---
>
> `var navigationEntry = {`
>
> `moduleName: "main-page",`
>
> `clearHistory: true`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
### [**Navigation transitions導航過渡**](http://docs.nativescript.org/core-concepts/navigation#navigation-transitions)
默認地,所有導航會有動畫并為相應的平臺使用默認的過渡 \(IOS下是UINavigationController 過渡 ,Android 下是 Fragment 過渡\) 。要改變過渡類型,設置 `NavigationEntry` 的 `navigationTransition` 屬性到一個對象來符合 `NavigationTransition` 接口。
> **JS**
>
> ---
>
> `var navigationEntry = {`
>
> `moduleName: "main-page",`
>
> `animated: true,`
>
> `transition: { name: "slide", duration: 380, curve: "easeIn" }`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
要使用任何內建的過渡,設置 `NavigationTransition` 的 `name` 屬性為下列之一:
* curl \(same as curlUp\) \(iOS only\)
* curlUp \(iOS only\)
* curlDown \(iOS only\)
* explode \(Android Lollipop and later\)
* fade
* flip \(same as flipRight\)
* flipRight
* flipLeft
* slide \(same as slideLeft\)
* slideLeft
* slideRight
* slideTop
* slideBottom
`duration`屬性允許你用毫秒定義過渡的持續時間。如果不定義,會默認使用各自平臺的持續時間——IOS是350毫秒,Android是300毫秒。
The `curve` property lets you specify the animation curve of the transition. Possible values are contained in the [AnimationCurve enumeration](http://docs.nativescript.org/api-reference/modules/_ui_enums_.animationcurve.html). Alternatively, you can pass an instance of type`UIViewAnimationCurve` for iOS or `android.animation.TimeInterpolator` for Android. If left undefined, and `easeInOut` curve will be used.
`curve` 屬性允許你定義動畫的過渡曲線。可能的值包含在 [AnimationCurve enumeration](http://docs.nativescript.org/api-reference/modules/_ui_enums_.animationcurve.html) 里。或者,你可以傳遞一個實例,IOS是 `UIViewAnimationCurve` 類型,Android是`android.animation.TimeInterpolator` 類型。如果不定義,會默認使用 `easeInOut` 曲線。
要指定所有frame導航的默認過渡方式,設置你要導航的frame的 `transition` 屬性。
> **JS**
>
> ---
>
> `topmost.transition = { name: "flip" };`
>
> `topmost.navigate("main-page");`
要指定一個默認過多到整個app的所有導航,設置 `Frame` 類的靜態的 `defaultTransition屬性。`
> **JS**
>
> ---
>
> `var frameModule = require("ui/frame");`
>
> `frameModule.Frame.defaultTransition = { name: "fade" };`
要為不同平臺指定不同的過渡,使用 `NavigationEntry` 的 `transitioniOS` 和`transitionAndroid` 屬性。
### [**Example 11: 設置指定平臺的過渡方式.**](http://docs.nativescript.org/core-concepts/navigation#example-11--set-up-platform-specific-transitions)
> **JS**
>
> ---
>
> `var navigationEntry = {`
>
> `moduleName: "main-page",`
>
> `animated: true,`
>
> `transitioniOS: { name: "curl", duration: 380, curve: "easeIn" },`
>
> `transitionAndroid: { name: "explode", duration: 300, curve: "easeOut" }`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
### [**Custom transitions自定義過渡**](http://docs.nativescript.org/core-concepts/navigation#custom-transitions)
替代設置預置的過渡方式的 `name` 屬性,你可以設置 `NavigationTransition` 的 `instance` 屬性到一個繼承于 `Transition` 類的實例。你可以通過編寫指定平臺的代碼以實現動畫過渡,來創建自己的用戶自定義過渡。為此你需要從 `Transition` 類繼承并為每個平臺重寫一個方法。既然這里有指定平臺的代碼,你就需要在兩份單獨的文件區別你的代碼。這里有個自定義過渡的例子,它通過使用尺寸縮放過渡來放大出現的頁面時縮小消失的頁面。
### [**Example 12: 創建你自己的自定義過渡.**](http://docs.nativescript.org/core-concepts/navigation#example-12--create-your-own-custom-transition)
`custom-transition.android.js/ts`
> `var transition = require("ui/transition");`
>
> `var floatType = java.lang.Float.class.getField("TYPE").get(null);`
>
> `var CustomTransition = (function (_super) {`
>
> `__extends(CustomTransition, _super);`
>
> `function CustomTransition() {`
>
> `_super.apply(this, arguments);`
>
> `}`
>
> `CustomTransition.prototype.createAndroidAnimator = function (transitionType) {`
>
> `var scaleValues = java.lang.reflect.Array.newInstance(floatType, 2);`
>
> `switch (transitionType) {`
>
> `case transition.AndroidTransitionType.enter:`
>
> `case transition.AndroidTransitionType.popEnter:`
>
> `scaleValues[0] = 0;`
>
> `scaleValues[1] = 1;`
>
> `break;`
>
> `case transition.AndroidTransitionType.exit:`
>
> `case transition.AndroidTransitionType.popExit:`
>
> `scaleValues[0] = 1;`
>
> `scaleValues[1] = 0;`
>
> `break;`
>
> `}`
>
> `var objectAnimators = java.lang.reflect.Array.newInstance(android.animation.Animator.class, 2);`
>
> `objectAnimators[0] = android.animation.ObjectAnimator.ofFloat(null, "scaleX", scaleValues);`
>
> `objectAnimators[1] = android.animation.ObjectAnimator.ofFloat(null, "scaleY", scaleValues);`
>
> `var animatorSet = new android.animation.AnimatorSet();`
>
> `animatorSet.playTogether(objectAnimators);`
>
> `var duration = this.getDuration();`
>
> `if (duration !== undefined) { animatorSet.setDuration(duration); }`
>
> `animatorSet.setInterpolator(this.getCurve());`
>
> `return animatorSet;`
>
> `};`
>
> `return CustomTransition;`
>
> `})(transition.Transition);`
>
> `exports.CustomTransition = CustomTransition;`
`custom-transition.ios.js/ts`
> `var transition = require("ui/transition");`
>
> `var CustomTransition = (function (_super) {`
>
> `__extends(CustomTransition, _super);`
>
> `function CustomTransition() { _super.apply(this, arguments); }`
>
> `CustomTransition.prototype.animateIOSTransition = function (containerView, fromView, toView, operation, completion) {`
>
> `toView.transform = CGAffineTransformMakeScale(0, 0);`
>
> `fromView.transform = CGAffineTransformIdentity;`
>
> `switch (operation) {`
>
> `case UINavigationControllerOperation.UINavigationControllerOperationPush:`
>
> `containerView.insertSubviewAboveSubview(toView, fromView);`
>
> `break;`
>
> `case UINavigationControllerOperation.UINavigationControllerOperationPop:`
>
> `containerView.insertSubviewBelowSubview(toView, fromView);`
>
> `break;`
>
> `}`
>
> `var duration = this.getDuration();`
>
> `var curve = this.getCurve();`
>
> `UIView.animateWithDurationAnimationsCompletion(duration, function () {`
>
> `UIView.setAnimationCurve(curve);`
>
> `toView.transform = CGAffineTransformIdentity;`
>
> `fromView.transform = CGAffineTransformMakeScale(0, 0);`
>
> `}, completion);`
>
> `};`
>
> `return CustomTransition;`
>
> `})(transition.Transition);`
>
> `exports.CustomTransition = CustomTransition;`
一旦你創建了 `custom-transition.android.js/ts` 和`custom-transition.ios.js/ts` ,你就要引入該模塊并實例化你的自定義 Transition ,并可選地傳遞一個延時和曲線給構造器。
### **[Example 13: 引入模塊并實例化你的自定義過渡.](http://docs.nativescript.org/core-concepts/navigation#example-13--require-the-module-and-instantiate-your-custom-transition)**
> `var customTransition = new customTransitionModule.CustomTransition(300, "easeIn");`
>
> `var navigationEntry = {`
>
> `moduleName: "main-page",`
>
> `animated: true,`
>
> `transition: {instance: customTransition}`
>
> `};`
>
> `topmost.navigate(navigationEntry);`
### **[Navigate back導航返回](http://docs.nativescript.org/core-concepts/navigation#navigate-back)**
頂層frame在一個導航棧里跟蹤用戶訪問過的頁面。要返回前一個頁面,你就要使用頂層frame實例的 **goBack** 方法。
> `topmost.goBack();`
### **[Modal pages模態頁面](http://docs.nativescript.org/core-concepts/navigation#modal-pages)**
使用page類的 **showModal** 方法能以模態框的方式顯示另一個頁面。你必須指定模態頁面模塊的位置。你可以提供一個上下文和一個回調函數以便在模態頁面關閉的時候調用。你也可以可選地指定是否以全屏的方式顯示模態頁面。要關閉模態頁面,你需要訂閱其shownmodally事件,并對通過事件參數提供的一個關閉的回調函數保存一個引用。當你準備關閉這個模態頁面時調用這個函數,可選地傳遞一些結果到主頁面。這里的例子有兩個頁面——一個主頁面和一個登錄頁面。主頁面模態地顯示登錄頁面;用戶輸入用戶名和密碼并在完成后點擊登錄按鈕。這將關閉模態登錄頁面并把用戶名和密碼返回給主頁面以記錄用戶登進。
> ### **TIP:** 在iPhone的設計里,模態頁面只以全屏方式出現。
### **[Example 14: 從模態頁面接收數據.](http://docs.nativescript.org/core-concepts/navigation#example-14--receive-data-from-the-modal-page)**
**main-page**
> `var modalPageModule = "./modal-views-demo/login-page";`
>
> `var context = "some custom context";`
>
> `var fullscreen = true;`
>
> `mainPage.showModal(modalPageModule, context, function closeCallback(username, password) {`
>
> `// Log the user in...`
>
> `}, fullscreen);`
**login-page**
> `var context;`
>
> `var closeCallback;`
>
> `function onShownModally(args) {`
>
> `context = args.context;`
>
> `closeCallback = args.closeCallback;`
>
> `}`
>
> `exports.onShownModally = onShownModally;`
>
> `function onLoginButtonTap() {`
>
> `closeCallback(usernameTextField.text, passwordTextField.text);`
>
> `}`
>
> `exports.onLoginButtonTap = onLoginButtonTap;`
你可以在 [這里 ](https://github.com/NativeScript/NativeScript/tree/master/apps/app/ui-tests-app/modal-view)找到完整的源代碼。
## **[Supporting multiple screens支持多屏](http://docs.nativescript.org/core-concepts/navigation#supporting-multiple-screens)**
移動應用在不同設備上以不同的屏幕尺寸和形式因素運行。 NativeScript 提供了一個方式來定義不同的文件 \(.js, .css, .xml, 等等\) 以基于當前設備的屏幕尺寸、平臺和方向加載。有點類似于[安卓里的多屏支持](http://developer.android.com/guide/practices/screens_support.html) 。這里有一組可以添加到文件里的適配,它\(適配\)可以在文件加載時就被重視。這是該文件看起來的樣子:
`<file-name>[.<qualifier>]*.<extension>`
接下來的部分我們將通覽支持的 適配 列表。
### **[Screen size qualifiers屏幕適配](http://docs.nativescript.org/core-concepts/navigation#screen-size-qualifiers)**
All the values in screen size qualifiers are in density independent pixels\(dp\) — meaning it corresponds to the physical dimensions of the screen. The assumptions is that there are ~160 dp per inch. For example, according to Android guidelines, if the device's smaller dimension is more than 600 dp \(~3.75 inches\) it is probably a tablet.
在屏幕尺寸適配的所有值都是以與密度無關的像素\(dp\)表示——意思是它對應于屏幕的物理尺寸。假設是有~160dp每英寸。例如,根據安卓的準則,如果設備較小的尺寸大于600dp\(~3.75英寸\),它可能就是個平板。
* `minWH<X>` - 較小尺寸 \(高或寬\) 至少應是 **X** dp.
* `minW<X>` - 寬至少是 **X** dp.
* `minH<X>` - 高至少是 **X** dp.
_例如 \(獨立的 XML 文件針對平板和手機\)_:
* `main-page.minWH600.xml` - XML 文件用于平板設備。
* `main-page.xml` - XML 用于手機。
### [**Platform qualifiers**](http://docs.nativescript.org/core-concepts/navigation#platform-qualifiers)**平臺適配**
* `android` – Android 平臺
* `ios` – iOS 平臺
* `windows` \(期待中……\) – Windows 平臺
_例如 \(指定平臺文件\)_:
* `app.android.css` - CSS 樣式表用于Android.
* `app.ios.css` - CSS 樣式表用于iOS.
平臺適配是在創建的時候執行,其它的是在運行的時候執行。例如, 當在為Anroid平臺創建的時候,app.ios.css 文件是不會被納入進去的。相反地,屏幕尺寸適配會在app以具體的屏幕尺寸運行在一個設備上的時候被采納。
### [**Orientation qualifiers方向適配**](http://docs.nativescript.org/core-concepts/navigation#orientation-qualifiers)
* `land` - 風景模式的方向\(橫\)。
* `port` - 肖像模式的方向\(豎\)。
> ### **注意:**
>
> ---
>
> **當頁面加載時所有的適配都會被考慮到。然而,改變設備方向不會觸發頁面重新加載并不會改變頁面,——也就是改變方向不會重新適配。**