作者:David Walsh
翻譯:Siqi (siqi.zhong@gmail.com)
原文:TweetView: Creating the Settings View
在上一篇教程Dojo mobile TweetView 系列教程之三——Tweets和Mentions視圖 中,我們夯實了TweetView應用程式的檔案結構,回顧了建立TweetView的目標並通過編寫tweetview._ViewMixin和tweetview.TweetView建立了Tweets和Mentions視圖。本教程將重點介紹TweetView中的"Setting"視圖:類的依賴關係、Setting視圖是如何和Tweet、Mention視圖聯絡在一起的、並編寫Setting視圖的代碼。
版本:1.6
難度:中級
系列:TweetView
“Settings”視圖的依賴關係
Settings視圖是三個視圖中最簡單的。該視圖包含兩個標題(主標題和副標題),和一個Twitter賬戶列表,每一個Twitter賬戶都對應著一個開關用以控制該賬戶的資訊是否需要在其他兩個視圖中顯示。在看過下面的之後,可以知道將會用到以下控制項:
- dojox.mobile.ScrollableView - 整個視圖
- dojox.mobile.Heading - 主標題"Setting"
- dojox.mobile.RoundRectCategory - 副標題"Show"
- dojox.mobile.RoundRectList - 賬戶列表容器
- dojox.mobile.ListItem - 賬戶列表元素
- dojox.mobile.Switch - 開關空間
該還清楚的說明了我們將會需要從Twitter擷取使用者的頭像資訊,因此我們還需要一些其他的Dojo資源:
- dojo.io.script - 使用JSONP從Twitter擷取資訊
- dojo.DeferredList - 讓我們可以一次處理多個Twitter資訊請求所返回的資料
這些資源將協助我們順利完成Settings視圖。與我們建立Tweets和Mentions視圖的做法類似,我們將為Settings視圖建立一個自訂類:SettingsView。
!如果我們不在Settings視圖顯示使用者的頭像資訊,我們就不需要dojo.io.script和dojo.DeferredList。我們可以寫死這些頭像圖片的路徑,但是這樣的話我們需要在每次使用者更改他們頭像時手動更新這些圖片的路徑。幸運的是我們之前建立的TweetView視圖的代碼已經包含了這些資源,所以在Settings視圖中使用它們不會導致代碼膨脹——這些資源提供的類已經可以用啦!
開發Setting視圖
我們的SettingsView類和TweetView類非常相似,他們都繼承dojox.mobile.ScrollableView和tweetview.ViewMixin。很重要的一點是SettingView類只是一個封裝了整個應用程式用以擷取賬戶資訊的tweetview.ACCOUNTS對象的容器。在明白了這一點之後讓我們看一下Settings視圖的具體實現吧。
新的類:Settings視圖
我們新的類叫做SettingsView,它的基礎結構和TweetView一樣:
// 提供UI類dojo.provide("tweetview.SettingsView"); // 匯入依賴項dojo.require("dojox.mobile.ScrollableView");dojo.require("dojo.DeferredList");dojo.require("dojo.io.script");dojo.require("tweetview._ViewMixin"); // 聲明類;它繼承自ScrollableViewdojo.declare("tweetview.SettingsView", [dojox.mobile.ScrollableView, tweetview._ViewMixin], { // 這裡將添加方法和屬性 });
依賴項已經被匯入,我們的類也已經聲明完畢。
!該類將被被放在TweetView和_ViewMixin同一個檔案夾下:js/tweetview
SettingsView的屬性
SettingsView將有三個自訂屬性。第一個是accountTemplateString:一個包含HTML標籤的字串,用以表示帳號列表中每一個列表元素的布局。
// 模板字串accountTemplateString: '<img src="${avatar}" mce_src="${avatar}" alt="${user}" class="tweetviewAvatar" />' +'<div class="tweetviewContent">' +'<div class="tweetviewUser">${user}</div>' +'</div><div class="tweetviewClear"></div>',
第二個屬性是views,它代表TweetView執行個體。為什麼SettingsView需要知道TweetView執行個體的ID呢?因為SettingsView需要根據每一個賬戶的開關狀態調整其對應的TweetsView執行個體中的tweets。views屬性接受一個由多個TweetView執行個體的ID所組成的字串,每個ID以逗號分隔開。
// SettingsView所引用到的TweetView視圖views: "",
最後一個自訂屬性是serviceUrl:
// 用以擷取使用者資訊的URL,這裡已經提供了一個簡單的URL模板serviceUrl: "http://api.twitter.com/1/users/show/${account}.json",
serviceURL屬性代表用來擷取使用者資訊的Twitter服務URL。對於SettingsView來說,我們只需要擷取使用者的頭像。
實現SettingsView
現在SettingsView的架構已經搭好(儘管它現在還幹不了什麼),是時候更新Settings視圖的HTML部分了:
<!-- settings 視圖 --><div id="settings" dojoType="tweetview.SettingsView" views="tweets,mentions"> <h1 dojoType="dojox.mobile.Heading" fixed="top">Settings</h1> <h2 dojoType="dojox.mobile.RoundRectCategory">Show</h2> <ul dojoType="dojox.mobile.RoundRectList" class="tweetviewList"></ul></div>
下面是我們所做的改動:
- 該控制項的dojoType已經改成了我們的新類:tweetview.SettingsView。
- 設定views屬性為“tweets, mentions”;即TweetView和MentionView的ID。
- 為RoundRectList節點添加了tweetviewList CSS類,這樣該控制項可以被分辨出來,並擷取其引用
當然我們需要在我們的app.html頁面頂部匯入tweetview.SettingsView類
// 使用輕量級的parserdojo.require("dojox.mobile.parser");// 匯入Dojo mobiledojo.require("dojox.mobile");// 匯入額外的、非標準的dojox.mobile控制項dojo.require("dojox.mobile.TabBar");// 匯入相容包dojo.requireIf(!dojo.isWebKit,"dojox.mobile.compat");// 匯入TweetViewUIdojo.require("tweetview.TweetView");dojo.require("tweetview.SettingsView");// 直接在命名空間下設定tweetview賬戶資訊tweetview.ACCOUNTS = { dojo: { enabled: true }, sitepen: { enabled: true }};
在將SettingsView添加到app.html頁面之後,是時候建立JavaScript部分了。
SettingsView _startup()
SettingsView類的startup方法是SettingsView工作的關鍵。讓我們一步步看下去。首先是調用父類(dojox.mobile.ScrollableView)的startup方法來擷取原本的功能:
// 擷取 dojox.mobile.ScrollableView 的startup功能this.inherited(arguments);
擷取視圖中list控制項的引用,並在添加列表元素之前隱藏它。
// 擷取list子控制項this.listNode = this.getListNode();// 隱藏list控制項,因為它還沒被填充this.showListNode(false);
建立一個賬戶數組並將它們排序,這樣他們就能按字母順序在視圖中顯示了
// 將帳號排序var accounts = [];for(var account in tweetview.ACCOUNTS) { accounts.push(account);}accounts.sort();
建立以請求Twitter使用者資訊返回的Deferred組成的數組:
// 建立一個容納deferred的數組var defs = [];// 處理每個賬戶dojo.forEach(accounts, function(account){ // 請求使用者資訊 defs.push(dojo.io.script.get({ callbackParamName: "callback", timeout: 3000, // "substitute"繼承自_ViewMixin url: this.substitute(this.serviceUrl, { account: account }) }));},this);
!你將會看到根據serviceUrl參數和一個包含賬戶名的對象產生URL。substitue方法繼承自_ViewMixin,SettingsView也繼承自該類。
在Twitter請求觸發之後,擷取TweetView空間的引用:
// 擷取視圖控制項this.viewWidgets = dojo.map(this.views.split(","), function(id) { return dijit.byId(id);});
剩下的功能在所有使用者資訊擷取完畢後在dojo.DeferredList的回呼函數中實現。對於每一個我們要擷取資訊的賬戶,如果賬戶存在並沒有設定保護:
// 建立一個deferred列表以添加進行格式化的回呼函數new dojo.DeferredList(defs).then(dojo.hitch(this, function(results) { // 對於每個返回的使用者數組... dojo.forEach(results, function(def,i) { // 如果deferred失敗,那麼使用者帳號不存在或者不可用 if(!def[0]) { // 移除該帳號防止發生更多問題 // 同時從我們本地經過排序的賬戶列表中刪除該賬戶 delete tweetview.ACCOUNTS[accounts[i]]; delete accounts[i]; return; } // 擷取使用者數組 var user = def[1]; // 如果使用者存在並且沒有被凍結或是保護... if(user.id && !user["protected"]) { // Protected是一個保留字 // 之後這裡會有更多代碼 } },this); }));
建立一個新的dojox.mobile.ListItem並使用我們的使用者資訊模板填充它:
// 為使用者建立一個新的列表元素,附帶一個開關var item = new dojox.mobile.ListItem({}).placeAt(this.listNode, "last"); // 使用我們的模板更新元素列表內容item.containerNode.innerHTML = this.substitute(this.accountTemplateString, { user: user.screen_name, avatar: user.profile_image_url, user_id: user.id});
為列表元素建立一個dojox.mobileSwitch控制項,我們需要考慮賬戶的啟用狀態:
// 建立開關var userSwitch = new dojox.mobile.Switch({ "class": "tweetviewSwitch", value: tweetview.ACCOUNTS[user.screen_name].enabled ? "on" : "off"}).placeAt(item.containerNode, "first");
為Switch控制項添加onStateChange事件,在該事件中將會更新tweetview.ACCOUNTS對象的啟用狀態。另外,通知TweetView執行個體帳戶的啟用狀態:
// 為開關添加change事件dojo.connect(userSwitch, "onStateChanged", this, function(newState) { // 擷取 true/false 值 var isOn = newState == "on"; // 更新ACCOUNTS表 tweetview.ACCOUNTS[user.screen_name].enabled = isOn; // 調用每一個Pane控制項的onUserChange方法 dojo.forEach(this.viewWidgets, function(viewWidget) { viewWidget.onUserChange(user.screen_name,isOn); });});
最後,如果我們收到了任何有效帳號,顯示Settings列表節點(因為現在它已經有內容了)並重新整理每一個視圖。
// 如果我們有任何有效賬戶...if(accounts.length) { // 顯示有內容的列表 this.showListNode(true); // 出發每個視圖的refresh方法 dojo.forEach(this.viewWidgets, function(view) { view.refresh(); });}
tweetview.Setting的JavaScript程式碼完成了 ——這個控制項現在可以正確工作啦!不過我們還沒有完成所有的JavaScript代碼!我們還需要為TweetView類實現onUserChange方法。
TweetView更新:onUserChange並從startup()中移除refresh()
我們之所以在SettingsView中調用每個視圖的refresh方法,是因為我們不想為那些確定無效的賬戶向Twitter發出請求。但不幸的是我們之前已經在TweetView的startup中調用了refresh方法。現在讓我們移除它:
// 擷取tweets//this.refresh();
SettingsView執行個體將會在確認一個賬戶是有效時調用視圖的refresh方法。
onUserChange方法在某個賬戶被啟用或者禁用時被SettingsView調用。我們有幾種處理啟用/禁用的方式:
重新整理整個控制項 - 會浪費資源和Twitter API的使用率
銷毀ListItem控制項,並重建它們 - 開銷太大,如果使用者馬上又啟用了賬戶怎麼辦呢? 那樣會導致重新擷取所有使用者的tweets。這樣乾的話還會破壞我們的緩衝功能。
使用CSS顯示/隱藏列表元素 - 就是它了!開銷很小,我們已經擷取了需要的資料,不需要重複擷取它們!
在TweetView中實現這一方法:
// 根據使用者的賬戶啟用屬性更新一個tweet的顯示內容onUserChange: function(account,isOn) { dojo.forEach(this.getElements("user-" + account,this.domNode), function(node){ dojo[(isOn ? "remove" : "add") + "Class"](node, "tweetviewHidden"); });}
還記得user-{screenName} CSS類已經被分配給了tweetview.TweetView中的列表元素嗎?我們將使用這個CSS類來找出需要被啟用/禁用的使用者,並為它們移除/添加一個新的名為tweetviewHidden的CSS類,用以將一個列表元素設定為display:none或者display:block。
設定SettingsView樣式
HTML和JavaScript部分都已經完成了,現在為我們的樣式表添加一些CSS類來使得列表看起來和裡的一樣:
/* 開關在右邊 */.tweetviewSwitch { right:10px; top:10px; float:right;} /* 為禁用的使用者隱藏對應的tweet */.tweetviewHidden { display:none;}
TweetView 完成了!
tweetview.TweetView在上一篇教程中已經完成,本教程又完成了tweetview.SettingsView,我們的控制項已經大功告成了!點這裡來查看我們的控制項。
在本系列最後一篇教程中我們將使用Dojo的打包系統來壓縮TweetView所用的JavaScript,HTML和CSS,使得整個程式更緊湊。
下載代碼
下載TweetView
TweetView系列中文教程
Dojo mobile TweetView 系列教程之一 —— dojox.mobile入門
Dojo mobile TweetView 系列教程之二 —— TweetView 啟程
Dojo mobile TweetView 系列教程之三 —— Tweets和Mentions視圖
Dojo mobile TweetView 系列教程之四 —— 建立Setting視圖