將網路資料變成 Widget:RSS 新聞閱讀器 Widget 範例

▲前頁 ☆目次 ▼次頁

您將學到什麼?

在剛剛的 HelloWorld 程式中,您已經了解 widget 的基本構成方式,還有其事件處理程式的基本模式。接下來我們在這個 RSS 新聞閱讀器 widget 範例中,我們將告訴您:

  • 如何結合獨立的 JavaScript .js 檔案,讓 kon 檔定義的使用介面與程式碼分離
  • 如何將資料由網路載入
  • 如何將載入資料分析並以 widget 方式來呈現
  • YWE 3.0 新增的 Frame 物件的用法與其 sub frame(subview)的關係
  • 利用 YWE 3.0 新增的 scrollbar 捲視軸讓原本放不下的內容變成可捲動檢視

以下是本節課程會用到的檔案:

▲Top

如何結合獨立的 JavaScript 檔案到 .kon 檔案

首先,為何要這麼作?您可以看到,在 Hello World 範例中,一個 .kon 檔案就包含了 widget 介面、屬性定義,及其 JavaScript 事件處理程式。但是萬一您往後的 widget 的程式邏輯越來越複雜,不是十幾二十行可以解決的時候,與介面定義混在一起時有下列缺點:

  • 程式與 XML 定義會混淆,讓閱讀性降低
  • 如果想利用如 Notepad++ 這類編輯器對各語言語法的色彩特別標示功能時,含在 .kon 檔中的 JavaScript 程式將不會清楚的標識,可能都會只是一種顏色而已,也降低程式的閱讀性 基於上述理由,我們強烈建議將含有大量程式碼的 JavaScript 程式碼獨立另一個 .js 檔案中,再利用 YWE 的定義將該 .js 檔 include 進來。

要將 JavaScript 的檔案 include 進來非常容易,您只要在 <widget> tag 之後加入 <action trigger="onLoad" file="JavaScript檔名"/> 敘述即可,這樣便可將 JavaScript 碼獨立在一個檔案中,且會在 widget 載入時就自動執行該 JavaScript 檔案中的程式碼。

▲Top

如何將資料由網路載入

在各位常常看到現有的 widget 中,大部分都是跟網路相關,方式都是從網站自入資料、分析之後,然後再以 widget 的方式呈現在使用者面前,因此 widget 程式設計裡,有一個很重要的課題,就是如何從網站載入資料並萃取我們想要的資料,再呈現在 widget 中。

如果您知道並設計過 AJAX,相信您對 XMLHttpRequest 這個 class 不陌生。在 Yahoo! Widget Engine 3.0 之後的版本也加入了 XMLHttpRequest 的支援,這個 class 讓您可以用 JavaScript 動態去載入外部網路的資料,而如果載入的資料是 XML 的話,這個 class 也會自動將載入的資料分解成 Document Object Model(DOM)的樹狀結構化物件(DOMDocument 物件),讓您方便的存取 XML 文件的任一個部份。

在這個範例中,我們會設計下面這個 loadRSS 的函式,會利用 XMLHttpRequest 來載入網站的 RSS XML 資料。以下是 XMLHttpRequest 的一般使用方式:

程式列表 1

var req = new XMLHttpRequest();	1
req.open("GET", "http://tw.news.yahoo.com/rss/realtime", false);	2
req.send();	3

