Posts Tagged ‘flex’

利用GroupingCollection幫你分組

Friday, March 26th, 2010

有些時候我們有一堆資料物件, 需要根據物件的某個屬性值作分類, 最直接的方法就是跑個迴圈,如:

var hash:Object = {};
for each( var obj:Object in objectList )
{
//檢查obj的屬性值
if( obj.name in hash )
hash[obj.name].addItem( obj );
else
hash[obj.name] = new ArrayCollcetion([obj]);
}

//結果就是根據 obj.name 值分類好的hashmap

但我們可以利用Flex的GroupingCollection幫我們作這個工作, 而且過程封裝的更好:

var groupingCollection:GroupingCollection();
var gping:Grouping = new Grouping();
var field:GroupingField = new GroupingField("你要分群組的屬性名稱");
//給予要群組的屬性, 可有多個GroupingField,
//會依序群組, 非常類似SQL::GROUP BY
gping.fields = [ field ];

groupingCollection.grouping = gping; //設定Grouping
groupingCollection.source = 原始資料 (通常是ArrayCollection);
groupingCollection.refresh(); //叫GroupingCollection開始工作

//重要的來了, 呼叫getRoot()取得GroupingCollection幫我們分群好的資料:
//結果通常是一個新的ArrayCollection, 裡面有多個Object ,
//每個 Object 有一個children屬性, 包了分組好的物件(原物件)
//Object本身有個預設的GroupLabel屬性,是根據你的資料內容的群組名稱
var result:Object = groupingCollection.getRoot();

預設的GroupLabel屬性名可以透過: Grouping#label 改成你想要的屬性名稱。
或許你會覺得這哪裡好,比一開始的還要複雜很多,其實他的威力不只是分群,還可以幫你分群作加總、求最大/最小值/平均值的功能呢,利用 SummaryRow 就可以輕鬆作到喔!

var summaryRow:SummaryRow = new SummaryRow();
var smryField:SummaryField = new SummaryField("price","SUM"); //price ==> 要處理的屬性名稱
summaryRow.fields = [smryField];
var groupField:GroupingField = new GroupingField( "name" );
groupField.summaries = [ summarRow ] ;
...參考上方

上述結果是一個根據物件的”name”屬性分組,並且根據物件的”price”屬性作加總的GroupingCollection。同樣地利用getRoot();取得結果,可以看到新建立出來的Object#children除了有分群好的物件之外,還多出了SummaryObject,裡面就是幫我們算好的值。

補充:

自訂群組結點的標籤產生方式:

GroupingField#groupingFunction :
自訂group object#label的方法,在label不是單單靠某屬性值就能解決的情況,特別好用。 ex: 想作出一個group#lable = vo.name + vo.lastName + vo.id
signature:

groupingFunction(item:Object, field:GroupField):String

特別注意的: 當你覆寫了這個方法同時,記得也要覆寫 GroupingField#compareFunction,因為GroupingCollection建立GroupingCollectionView的時候會先對原始內容(來源ListCollectionView)排序,再產生Grouping(這樣效能比較好)。因此假設你的compareFunction並沒有寫好,那麼你會常常看到同一個群組結點標籤出現在不同的群組結點上。如果你不想用compareFunction,那麼,至少GroupingField#name的值對排序要有意義。

自訂群組結點物件內容:

GroupingField#groupingFunction :

產生group object的方法,當你不想要用預設的groupobject + label的方法,可用此方法自訂一Grouping Object的結構。
signature: myGroupObjectFunction(label:String):Object

Embeded font

Saturday, December 19th, 2009

常用的Embeded font 寫法,從Adobe手冊抄來的:
重點是: 用同一個Font Family Name 但是Embed不同的Font Face, 這樣可處理大部分的字型問題,不然,常常會有字形粗體時跑回Arial, Tahoma 的情況。

