AIR 程式效能調校技巧

May 7th, 2009

更新@2009/5/11:
Adobe 有個驅動Garbage Collection的方法:

flash.system.System.gc();
flash.system.System.gc();

但是必須連續呼叫兩次,第一次呼叫: 對物件加上記號;第二次:清除記憶體
更多詳情請見:
http://www.craftymind.com/2008/04/09/kick-starting-the-garbage-collector-in-actionscript-3-with-air/


原文:
http://blogs.adobe.com/air/2009/05/performance_tips_for_adobe_air.html

在處理Grant Skinner發現的CPU效能問題之前,有幾個方法可以先改善AIR程式的效能:

  1. 盡可能地減少frameRate
  2. Grant Skinner提供了一個小程式,監聽 AIREvent.APPLICATION_DEACTIVE事件(該事件在AIR的native window失去focus時會觸發; 重新得到focus時,會觸發 APPLICATION_ACTIVE),自動將frameRate降至 1 ,可以看他的文章: http://www.gskinner.com/blog/archives/2009/05/idle_cpu_usage.html
  3. 盡可能用Timer; 而不是監聽EVENT.ENTER_FRAME做處理
  4. 用profiler 查看Flex app吧

更多文章:

關於AIR Native Menu(for Mac)

May 5th, 2009

用Adobe Air 製作 Mac用的native menu時,發生了一個狀況: 在預設的submenu下加上自己的NativeMenuItem,監聽menu的Event.SELECT事件無法成功,很奇怪,但是只要整個menuItem是自己建立的instance就不會出現該問題。換言之,必須手動抽掉 File, Logo 類型的menu item,然後重建。挺麻煩的。
還有一個地方,監聽Event.SELECT的事件處理程式必須加上 Event#stopImmediatePropagation() ,否則會出現重複聽到事件的狀況。
要小心呢!

偵測swf的安全漏洞: SWFScan

March 26th, 2009

HP 推出可以偵測swf檔的安全漏洞的程式: SWFScan
可到HP官網免費下載!

少用File#nativePath, 用File#url

March 22nd, 2009

File.nativePath 是完整的絕對路徑,如果直接用nativePath的值當作路徑,在mac上有時會出現IOError。然而,File.url 會把applicationStorage的路徑用app-storage: 替換掉,參照的時候再轉換,對於不同的系統結構,用File.url是比較保險的作法。

var docs:File = File.applicationStorageDirectory;
trace(docs.nativePath); // C:\Documents and Settings\user\Application Data\my_air\Local Store\
trace(docs.url); // app-storage:/

一次展開Tree元件的所有節點(Expand All Children of Tree)

March 22nd, 2009

展開一個Tree元件的所有節點的方法,有兩個階段:
1. 走訪所有節點
2. 如果是枝幹(branch)結點,就打開它( 有預設的方法可以使用, expandChildrenOf( node, open) )

要提醒的地方是,所有ListBase類型的元件,裡頭都有一個collectionChangeHandler的涵式,我們可以複寫他,讓每次資料重讀之後,就打開全部節點。
請見下方原始碼 :)

===
All ListBase-components handle collection with a collectionChangeHandler(event). All we have to do is override it by adding a expandAll() function. How will we do with this expandAll()? See following sample:

override protected function collectionChangeHandler(event:Event):void
{
    super.collectionChangeHandler( event );
   
    if( expandChildrenAfterReset )
    {
        if( CollectionEvent( event ).kind == CollectionEventKind.RESET )
        {
            expandAll();
        }
    }
}


public function expandAll():void
{
    if( collection )
    {
        var c:IViewCursor = collection.createCursor();
        var o:Object;
        while( !c.afterLast )
        {  
            o = c.current;
           
            if( dataDescriptor.isBranch( o ) )
                expandChildrenOf( o ,true );

            c.moveNext();
        }
    }
}

As you see, we just iterate all tree node, determining if it is a branch and then call the built-in expandChildreOf(node,open) method.

漲潮的回憶

March 8th, 2009


La Maison en Petites Cubes from dorsumi on Vimeo.