1 var req = new XMLHttpRequest();
XMLHttpReqeust 是一個 class,要使用前就像其他物件一樣,您要先用 new 敘述做出一個 XMLHttpRequest
2 req.open("GET", "http://tw.news.yahoo.com/rss/realtime", false);
open 這個 method 是用來作載入資料準備的,以這個範例來說它有三個參數:
  • "GET"
    這個值有可能是 GET 或是 POST,看您去要求資料的伺服器上的 server-side 程式使用 GETPOST 來傳送資料。一般來說,如果您傳送的參數是用網址來傳,例如 http://www.yahoo.com/rss?feed=1,或者就沒有傳資料給伺服器程式時,都是使用 GET。除非該伺服器程式是特別用 POST 形式來接受外來的資料並作反應的話,才會使用 POST(例如一個 HTML Form)。
  • "http://tw.news.yahoo.com/rss/realtime"
    這是您要載入資料的網址,為一字串。
  • false
    這是一個 Boolean 值,true 或是 falsefalse 代表同步模式,就是等資料都下載或是確定不能下載時,才會執行 send() 之後的下一個命令。如果是 true,您必須同時還要設定 req.onreadystatechange 到一個函式(如 req.onreadystatechange = loadPostfix,其中 loadPostfix 是一個函式),那這樣在資料尚未確定載入或無法載入前,就會執行 send() 之後的下一個命令,這是非同步模式。使用非同步模式可以讓萬一網路出問題時,程式仍然可以接受使用者所觸發的事件或是顯示適當的訊息(如載入進度),讓整個程式不至於在出問題時整個陷入沒反應的狀態。在這次的範例,因為我們主要的重點是如何載入與分析資料,我們使用同步模式,而非較複雜但給使用者感覺較好的非同步模式。
3 req.send();
剛剛的 open() 還有一些其他輔助函式都是準備工作,在 send() 尚未被呼叫前,都不會真的向網站要求資料。send() 則真的提出資料要求,進行資料下載的一個函式。在同步模式時,就會等到資料確定載入或失敗時才會執行 send() 之後的下一個命令。

以上使用 XMLHttpRequest 來載入網站資料的基本使用方法。根據這樣的使用方法,我們來建立一個載入 RSS 2.0 Feed 的函式 - loadRSS()

程式列表 2

/*
	loadRSS 從 feedURL 載入 RSS feed 內容,並傳回一個 DOMDocument 物件。
	如果不是 RSS 2.0 feed 或載入有問題時,將傳回 null。
	注意,本 function 使用同步 XMLHttpRequest,當網路發生問題時,會有可能發生延滯現象。
	input:
			feedURL			RSS Feed URL
	Output:
			DOMDocument		含有 RSS XML、已 parse 的結構化物件
*/
function loadRSS(feedURL) {
	// 使用 XMLHttpRequest 將資料由 feedURL 指定的網址下載下來
	var req = new XMLHttpRequest();
	req.open("GET", feedURL, false);
	// 先試著請伺服器傳回 Content-Type 為 text/xml 的資料,以方便直接 parse 成 XML
	req.setRequestHeader("Content-Type", "text/xml");	1
	req.send();
	if (req.status != 200)
		return null;				// 沒有載入成功,傳回 null
	if (req.responseXML == null) {
		// 會沒有 DOMDocument 有可能伺服器回應的 Content-Type 不是 text/xml
		//(有些會傳回 application/xml)
		// 用 XMLDOM  強制來 parse 成 XML
		try {
			var xml = XMLDOM.parse(req.responseText);	2
		} catch (e) {
			return null;			// 試著 parse 成 XML 但失敗,傳回 null
		}
	} else
		var xml = req.responseXML;
	// 檢查是否為 RSS 2.0
	var rsss = xml.evaluate("rss");			// 使用 XPath 來取得我們要的 RSS 區段	3
	if (rsss == null)
		return null;				// 沒有 RSS,傳回 null
	if (parseFloat(rsss.item(0).getAttribute("version")) >= 2.0)
		return xml;				// 是 RSS 2.0 feed,傳回載入的 DOMDocument
	else
		return null;				// 不是 RSS 2.0 feed,傳回 null
}

loadRSS 這個函式會利用 XMLHttpRequest 載入網頁資料,然後將載入資料分析成結構化的 DOMDocument 物件。其中有幾行值得注意的:

