頁面跳轉(zhuǎn)
Page是AppWorker應(yīng)用中最重要的概念,我們詳細(xì)介紹Page的一下相關(guān)問題,相關(guān)的App和UI的概念也會介紹,適合所有初學(xué)者。
一. 概念
Page是管理一組ui和相關(guān)邏輯的基本集合。如果了解Android和iOS的話,Page等同于Android的Activity和iOS的UIViewController。
從視覺上來看,一個Page是一個擴充全屏的一個View,這個View包含一個主ui,主UI又可以包含很多個子ui,這里的ui指一個ui文件。從邏輯上來說,一個Page通常管理業(yè)務(wù)邏輯的一個模塊,比如登陸頁面,首頁等等,我們平常設(shè)計App的時候,每個頁面通常就指一個Page。
二. 組織結(jié)構(gòu)
1.?Page棧
一個AppWorker的App包含一個do_App對象,包含多個Page(do_Page對象)。通過do_App維護一個棧來管理多個Page對象,如下圖。

管理手段其實就2個方法:openPage和closePage,對應(yīng)上面的圖就是壓棧和出棧。這個圖表示App運行時當(dāng)前的頁面情況,Page1上面是Page2,Page2上面是Page3,Page6是棧頂,在當(dāng)前時刻,用戶只能看見Page6,因為Page是全屏不透明的。但是其它3個Page還是在應(yīng)用的內(nèi)存中,并沒有釋放。
do_Page是一個SM對象,但是是一個特殊的SM對象,通常一個SM對象在整個App沒有退出前,在任何地方都只有一個對象實例,也就是在任何時刻獲取這個對象的地址都是一樣的。Page對象特殊在于它并不是唯一示例,但是由于用戶只能操作Page棧頂?shù)腜age,所以不會有什么問題。
//比如do_App是一個SM對象,do_Album是一個SM對象,do_Page是一個SM對象
var app = sm("do_App");
var album = sm("do_Album");
var page = sm("do_Page");
print(app.getAddress());//任何時刻任何地方執(zhí)行值都一樣
print(album.getAddress());//任何時刻任何地方執(zhí)行值都一樣
print(page.getAddress());//返回的值可能不一樣
2.?Page交互設(shè)計
有一個常見問題就是App的設(shè)計思路沿用了瀏覽器加載網(wǎng)頁的思路,不斷的openPage而不closePage。正確的頁面跳轉(zhuǎn)交互設(shè)計應(yīng)該是樹狀結(jié)構(gòu),而不應(yīng)該是網(wǎng)狀結(jié)構(gòu),如下圖:


我們來比較一下,從page1到page3再到page8的過程中,Page棧的內(nèi)容差異如下
正確的方式
page1—>page2
page1—>page2—>page3
page1—>page2
page1
page1—>page8
錯誤的方式
page1—>page2
page1—>page2—>page3
page1—>page2—>page3—>page8
我們可以看到錯誤的方式會導(dǎo)致棧一直增加,從不或者很少出棧,經(jīng)常出現(xiàn)的問題是page1打開page2,page2又打開page1,導(dǎo)致循環(huán),這樣很快內(nèi)存就會耗盡導(dǎo)致app閃退,而且這種方式導(dǎo)致數(shù)據(jù)的流轉(zhuǎn)非常混亂。
為了避免棧太深,Android限制了最多只能16層。也就是page1打開page2,page2打開page3,一直到page16就無法再openPage了。
三. 生命周期
一個Page的開始是由openPage開始,到結(jié)束closePage,它的基本流程是如下圖:

有一些問題需要進一步解釋
1.?每個Page都有一個主ui文件,比如當(dāng)前棧頂是app.js,在app.js里執(zhí)行openPage代碼
//app.js
app.on("loaded", function () {
? ? d1.print("start open page");
? ? app.openPage("source://view/index.ui");
});
執(zhí)行完,那么新的page打開,這個page的主ui就是index.ui。但是index.ui.js里可能會加載一個或多個子ui,通常2種方式引進子ui
//index.ui.js
//第一種:通過add方法在現(xiàn)有ui上添加新的ui
var layout = ui("ALayout_1");
layout.add("idtest", "source://view/added.ui")
//第二種:很多容器組件可以添加多個子ui
var viewshower = ui("do_ViewShower_1");
var data = [ {
? ? "id" : "test1",
? ? "path" : "source://view/1.ui"
}, {
? ? "id" : "test2",
? ? "path" : "source://view/2.ui"
}, {
? ? "id" : "test3",
? ? "path" : "source://view/3.ui"
} ];
viewshower.addViews(data);
viewshower.showView("test2");
這樣當(dāng)前棧頂?shù)膒age主ui就是index.ui,里面包含了子ui是 added.ui,1.ui,2.ui,3.ui?
2.?上個例子,added.ui,2.ui都加載完,而且added.ui.js,2.ui.js都加載完,新的page才開始以動畫的形式打開。為了提高效率和體驗,可以把一些子ui以到page打開之后才加載。比如放到page的loaded事件里執(zhí)行。
//index.ui.js
var? page = sm("do_Page");
page.on("loaded",functioin(){
? ? //在新的page頁面彈出之后,才執(zhí)行下面的代碼
? ? var layout = ui("ALayout_1");
? ? layout.add("idtest", "source://view/added.ui")
})
上面的例子并沒有加載1.ui和3.ui 是因為do_ViewShower組件處理過,只有showView的時候才去加載,而addViews的時候不加載,為了提高效率。
這里可以參考完整的例子,執(zhí)行的時候可以看到在Logger看到加載的順序打印。
3.?Page的生命周期中還有pause和resume事件,平常用的不是很多,請參考API文檔.
result事件用的非常多,下面還會詳細(xì)說
四. 數(shù)據(jù)傳遞
涉及到2個部分,page和page之間的數(shù)據(jù)傳遞,單個page里的主ui和子ui之間的數(shù)據(jù)傳遞
1.?Page與Page之間的數(shù)據(jù)傳遞
Page1需要在打開Page2的時候去傳遞一些數(shù)據(jù)給Page2,Page2關(guān)閉自身,需要通知Page1并傳遞一些數(shù)據(jù),這個過程主要依靠openPage,closePage的data參數(shù)以及result事件來實現(xiàn)。
這一部分在文檔數(shù)據(jù)分享和數(shù)據(jù)傳遞有了詳細(xì)的介紹
2.?一個Page內(nèi)數(shù)據(jù)傳遞
單個page里的主ui和子ui之間的數(shù)據(jù)傳遞主要依靠page對象的自定義消息的機制。一個ui訂閱page的一個消息,另外一個ui觸發(fā)這個消息。這一部分在文檔事件機制有了詳細(xì)的介紹
五. closePage多層
Page棧是通過openPage一層層的壓棧,通過closePage一層層的出棧,并不能跳躍的關(guān)閉棧內(nèi)任意Page,但是AppWorker支持一次closePage多層或關(guān)閉到某一特定層
二種方式
1.?一次關(guān)閉多層

2.?直接關(guān)閉到特定層,其中page1的唯一標(biāo)示是’pageid1’