很喜歡他的畫風和故事性。

小塔山的大冒險

March 7th, 2009

全程所有照片:http://www.flickr.com/photos/shiue/sets/72157614711552269/detail/
DSC_0859

登山社的年度計畫投票結束了。
塔山進了榜,心情卻像鐘擺盪了下來,後悔當初不該開玩笑地要B君提議塔山行程。相信除了我之外,沒人看過「MIT台灣誌塔山紀錄片」,上次到來吉部落渡假村看了紀錄片,便覺此路程跳脫以往百岳類型;百岳的稜線路徑雖然伴著狂風和徹骨之寒,但大致路徑都是清楚。然而,塔山屬中級山,紀錄片中的畫面:草比人高、路不成路,加上峭壁陡坡,危險度大大提升。
B君用心地準備此行,我猶豫,不知道該不該說出心裡的憂心呢?

到了來吉部落,嚮導,亞巴斯勇大哥準備了簡單的宵夜,以及紀錄片,讓大家有個心理準備,不明瞭大家的胃口如何,我幾乎食不下嚥。暗暗想著,此行千萬不能勉強,就算塔山顛在十步之遙,也必須放棄。
晚上,再一次仔細檢查裝備,再一次地瞥見斯勇大哥準備的安全繩,我沒法抑制自己不去想塔山鋒利的山壁,無法不去想寸草不生的陡坡,更無法不去擔心,陡坡下毫無障礙物的懸崖。刻意戴上耳塞早早躲進被窩,貪圖多一點的休息時間,癡心妄想地幫自己建立丁點信心,然而,一閉上眼睛,夜的黑彷彿脫離了束縛朝我壓來,夥伴們的聊天和鼾聲,是墨色的隱隱回聲,心跳不自主地快了起來,我好像已經站在峭壁上,手再也拉不動繩索,進退兩難,放手與死亡的透過黑夜更加立體。『擔心』在睡眠裡肆無忌憚地變化成各種噩夢,擾亂了時間的感知,連上個廁所,都擔心門外是不是已經天明,深怕休息不夠,讓隔天更加兇險。

一早,大家默契十足將多餘的裝備卸下,盡量減少負擔,以面對未知的懸崖峭壁。到了登山口,斯勇大哥虔誠地祭祀山神,我默默地跟著他喃喃念著,希望每個伙伴,平安歸來。山神是眷戀我們的,給了我們好天氣。
一出發,立刻追到斯勇大哥後方,除了不要拖累整個隊伍之外,也希望多拍照,越凶險的旅程越值得多多紀錄。這是登山者的矛盾吧,明明知道危險,卻義無反顧,我的任務便是將這一刻保存下來。
DSC_0050
(斯勇大哥)
一路上,斯勇大哥的山刀從未停下,他如同破冰船,替我們從芒草海中,硬生生劈出一條路。撥開層層疊疊的芒草,強韌又細薄的芒草葉不斷地在週遭舞動,彷彿活生生的柳葉刀,無情地對每個人臉上、手上、腳上切割凌遲;除此之外,芒草莖亦是一大威脅,有次攀爬沒注意面前一小叢芒草,奮力避開眼睛,犧牲了嘴角,銳利的莖部割開了肌膚,留下了一道小疤。然,芒草亂舞雖然惱人,卻不是最大的危險,最大的危險,往往是隱藏在芒草底下的山溝懸崖,它們是天然的陷阱,無時無刻地考驗著隊員的注意力。由於塔山人煙罕至,幾乎看不到明顯的路徑,在茂密的植被裡前進,可以發現:『下一步的上下落差』、『周圍土地是否穩固』,似乎永遠都是疑問句。
DSC_0085

DSC_0505