1 req.setRequestHeader("Content-Type", "text/xml");
這一行是像伺服器程式提出資料請求時,請伺服器提供 Content-Typetext/xml 的資料。加入這行主要是因為如果您要 XMLHttpRequest 自動將資料載入後 parse 成 DOMDocument 並放在物件變數 req.responseXML 的話,其回傳資料的 Content-Type 一定要是 text/xml,否則不會自動將資料 parse 成 XML,且物件變數 req.responseXML 會傳回 null
2 var xml = XMLDOM.parse(req.responseText);
雖然我們剛剛有利用 req.setRequestHeader 來向伺服器要求餵給我們 Content-Typetext/xml 資料,但是並非所有伺服器都會照作(Yahoo! 奇摩的新聞 RSS 就是一例),它們讓我們下載的有可能不是 text/xml 的資料,所以我們再利用 Yahoo! Widget Engine 提供的 XMLDOM.parse() class 函式來強制將傳回的資料(在 req.responseText 中)parse 成 DOMDocument 物件。這個 XMLDOM.parse 如果傳回是 null 就代表無法將指定的資料 parse 成結構化的 DOMDocument 物件。
3 var rsss = xml.evaluate("rss");
這一行我們在往後 XML 的程式也都會看到,是個重要的指令。我們已經將結構化的 DOMDocument 物件放在 xml 這個物件中,接下來利用 xml.evaluate 這個物件函式將 RSS feed 的 rss 部份抽取出來。這個函式還有例如這行 rsss.item(0).getAttribute("version") 我們會在稍後的「如何將載入資料分析並以 widget 方式來呈現」中會詳細解釋

loadRSS 執行完之後,便會回傳 null 或是含有 DOMDocument 的物件。如果載入資料無誤並確定為 RSS 2.0 Feed,我們就必須有編寫另一個函式來將載入資料轉化為 widget 的顯示資料及使用者介面的行為。

▲Top

如何將載入資料分析並以 widget 方式來呈現

接下來的部份,我們會撰寫 showRSS 函式,將載入的新聞主題與時間以列表方式呈現,然後當使用者將滑鼠指標移向個別新聞標題或時間時,就會改變文字顏色;反之,如果移出該新聞標題,就會還原文字顏色。如果按下某一新聞標題或時間時,就會打開預設瀏覽器並直接連到該新聞的網頁。

先看看我們現在有的東西,loadRSS 會輸出一個 DOMDocument 物件,我們要如何將這樣的物件抽出我們要的標題、時間與網頁連結呢?首先我們必須對 RSS 2.0 Feed 的 XML 語法有所了解才行。

以下是僅含有一則 RSS 2.0 Yahoo! 奇摩新聞的簡化 XML 檔案範例(實際上一個 RSS 有很多則新聞):

程式列表 3

<rss version="2.0">
	<channel>
		<title>Yahoo!奇摩新聞-即時新聞</title>
		<link>http://tw.news.yahoo.com/realtime/0.html</link>
		<description>Yahoo!奇摩新聞-即時新聞</description>
		<language>zh-tw</language>
		<lastBuildDate>Sun, 5 Feb 2006 02:15:07 GMT</lastBuildDate>
		<ttl>5</ttl>
		<item>
			<title>強風迫使美國華盛頓州16萬戶斷電</title>
			<link>http://tw.news.yahoo.com/060205/4/2to0n.html</link>
			<pubDate>Sun, 5 Feb 2006 02:00:00 GMT</pubDate>
		</item>
	</channel>
</rss>