@font-face {
        src:url("../assets/MyriadWebPro.ttf");
        fontFamily: myFont;
        advancedAntiAliasing: true;
     }

     @font-face {
        /* Note the different filename for boldface. */
        src:url("../assets/MyriadWebPro-Bold.ttf");
        fontFamily: myFont; /* Notice that this is the same alias. */
        fontWeight: bold;
        advancedAntiAliasing: true;
     }

     @font-face {
        /* Note the different filename for italic face. */
        src:url("../assets/MyriadWebPro-Italic.ttf");
        fontFamily: myFont; /* Notice that this is the same alias. */
        fontStyle: italic;
        advancedAntiAliasing: true;
     }

see:
http://livedocs.adobe.com/flex/3/html/help.html?content=fonts_04.html#133099

[Embed(systemFont='Myriad Web Pro',
        fontStyle='italic',
        fontName='myItalicFont',
        mimeType='application/x-font',
        advancedAntiAliasing='true'
     )]
     private var font2:Class;

你也在寫Flex嗎? 那你可能要特別注意…

Tuesday, November 10th, 2009

本篇文章來自MAX2009的session:
How Not to code FLEX

演講者主要說明何謂壞程式碼(bad code),但也說壞程式碼不一定不好,只要你確定能盡快回來改正他。那何謂壞程式碼?

  • Container 元件的誤用:Container元件諸如Box, Canvas, Tile等等,他們的原本目的都是: 排列裡面的子元件。因此只要你不是拿來排版或甚至沒有給予它任何的子元件,就違背原本用意了。
    例子:

    <mx:TextInput/>
    <mx:VBox backgroundImage="blah.png" borderStyle="solid"/> <!-- 你看, 沒有小孩的容器元件-->
  • ListBase 元件的誤用: List, TileList, DataGrid 都是用來針對列表物件的選取、排序、renderer回收而生,因此,當你想要製作的元件並不需要這些功能,就不該採用List來處理。
  • local model binding: 避免直接將值給UI元件,盡量用local model binding來處理。(按:此方法還可避免元件初始化延遲所帶來的問題)
    ex:

    <mx:script>

      //這樣是不好的
      function test():void
      {
         myLabel.text = "hello";
      }

    </mx:script>
    <mx:Label id="myLabel"/>
    <mx:Script>
    //用local model binding.
    [Bindable]
    private var helloText:String = "hello";
    </mx:Script>
    <mx:Label id="myLabel" text="{helloText}"/>
  • 別把local view event 交由非常上層的元件處理。(按:這裡必須搭配cairngorm microframework 來解釋 )
    元件與元件之間的訊息傳遞,如果能在local就解決,便盡量不要拋到上層的command-frontcontroller 去處理然後又繞回來”原本的元件”。以後維護會很辛苦。
  • 不適當的容器元件使用方法:
    如果你發現你的容器元件裡面只有一個子元件(child),那可能有問題。
  • Two dots and you are out.
    兩個參照你就出局囉!

    如果你的程式碼需要進入非常多的子元件,那就有問題了。如下:

    var hey:String = myBox.thisBox.thatBox.rightLabel.text;

    這是一個本質上的問題,應該要封裝這些子元件,若其他元件需要存取裡面的值,應該要透過Event(帶資料出來) 來幫你。

  • 自以為的聰明寫法並不好。
    這種情況會發生在複雜的binding寫法,像是:

    <mx:Button id="btn" enabled="{ isCheck &amp;&amp; ( firstBtn.enabled &amp; !secondBtn.enabled) &amp;&amp; (thirdBtn.enabled|| feelGood) }"/>

    應該要用function 去處理這些binding 邏輯:

    <mx:Script>
    private function getEnabled( a:Boolea , b:Boolea , c:Boolean , d:Boolean , e:Boolean ):Boolean
    {
    //邏輯應該在這裡描述啦:
    return a && (b&&!c) &&(d||e);
    }
    </mx:Script>
    <mx:Button id="btn" enabled="{ getEnabled(isCheck,firstBtn.enabled,secondBtn.enabled,thirdBtn.enabled,feeGood) }"/>

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

Sunday, 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.

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

Monday, 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

Thursday, 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();
        }  
}

Column Chart v.s. Bar Chart