塔山岩石多為砂岩及頁岩,經過風吹日曬,容易剝落龜裂形成稜稜角角的地形。其中,峭壁,往往最令人驚嘆,除了讚嘆大自然的鬼斧神工之外,也驚嘆自己居然要攻上那不可思議的角度。每次仰望卻看不到峭壁頂,無法驚恐,只能股起所剩無幾的勇氣,『讓我第一個爬上去吧』。塔山峭壁生的像一個『上』字,表明了無路可退只能往上,有時中段會有休息的平台;但有時只能咬緊牙關一口氣衝到頂點,單純的地貌地形,暗示了生死的極端。最厲害的是斯勇大哥,他總是當攻擊手,率先攻上頂峰或是中段位置,安置安全繩讓我們放心攀爬。
DSC_0330
(斯勇大哥先攻上去)
DSC_0172
(安全繩)
DSC_0383
(三段攻頂)

爬升的過程中,最好的朋友不是斯勇大哥的安全繩;而是曾經惱人的芒草,當主和安全繩無法顧及所有的施力角度,芒草像是山神的化身,親切地拉了一把,助你上升,一放開,他又準備好活力迎接下一位伙伴,漫長的攀爬過程中,他默默地祝福我們,我們是幸運的。然而有時候,山神是刻意給我們考驗罷?陡峭的岩石加上滑溜的青苔,無處著力的我們,只能全憑肌耐力引體向上,還必須面對失敗的恐懼,因為,往往峭壁的旁邊便是懸崖,是通向死亡的溜滑梯,週遭的路徑又異常地狹小,若真的失誤,後果難以想像。難怪斯勇大哥說,我們不可能走原路回來,因重裝攀爬陡坡更難控制重心,消耗的力量更大。所以我們無法回頭,只能向上。
大峭壁

小塔山中段,俯視來吉部落。不久前的跨年到此處參加活動,然而,我作夢也沒想到會回來,更沒想到,會以俯視的角度面對『鐵達尼』等景點。
DSC_0196
(阿里山溪、清水溪、濁水溪)

在山路的轉角,往往會有攝人的風景和稍來驚喜涼風,讓疲憊的我們稍稍放鬆緊繃的肌肉,不知道爬過了多少個好漢坡,也不知道斯勇大哥說『快到了!』究竟有多快呢?

DSC_0160
(木棧道)

塔山上有許多早期的棧道,有時候它們跟峭壁一樣難行。大部都已經爬滿了植物,原本的外貌已經模糊,但堅固依舊。棧道的由來:日據時代,日人從阿里山上砍取大量的檜木,並要求鄒族婦女扛下來,這就是她們走過的,用檜木做成的棧道。想想當時的苦,我們真是幸福了。

DSC_0600
(突破險路)

DSC_0803
(這不是水泥牆,是大自然的山壁)

接近黃昏,終於到達了紮營點『泉水仙洞』,他像是山神在塑造塔山時,不經意摳掉的一小段,也可說是神來一筆吧,如山水畫裡神采飛揚的一橫,再不容任何雕飾。這是一段橫切的山凹處,裡頭地面平整還有泉水滴入,先人還特地背了一個小水塔汲水,方便了後人飲用;斯勇大哥也早已安排了兩個帳棚供大家休息,還熱情地要我們簽名,看看上一個簽名的日期,竟然是西元2001年了。山洞裡頭還有一個日據時代留下來的大茶壺,煮水都靠它,也許不久後,它便吸收了夠多山中靈氣,成了塔山茶壺仙了吧?
此山洞正正地坐北朝南,直接將塔山群延攬入眼底,彷彿塔山的龍椅。晚上有幸在龍椅下一眠,大快人心。也不忘再一次感謝山神給我們好天氣和平安。
DSC_0124
(泉水仙洞)

DSC_0039
(帳篷簽名)

DSC_0918
(茶壺仙)

DSC_0930
(祭祀山神)