在這教材裡,我們並不會特別去詳細解說 RSS Feed 的結構,我們只講解我們會利用到的部份。我們無論是利用 XMLHttpRequestXMLDOM.parse 去分析這些資料並轉換成 DOMDocument,其結果都是一樣的,就是在 DOMDocument 裡面,如果以檔案的樹狀結構來解釋上面的 XML,就會像:

  • rss(含屬性 version = 2.0
    • channel
      • title(內容為 Yahoo!奇摩新聞-即時新聞
      • link(內容為 http://tw.news.yahoo.com/realtime/0.html
      • description(內容為 Yahoo!奇摩新聞-即時新聞
      • language(內容為 zh-TW
      • lastBuildDate(內容為 Sun, 5 Feb 2006 02:15:07 GMT
      • ttl(內容為 5
      • item
        • title(內容為強風迫使美國華盛頓州16萬戶斷電
        • link(內容為 http://tw.news.yahoo.com/060205/4/2to0n.html
        • pubDate(內容為 Sun, 5 Feb 2006 02:00:00 GMT

以剛剛 loadRSS 函式用來判別是否為 RSS 2.0 Feed 的這個需求來說,我們使用了 rsss = xml.evaluate("rss") 來萃取在載入資料中所有含有 rss tag 的項目(有可能 0 或多個 rss tag 項目),並利用 rsss.item(0).getAttribute("version") 來粹取第一個 rss 的 version 屬性的值。其中 item(0) 就是指定萃取出的所有 rss tag 的第一個 rss 項目。然後再利用物件函式 getAttribute("version") 來萃取其 version 屬性。

請注意,XML 語法是有可能同時存在好幾個相同的 tag 的項目,所以當您使用 DOMDocument 的 evaluate 函式時,萃取出來的有可能是 0 到多個含有同樣 tag 的項目(每個項目皆為 DOMNode 物件,傳回的是 DOMNodes 物件集合),想知道有多少個的話,可以檢查 length 屬性,想要個別存取每個項目的話,可以用 item(n),其中 n 是 0 到 length-1 的數字。

xml.evaluate("rss")(以此範例來說,xml 是一個 DOMDocument 物件)這個函式中,其參數使一個 XPath 字串,所謂的 XPath 基本上就像你在存取檔案或網頁的路徑一樣。假設我們想存取 rss 下的 channel 下的所有的 item tag 項目,那麼你可以下如 xml.evaluate("rss/channel/item") 的指令便可傳回所有 rss -> channel 下的所有 item tag 項目。其實 XPath 還有更多強大功能,讓您方便準確地存取 XML 裡的任一部份,在 Yahoo! Widget Engine 3.0 Reference 裡的 XPath Support 小節裡有更進一步的 XPath 說明。

有了這個概念,我們接下來看我們即將利用的 DOMDocument 的 evaluate 函式來進行萃取並轉化成 widget 呈現方式的 showRSS 函式。

這個 showRSS 函式最基本的邏輯大致如下:

  1. 清除所有顯示在 widget 中的新聞資料。
  2. 從輸入的參數 rssDOM 這個 DOMDocument 物件萃取出所有 rss -> channel 下的 item 項目(含有 DOMNode 的 DOMNodes 物件集合)。
  3. 再利用迴圈將每個含有 item 的 DOMNodes 的 titlelinkpubDate 項目都萃取出來,然後記錄到 gRSSItems 這個整體變數陣列(以方便清除或往後程式存取之用)。
  4. 將這些資料萃取後,再把她轉化為一行行的顯示資料,並連結致會對使用者滑鼠的移入、移出、按滑鼠按鈕等動作有反應的介面事件處理程式。

程式列表 4

/*
	showRSS 將 rssDOM 內含已 parse 過的 RSS DOMDocument object 來顯示於 widget 的 itemList frame 裡
*/
function showRSS(rssDOM) {
	var theItem, newItem, titleView, timeView;
	// 先清除目前 itemList frame 的內容
	while ((theItem = gRSSItems.shift()) != null) {
		theItem.view.removeFromSuperview();
	}
	var nodes = rssDOM.evaluate("rss/channel/item");	1
	var curY = 0;
	for (var i = 0; i < nodes.length; i++) {
		newItem = new Array();	2
		newItem.title = (nodes.item(i).getElementsByTagName("title")).item(0).firstChild.data;	3
		newItem.link = (nodes.item(i).getElementsByTagName("link")).item(0).firstChild.data;
		newItem.pubDate = new Date(Date.parse((nodes.item(i).getElementsByTagName("pubDate")).
					item(0).firstChild.data));	4
		newItem.view = new Frame();			// 新增 RSS 新聞項目 sub frame
		newItem.view.hOffset = 0;
		newItem.view.vOffset = curY;
		newItem.view.width = itemList.width;
		newItem.view.height = 18;
		newItem.view.onMouseEnter = "hiliteItem(" + i + ")";
		newItem.view.onMouseExit = "dimItem(" + i + ")";
		newItem.view.onMouseUp = "openURL('" + newItem.link + "')";
		titleView = new Text();				// 新增 RSS 新聞項目的新聞 title 文字區塊
		titleView.data = newItem.title;
		titleView.vOffset = 15;
		titleView.hOffset = 0;
		titleView.font = "MS UI Gothic, Arial, Helvetica";
		titleView.color = "#FFFFFF";
		titleView.size = 12;
		titleView.width = itemList.width - 100;
		titleView.truncation = "end";
		titleView.tooltip = newItem.title;
		newItem.view.addSubview(titleView);		// 加到 RSS 新聞項目 sub frame 中
		timeView = new Text();				// 新增 RSS 新聞項目的新聞 title 文字區塊
		timeView.data = newItem.pubDate.getMonth() + "月" + newItem.pubDate.getDate() + 
   "日 " + newItem.pubDate.toLocaleTimeString();
		timeView.vOffset = 15;
		timeView.hOffset = itemList.width - 95;
		timeView.font = "MS UI Gothic, Arial, Helvetica";
		timeView.color = "#FFFFFF";
		timeView.size = 12;
		timeView.truncation = "end";
		timeView.width = 90;
		newItem.view.addSubview(timeView);		// 加到 RSS 新聞項目 sub frame 中
		gRSSItems.push(newItem);			// 將所有 RSS 新聞項目記錄在 gRSSItems 陣列中
		itemList.addSubview(newItem.view);
		curY += 18;
		updateNow();
	}
}

首先我們先從第 2、3 點這些跟分析資料相關的重點程式碼來說明:

1 var nodes = rssDOM.evaluate("rss/channel/item");
這就是我們剛剛說的,從讀入的 rssDOM 這個 DOMDocument 物件中,萃取 rss -> channel 下的所有 item 項目,然後存在 nodes 這個變數中。
2 newItem = new Array();
newItem 是我們新製作的一個物件,每一則新聞都會對應到一個這樣的物件,使用者介面會用到的 Frame 物件(放在 view 物件變數中),還有從 RSS Feed 分析而來的 title(字串,新聞標題)、link(字串,新聞網頁連結)、pubDate(Date 物件,新聞發佈時間)等資料。當所有資料蒐集、分析、製作完畢後,就會被 push 到 gRSSItems 整體陣列變數中。
3 newItem.title = (nodes.item(i).getElementsByTagName("title")).item(0).firstChild.data;
這段有點長的命令,我們從裡到外分解來看。以 nodes.item(i) 來說,它就是用來依序存取剛剛被分析出來的 item 項目。(nodes.item(i).getElementsByTagName("title")) 這段便是存取 item 下所有 title 的 element 項目(不包含 attribute 屬性項目)。同樣的,它也是傳回一個陣列,因為有可能在這個 item 下有可能不只一個名為 titleelement 項目。但是我們在 RSS XML 的定義中,我們只關心第一個 title,所以 (nodes.item(i).getElementsByTagName("title")).item(0) 便會傳回第一個 title 項目。在文字的項目裡可能有超過一行的文字資料,而每一行文字資料都是一個在這個 title 項目下的 DOMText 物件,以這個範例為例,我們也只關心其中第一行的資料,因此我們便在之後加入 firstChild.data 便可存取到第一行的文字資料,這樣便需要 (nodes.item(i).getElementsByTagName("title")).item(0).firstChild.data 才能存取到我們要的新聞標題資料。在之後的 linkpubDate 的萃取中,也是以此類推。
4 newItem.pubDate = new Date(Date.parse((nodes.item(i).getElementsByTagName("pubDate")).item(0).firstChild.data));
這部份基本上跟第 3 點一樣,但是不同的是他將字串形式的日期直接用 Date.parse() 這個函式轉換成 Date 物件。這有點危險的是,目前 RSS 的日期字串不見得都可以用 Date.parse() 這個函式來轉成 Date,但以這個 Yahoo! 奇摩新聞的 RSS 為 parse 對象的話,可以只用 Date.parse() 函式即可,但不保證其他 RSS feed 的日期可以正確 parse 成 Date 物件。如果您打算用這段程式來做出某個 RSS 的專屬 widget 的話,這段程式務必修改。

▲Top

YWE 3.0 新增的 Frame 物件的用法與其 sub frame(subview)的關係

在萃取到我們要的新聞標題、日期與連結後,我們可以依據這些資料來建立顯示的資料。我們首先必須把每個顯示介面分開來看:

這是我們即將用來顯現新聞列表的介面,整個列表(紅框區,命名為 itemList)與包含 0 到多個新聞項目(行)還有一個捲動軸(綠框區,命名為 sb)。其中黃框區是單一的新聞項目(黃框區),其中一個新聞項目包含有兩個主要顯示文字區塊(藍框區,新聞標題與發佈時間),然後有三個事件處理程式(onMouseEnter 當滑鼠指標移入時,將文字以橘色顯示;onMouseExit 當滑鼠指標移出時,將文字以白色顯示;onMouseUp 當滑鼠按鍵被按下又放開時,會打開瀏覽器並連結至新聞網頁)。

我們假設在 MyRSS.kon 檔案我們定義了一個名為 itemList 的 Frame 區域給新聞列表顯示之用,如下:

程式列表 5

		<frame name="itemList">
			<hOffset>25</hOffset>
			<vOffset>45</vOffset>
			<width>340</width>
			<height>220</height>
			<vScrollBar>sb</vScrollBar>
		</frame>

其中比較特別的是我們利用 sb 為這個 Frame 嵌入一個垂直捲動軸,這樣便可以在有限的空間顯示更多資訊。以下是這個垂直捲動軸的宣告(名為 sb):

程式列表 6

		<scrollbar name="sb">
			<hOffset>365</hOffset>
			<vOffset>44</vOffset>
			<width>16</width>
			<height>220</height>
			<thumbColor>#dddddd</thumbColor>
			<autoHide>true</autoHide>
		</scrollbar>

首先說明 Frame 這個使用介面物件。在 Yahoo! Widget Engine 3.0 版之前,並沒有階層式的使用介面概念,也沒有 subview 的顯示方式。也就是說,所有顯示在 widget 裡的座標位置,都是只用同一個座標系統來標示,而當您要將一組含有多的使用介面元件做變化時(如隱藏或顯示),您必須要一個個去作。這樣還好,但是另一個帶來的問題就是,如果您有很多資料要顯示時,您並不能像正常的程式一樣,可以去利用捲視軸利用有限空間來顯示那麼多資料,因為也沒有所謂的剪裁區域(clipping region)物件讓您這麼作。

在新的 Yahoo! Widget Engine 3.0 版引進了這樣階層式的使用介面概念,並有了 Frame 與 scrollbar 物件,且提供非常容易的應用方式,一次解決上述的問題。

以這個範例為例,我們可以說,itemList 這個 Frame 是我們新聞列表最上層的顯示區域。這之下有 0 到多個的新聞項目 subview(也就是 Frame),給個新聞項目 subview 有兩個 Text 物件,一個 Text 物件是新聞標題,另一個是發佈時間。

我們來看剛剛的程式列表的一部份:

程式列表 7

		newItem.view = new Frame();				// 新增 RSS 新聞項目 sub frame
		newItem.view.hOffset = 0;
		newItem.view.vOffset = curY;
		newItem.view.width = itemList.width;
		newItem.view.height = 18;
		newItem.view.onMouseEnter = "hiliteItem(" + i + ")";
		newItem.view.onMouseExit = "dimItem(" + i + ")";
		newItem.view.onMouseUp = "openURL('" + newItem.link + "')";

以這段程式來說,這是我們定義每一行新聞項目的 subview(Frame),其中包括座標(curY 是累積的縱向座標,會隨著項目的順序而一次增加,越後面的項目縱向座標越下面,每次遞增 18)、顯示大小(寬度等於 itemList.width、高度 18),並定義 onMouseEnteronMouseExitonMouseUp 三個事件處理程式個別指向 hiliteItem()dimItem()openURL 函式。接下來看其下面的 subview 的程式:

程式列表 8

		titleView = new Text();				// 新增 RSS 新聞項目的新聞 title 文字區塊
		titleView.data = newItem.title;
		titleView.vOffset = 15;
		titleView.hOffset = 0;
		titleView.font = "MS UI Gothic, Arial, Helvetica";
		titleView.color = "#FFFFFF";
		titleView.size = 12;
		titleView.width = itemList.width - 100;
		titleView.truncation = "end";	1
		titleView.tooltip = newItem.title;	2
		newItem.view.addSubview(titleView);		// 加到 RSS 新聞項目 sub frame 中	3
		timeView = new Text();				// 新增 RSS 新聞項目的新聞 title 文字區塊
		timeView.data = newItem.pubDate.getMonth() + "月" + newItem.pubDate.getDate() + 
				"日 " + newItem.pubDate.toLocaleTimeString();
		timeView.vOffset = 15;
		timeView.hOffset = itemList.width - 95;
		timeView.font = "MS UI Gothic, Arial, Helvetica";
		timeView.color = "#FFFFFF";
		timeView.size = 12;
		timeView.truncation = "end";
		timeView.width = 90;
		newItem.view.addSubview(timeView);		// 加到 RSS 新聞項目 sub frame 中

在這段程式裡面有幾個值得注意的:

1 titleView.truncation = "end";
Text 物件的 truncation 變數主要是在設定,當文字內容超過指定寬度時,YWE 該如何顯示。如果沒有指定或是設定為 none,超出寬度的文字會被直接切掉。如果設定為 end,則超出部份會以 … 來顯示。如果設定為 center,則會顯示文字的頭尾,但中間的文字以 … 來顯示。
2 titleView.tooltip = newItem.title;
這是設定提示視窗(tooltip)之用。有些新聞標題超過我們設定的寬度,除了用剛剛的 truncation 來標示是個過長的標題外,如果使用者將滑鼠指標停在這標題之上幾秒鐘,便會出現提示視窗,顯示完整的新聞標題。
3 newItem.view.addSubview(titleView);
這段命令就是將 titleView 這個含有新聞標題的 Text 物件加到含有整行新聞項目的 Frame 之下,成為其 newItem 這個 Frame 的一個顯示物件。所有 titleView 用的座標系統都是以 newItem 這個 Frame 的左上角為原點。如果 newItem 這個 Frame 被隱藏、移動、更改不透明度,都會影響 titleView 的顯示。再這段程式的最後一行 newItem.view.addSubview(timeView); 也是以此類推,只是加入的是顯示發佈時間的 timeView Text 物件。

接下來看迴圈最後的幾行程式:

程式列表 9

		gRSSItems.push(newItem);	// 將所有 RSS 新聞項目記錄在 gRSSItems 陣列中	1
		itemList.addSubview(newItem.view);	2
		curY += 18;
		updateNow();	3

1 gRSSItems.push(newItem);
我們將已經都準備好、代表一行新聞項目所有資料的 newItem 物件記錄到 gRSSItems 陣列中,以方便我們之後的程式存取這些資料之用。
2 itemList.addSubview(newItem.view);
itemList 是整個新聞列表的顯示區域 Frame,所以我們已經把代表一行新聞項目的顯示資料放在 newItem.view 中了,因此我們必須最後把它放到 itemList 這個區域才真的會顯示出來,所以我們再次利用 addSubview 函式將 newItem 加入到 itemList 這個 Frame 之下。
3 updateNow();
這個函式主要是通知 YWE 立即更新畫面。如果您沒有呼叫這個函式的話,YWE 會自行選擇適當時機來更新畫面顯示。每一個新聞項加入到 itemList 這個 Frame 後我們都會呼叫 updateNow() 主要是在我們分析每一行新聞資料時,也讓使用者可以立即看到目前已載入並分析好的新聞項目,但您也可以選擇不呼叫。

▲Top

使用 Widget Converter 將 RSS 新聞閱讀器 Widget 打包成跨平台的 YWE Widget

接下來來做最後的打包工作。在剛剛的 Hello World 範例中,我們使用了手動的方式來打包 widget,但是事實上有更簡便的方法。您可以到這裡下載 Widget Converter 這個 widget,下載後打開後,請用下列步驟將 RSS 新聞閱讀器打包起來:

1. 將「RSS Reader」這個檔案夾拖到 Widget Convert 的視窗。

2. 之後會出現如下圖的變化,此時請按下「convert」按鈕。

3. 過一下子,會出現下面的視窗,並會在您「RSS Reader」所在檔案夾中出現「RSS Reader.widget」的 widget 檔案。這樣便大功告成。



其實 Widget Converter 還可以做相反的事,就是如果您將一個 widget 檔案拖到 Widget Converter 的視窗中並按下「convert」按鈕,便會將壓縮的 widget 檔案轉成檔案夾,非常方便。

▲Top


▲前頁 ☆目次 ▼次頁