Wednesday, October 1st, 2008

Well, it was confusing. I was thinking bar charts and column charts were doing the same thing. The distinct appearance of them is the direction of the “bar”. Actually, you could call columns “bars” if you want. Thus, I event didn’t get this critical idea that a bar chart CANNOT draw bars vertically. You know what? I struggled with this for hours. Finally I realized it.

So, please remember:
A Column Chart is showing number of columns vertically, which seems to “stand” on the horizontal axis.
A Bar Chart is showing number of bars horizontally, like a rotation column chart.
However, they are logically different.

測量文字框

Wednesday, September 17th, 2008

有個很方便的方法:

UIComponent#measureText( text:String ):TextLineMetrics

先看看官方說的:
Measures the specified text, assuming that it is displayed in a single-line UITextField using a UITextFormat determined by the styles of this UIComponent.

簡單來說,這個方法會根據你當下使用的文字樣式,測量一段文字的長寬高。舉例來說, “Taiwan” 這幾個字,在各種字體、字型大小設定下,顯示出來的結果當然不同(比如字體大小11pt時高度為21px; 14pt時高度為34px…) ,為了要得到正確的文字框數據,就可以運用該方法。
那麼,到底什麼時候會用到呢? 很多時候,在開發與TextField相關的自訂元件時,會大量地運用到,譬如 Button,裡頭的Label和icon要對齊,label的位置就利用了此方法測量出來,然後位移之。

看看原始碼,應該會常常看到這種寫法:
因為大寫W是最高的字, 小寫j是最低的字, 如此可以算出最大的文字框高度

measureText("Wj");

算出現有的文字框高度

measureText(textField.text);

UIComponent#measureText(text:String):TextLineMetrics

It is a very handy method for determining the bounds of a specific text. To illustrate, with different text format, the appearance of text must be different. In other words, the height, width and other values will be changed as well. Therefore, to make sure we get the correct and precise values of it, we can call this method to get a TextLineMetrics object, which provides all the properties. Given that object, we could update positions of components precisely.

For example, while the label of a button changes, we could measure it by

measureText(label)

or
to get a safe size of it, use following code:

measureText("Wj")

Here is a thing, the capital w and lowercase j are the latter with highest baseline and the lowest. That’s why Adobe developer often writes up code in this way.

FYI

Hide Chart Elements

Sunday, August 24th, 2008

First of all, a visual chart, generated via Flex-Charting framework, is constructed with many layers of elements. They are series(data) layer, axis layer, background elements and annotation elements. Besides, you are able to create your own chart elements by implementing IChartElement2 interface.

Here are some quick tips:

  • To kill the grid lines behind charts:
    backgroundElements=[]
  • To make the grid lines drawn with larger interval:
    GridLines#horizontalChangeCount = larger_number;
  • hide elements of axises
    AxisRenderer#showLabels = false;
    AxisRenderer#showLine = false;
    AxisRenderer#tickPlacements = "none";

用Filter Function作簡單線性圖Drill-Down

Monday, July 28th, 2008

首先,想像有一張線性圖(Line Chart),上頭的線是由許多資料點所構成。在Flex中,一旦對該資料及和作了過濾的動作,圖表會重繪。然後,Flex3的圖表都有支援selectedItems:Array的方法,以獲取你圈選的資料點,因此,我們可以藉由選取的資料點,得到一個範圍,利用範圍來過濾原資料集合,就可以做到類似Drill-Down的功能了。

這是範例(線性圖的drill down)
這是原始碼


Drill down into data in a line chart.

With Flex3 data visualization package, we are able to get the selectedItems by

ChartBase.selectionMode = "multiple";<br/>ChartBase.selectedItems

Therefore, it is handy to measure a range for filtering out our data. We have to know that every series in a chart contains a data provider. The data providers are ListCollectionView objects. So to filter out those objects will update the display of chart immediately. Those lines will be redrawn. You could see the line scaled just like drilling-down into it.

Here is a sample and source code.
Simple Drill Down into Line Chart
Source