是夜,斯勇大哥巧手燒了山豬肉大餐,佐上過期的罐頭和糖果,以及各式材料的精力湯,洞外的紅橙色的夕陽和鵝黃色的彩雲,和滿溢的山嵐雲氣,旁邊是,從前不見的中海拔樹木以及濃密像是抹茶一般的草叢,洞裡溫暖的柴火燒出陣陣白霧,如同另一道雲氣緩緩地朝遠方會合,整齊排列的登山背包後不時傳來同伴的陣陣笑聲,我想做皇帝也不如這般愜意。
深夜,是另一個舞台,專門為斯勇大哥、鄉長(信忠)和木烏大哥(明威)準備的。他們打獵去了,很難想像整天照顧我們之後,居然旺盛的體力在全然幽暗的環境下追捕獵物,更難想像,我們早上經過的峭壁的險峻地貌,他們必須在黑夜下奔跑疾行,他們,是不折不扣的山神的孩子。
記得曾經問過斯勇大哥如何在黑夜中辨識正確的路,他說:『踩踩看,細微的軟硬差異就是路標。』也許我再爬個一千次就可以體會了吧。
DSC_0018

清晨,從露宿袋中醒來,感覺像破繭而出。
又是個好天氣,山神真的很眷戀我們。以後每次爬山一定都要照著斯勇大哥的方式祭祀一番。
今天預計到三角點,然後直接攻上阿里山。

DSC_0089
(斯勇早餐店)

DSC_0110
(木烏大哥)

DSC_0139
(離開仙洞前)

斯勇大哥給了好消息:不用爬峭壁了,但是他沒說:有很多好漢坡等著我們。山羊徑是我們攀過不知道第幾個好漢坡到達的路徑,它的獨特之處在於,沿著遠方所見的塔山峭壁行走,摸著沙岩,就算隔著手套也能清楚感受到岩石的觸感以及特殊的『萬年松』,前方依舊沒有明顯的人跡,不同的是,茂密植被是及腰劍竹,沒有芒草的殺傷力,劍竹輕盈的枝葉可比一張大大的薄被,輕飄飄地覆在真正的路下,撥開即可見到真正的人跡。回頭一看,我們好像置身叢林一般呢。山神再一次照顧我們,在艷陽底下,給了我們樹蔭和涼風,這段路程雖長,但清閒許多。遠方的玉山山脈、二萬坪、多羅煙車站伴著我們走著,在偉大的塔山下。
DSC_0314
(山羊徑)

到了『杜鵑坡』,每年四五月會開滿整個山巔,喔,要提醒你,這其實應該叫做『杜鵑好漢坡』。過了它,三角點便在眼前,塔山二等三角點,俏麗的令人想親吻它。
DSC_0276
(山羊徑)

DSC_0045
(好友B君與三角點)

DSC_0082
(B君與地毯)

中午休息地點,是個魅力無限的地毯『松針地毯』,由無數細密的松針鋪成,有如千萬絲線層層疊疊,柔軟且滿溢著松香,躺下後絕不想起來。這裡也是阿里山步道的交會點,從姐妹潭過來的步道是比較休閒的路,遇到了其他山友,由他們驚訝的表情,才注意到,我們一行人的特立獨行,誰也沒想到,小小的塔山裡竟然有如此美妙的冒險呢!

DSC_0143
(終點合照)

DSC_0154
(眠月線)
終點站:眠月鐵路,九二一大地震坍崩的小火車支線。這是本段旅程最特別的調整運動,步行鐵道九公里,沿著大塔山腳下,呼吸著飄上山巔的雲氣,幽靜鐵道和卸下重裝的我們,塔山依然眷戀著我們,不忘展現它豪邁的峭壁山勢。與夥伴們信步走著,綠色的微光若隱若現,似乎是山神給我們的小小肯定、喝采,鐵路綿延,我看不到盡頭,就像昨天,看不見峭壁盡頭一樣…然,我希望這條路,永遠沒有盡頭。

DSC_0197
(眠月鐵路)

DSC_0243
(辛苦的斯勇大哥)

Drag and Drop on AIR.HTML Using NativeDragManager

March 4th, 2009

DOM object in HTML ( HTML.domWindow ) stop the Drag events propagating so that dragging over HTML ui will not succeed, which means you cannot see an accept-drop-icon near mouse cursor. To solve this, you have to register a handler to prevent DOM doing something behind but accept drop action, as following sample code:

