2008年8月19日 星期二

Binding 的使用, part 1

Binding 是 Flex 中一個很特別的項目。許多傳統的程式語言中,大多都沒有類似的東西。說真的,福克斯認為,要設計一個非常易於使用的 Binding 並不是件容易的事。對福克斯來說,Flex 的 Binding 算是一個還算簡單,但還不夠直覺的方式。不過,經過幾次的使用,福克斯覺得 Binding 真的是一個很方便的工具。

最近,福克斯須要教同事,如何設計 Flex。當我們討論到 Binding 的時候,福克斯發現,這個觀念似乎對某些程式設計師來說,不容易被接受。一些 Flex 的設計師們可能會覺得,這麼好用的東西,怎麼可能會不容易接受。福克斯發現,不易被接受的原因並不是不好用,而是不夠直覺。說真的,Flex Binding 的設計可以算是目前不錯的設計。所以這一系列的文章,將會和大家一起討論,如何正確地使用 Binding 這項工具。

Binding 是指一個永遠成立的方程式,例如: objA.x = objB.y + n。此時,如果 objB.y 改變的時候,objA.x 也會跟著被修改。針對這種關係,在傳統的程式語言中(如 Java, C#, C++, JavaScript…等),我們必須要在 objB 中加入一個 event listener 用來聽取 y 修改的事件,進而重新計算方程式的結果。

Flex 的 Binding 也是使用同樣的設計方式。當我們在 Flex 中建立一個 Binding 時,Flex 會自動建立 event listener 並自動建立重新計算方程式的 function。

福克斯建議,第一次接觸 Binding 最好從最簡單的模式開始學習。所以,以下是完成 objA.x = objB.y 的 Binding 呼叫方式:

BindingUtils.bindProperty(objA, "x", objB, "y");

這段程式中,BindingUtils 是 mx.binding.utils package 中用來建立 Binding 的物件。最簡單使用 Binding 的方式,就是讓兩個物件的一個屬性互相連結在一起。以上面的這個例子來說,我們使用最簡單的方式,即 bindProperty 來連結兩個物件的屬性。我們可以直接以方程式 objA.x = objB.y 的方式來看,呼叫 BindingUtils.bindProperty 時,可以使用同樣的順序來呼叫。所以,我們可以直接將方程式的參數,直接代入 BindingUtils.bindProperty 之中。要特別注意的地方是,物件的 property 部份,都必須要以『字串』的方式輸入。




In Flex, the binding is made by BindingUtils and ChangeWatcher.

2008年8月8日 星期五

Autoresizing Canvas


Auto-resizing is a common feature in contemporary programming languages, but not Flex. It is uncommon. After surveying, I have to admit that I can't find it. So, I decide to write it by my own. 

The auto-resizing is based on the mouse position and dragging operation. The dragging operation can be observed by mouse down or drag start.  The mouse position can be observed by the mouse move event. But we won't be notified while the mouse is moved out side of canvas. Another way to be aware of mouse can be done by reading mouseX and mouseY in enter frame event, even if the mouse is moved out of canvas. 

The example program will be designed as:
1. using a boolean flag to record mouse is down
2. resizing the inner canvas while the mouse is dragging the right-bottom corner
3. check if auto-resizing while entering frame

The following are the code:

<mx:canvas id="outer"  enterFrame="handleEnterFrame(outer.mouseX, outer.mouseY)" mouseUp="handleMouseUp(inner.mouseX, inner.mouseY)" mouseDown="handleMouseDown(inner.mouseX, inner.mouseY)" mouseMove="handleMouseMove(inner.mouseX, inner.mouseY)" x="114" y="56" width="334" height="271" borderStyle="solid" borderColor="#FFFFFF" borderThickness="5">

<mx:canvas id="expander" x="0" y="0" width="{inner.width+ 20}" height="{inner.height + 20}">

<mx:canvas id="inner" x="10" y="10" width="200" height="200" borderStyle="solid" borderColor="#33444F" borderThickness="2">

</mx:canvas>

</mx:canvas>

</mx:canvas>


The above codes declare three canvases, outer, expander, and inner. The outer is the bound while holds the resizing canvas. The expander is one of the resized canvas who preserves 30 pixels offset between inner and outer.  The inner canvas is the one we want to resize.

private function handleMouseDown(x:Number, y:Number):void

{

if(x > (inner.width - 20) && y > (inner.height - 20))

{

m_MouseDown = true;

m_MouseOffsetX = x;

m_MouseOffsetY = y;

}

}


These codes handles mouse down event of  outer canvas, but the x and y arguments are mouseX and mouseY of inner canvas. This method marks mouse down while a user presses right-bottom corner.

private function handleMouseUp(x:Number, y:Number):void

{

m_MouseOffsetX = 0;

m_MouseOffsetY = 0;

m_MouseDown = false;

}


These codes handles mouse up event of outer canvas. It marks mouse not down.

private function handleEnterFrame(x:Number, y:Number):void

{

if(m_MouseDown){

if(x > outer.width - 30 && x <>

{

inner.width += 1;

m_MouseOffsetX = inner.mouseX;

outer.horizontalScrollPosition+=2;

trace("Frame Increase X");

}

if(y > outer.height - 30 && y <>

{

inner.height += 1;

m_MouseOffsetY = inner.mouseY;

outer.verticalScrollPosition+=2;

trace("Frame Increase Y");

}

}

}


These codes actually resizing inner canvas. While user presses the right-bottom corner of inner canvas and moves the mouse to the offset between inner canvas and outer canvas, it auto-resizes. This method increase width or height of inner canvas by one and change the scroll bar position to the end.

The following is the view of the above code:


















 

2008年8月4日 星期一

Rotation in Flex

Rotation is a easy task in Flex. But it's not easy while developing an integrated editing environment. Recently, I was starting to write an editor for photos. I never learned graphics techniques. So, I don't know the vector graphics.

At the first, I found an equation to calculate a rotated point, which is x = dx + x*cos(a) - y*sin(a) and y = dy + x*sin(a) + y*cos(a). That's great. I use it anywhere to calculate a non-rotated point to rotated point. When calculating a rotated point to non-rotated point, I was going to find an equation too, but nothing found. At that time, I felt some thing wrong. Why I could find an equation but not its reverse equation. So, I open my only one book about graphics, Filthy Rich Client. I found I was totally wrong.

Rotation is always done by matrix of affine transform. We can find the equation I found before is the result of applying a rotated and shifted matrix. So, it doesn't need to use math equation to calculate all the time. All you need is just call: transformPoint. If you want to reverse it, just call invese() and then you can have a reversed matrix. It's simple and easy. 

Transform non-rotated to rotated:

var oMx:Matrix = new Matrix();

oMx.rotate(radian);

oMx.translate(dx, dy);

return oMx.transformPoint(new Point(x, y));


The dx and dy is the about point. The x and y is the relative point to the about point. The result of this method is a absolute point.

Transform rotated to non-rotated:

var oMx:Matrix = new Matrix();

oMx.rotate(radian);

oMx.translate(dx, dy);

oMx.invert();

return oMx.transformPoint(new Point(x, y));


The dx and dy is the about point. The x and y is the absolute point. The result of this method is the relative point to the about point.

The following is a sample applying the above functions (two points in the rect are the rotating point.):