myHtml.domWindow.document.addEventListener( "dragover:", onDomDragOver );
function onDomDragOver( e:* ):void
{
e.preventDefault();
NativeDragManager.acceptDragDrop( this );
}

試圖用NativeDragManager拖拉外部物件到Air時,如果遇到HTML元件,會出現無法acceptDragDrop的情況,此時,必須針對HTML#htmlLoader底下的DOM 物件監聽 dragover/dragenter 等javascript事件,阻止DOM自己的事件處理,並在此時,命令HTML接受dragDrop,請見上方原始碼。

PS: Javascript drag event types:
dragover
dragenter
drop

ex: getElementById( “myBox” ).addEventListener( “dragover” , onOverBox );

Table-like List (類似表格的List)

February 23rd, 2009

List displays scrollbar if containing too many items. What if we just need it expand as high as possible no matter how many items here? Some solutions are available :
If variableRowHeight is false, please assign explicit height as number of height multiplies rowHeight, as following code:

height = rowHeight * IList(dataProvider).length

Otherwise, if you get variableRowHeigh ture, there is a handy function for you:

measureHeightOfItems(0,0);

Please see List#measureHeightOfItems() for more information.


要讓List元件自己長到顯示所有資料的高度,像是表格一樣,有以下方法:
如果variableRowHeight 為 true , 表示每個資料物件所需欄高不同,可以用 List#measureHeightOfItems(index,count); 得出總高度。 反之,欄高都相同的情況,只要把欄高乘於資料數,得出總高就是了。

height = rowHeight * IList(dataProvider).length ;

消失的BarSeries標籤文字 - Missing Labels on Bar Series

December 11th, 2008

[更新@2008.12.16]
關於更改Series上的label,修改InstanceCache宣告會更快更好:

/**
*To manipulate label instances on series, overridding the instanceCache object  is a better approach.
**/

labelCache = new InstanceCache( Label , labelContainer )
labelCache.property = { truncateToFit : false };

使用Bar Chart 的時候,有個現象:
當Bar上頭的標籤文字太長,以至於無法放進畫面中,BarChart 會清除掉裡面的文字內容:
label.text = “”; (請見BarChart.as line 662)
好在,它只是清掉renderData的cache,並非真正的dataProvider內容(如果是,就糟了),也因此,我們有機會把這些文字回覆,逼它無論如何都要顯示,方法很簡單,你可以自訂一個BarSeries,請見如下程式碼。走訪dataprovider,把值塞回去給Label 實體即可,但請確保文字框夠大,或是把truncateToFit改成false,這樣就不會被裁切掉了。

[English version]
A Bar-Chart renders several series where those labels show up. If you have read source of BarChart.as, you would aware of one thing: If by checking the TextField width against remaining space, there is no enough space to place the label , the label text will be eliminated. ( see source: BarChart.as Line 662 ) However, I think Adobe team might do too much here so that even if you set explicit textField width with a very large number, it won’t work. Fortunately, BarChart itself clear the renderCache instead of dataprovider so that we could recover the text of each label. Here is an example:

// extends BarSeries

//override updateDisplayList()
override protected function updateDisplayList( w:Number , h:Number )
{
super.udpateDisplayList( w , h );
//here we get all created *Label* instances
var labels:Array = labelCache.instances;
// cursor is a IViewCursor, iterating the dataprovider so that cursor#view is just dataprovider object.
var dataList:Array = IList( cursor.view ).toArray();
var len:int = labels.length;
//iterate all label instance, if the text is removed, re-assign it and validate as well.
//one more thing, the length of labels and dataprovider should be the same.
       var label:Label;
    for(var i:int = 0 ; i < len ; i++ )
    {
        label = Label( labels[i] );
        if( label )
        {
            label.truncateToFit = false; //optional
            label.width = Math.max(w,300); //optional
           
            if( label.text == "" && labelFunction != null )
            {
                var c:ChartItem = ChartItem( items[i] );
                c.item = v[i];
                var txt:String = this.labelFunction( c , this )
                label.text = txt;
            }
           
            label.validateNow();
        }  
}