Adobe Air & Adobe Flex & ActionScript & Mobile Dev & HTML5 & RIA & User Experience
Flex4.5出来之后最有影响力的莫过于移动开发方面的发展,目前移动端开发通常指的是最有影响力的Android和iOS平台。如果要用Flash/Flex开发移动端程序,目前开发iOS必须是AS工程,而Android可以为Flex工程和AS工程,用Flex开发iOS程序会在稍后的版本推出。
Android程序一般用Java开发,而iOS一般用Objective-C开发,而用同一种语言开发完之后一次性同时部署再两大系统的也只有Actionscript语言。这也给了Flash/Flex者参与移动互联网开发大潮中得一次机会。
Flash打包的iOS程序其实是完全转换成Objective-C的ByteCode,而这样,撇开本身代码质量而言,转换的算法是非常关键的。
Android上部署的app还是需要Android 2.2以上和Adobe AIR 2.6以上的支持,也就是说,要运行Flash打包的Android程序,移动端必须要装有Adobe AIR,如果没有,它会自动下载AIR runtime。目前Flex4.5之后,对移动端优化的控件越来越多,但是真正符合项目需求最优化的还是需要手动自己写。而且这样打包出来的程序毕竟还是要运行在AIR runtime上面,比起直接用Java写得程序而言,性能方面肯定是用Java写得好,特别是桌面版本AIR在性能方面一直不敢令人信服的背景下。
1.Adobe继续对移动端的优化,在Android平台上,最好做到能像iOS平台一样没有AIR runtime的需求。
2.Adobe对SDK的进一步更新和优化(Adobe势必会投入很大力量,因为移动互联网与人的生活越来越紧密联系在一起)。
2.移动端硬件方面进一步提升(每年都产生很大变化)。
因此综合所述,Flash/Flex 移动端开发的优势还是在于一次开发同时部署,而且对企业还是客户来讲,都省下了一笔不小的成本。只要Adobe公司能继续努力,Flahs/Flex在移动互联网的前景还是一片光明的。
我也将继续写一些Flash/Flex移动端开发的文章,敬请关注,谢谢。
昨天 Erin 問了一個關於 Flex 4 State Name 問題
Flex 3 State Name 是可以不要寫死,Binding 到一個常數的
可是 Flex 4 卻不能這樣作,因為 Flex 4 多了很多 Inline State 增強語法
這些功能主要都是在編譯期完成的
所以 Flex 4 Compiler 禁止 State Name Binding 功能
Flex 4 文件上的說明 Using Flex 4.5 – Creating view states
Note: View state definitions are processed at compile time. Therefore, you cannot use data binding to set the State.name property because data binding occurs at run time.
測試之後發現,雖然不能用 Binding Expression
但是還是能夠用陳述式修改 State Name 的
只是需要注意的是 currentState 的 name 不能直接變更
否則會出現 Error
須要先建立一個空的 State,並切換 currentState 到這個上
等到所有 State Name 都變更完,再切回來就好了
以下是一次變更所有 State Name 的測試程式:
2011/06/05 更新
之前的寫法有點錯誤
然後拆成外部獨立 StateUtil Class
package { import mx.core.UIComponent; import mx.states.State; public class StateUtil { public static function setStates(component:UIComponent, stateNames:Array, currentState:String = ""):void { var states:Array = component.states; if (states.length != stateNames.length) { throw new Error("Incorrect Number of State Names Parameter"); return; } var tmpState:State = new State(); tmpState.name = "_tempState_"; states.push(tmpState); component.currentState = "_tempState_"; for (var i:int = 0; i < stateNames.length; ++i) { states[i].name = stateNames[i]; } component.currentState = currentState; states.pop(); } } }
Flex 4 MXML 測試程式:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" fontSize="14"> <fx:Script> <![CDATA[ public function randomStateNames():void{ var stateName1:String = "s" + int(Math.random() * 10000); var stateName2:String = "s" + int(Math.random() * 10000); StateUtil.setStates(this, [stateName1, stateName2], stateName2); } ]]> </fx:Script> <s:states> <s:State id="st1" name="X01"/> <s:State id="st2" name="X02"/> </s:states> <s:layout> <s:VerticalLayout verticalAlign="middle" horizontalAlign="center"/> </s:layout> <s:Label text="Current State Name: {currentState}"/> <s:Button height="40" label.X01="Switch to State 02" label.X02="Switch to State 01" click.X01="currentState = st2.name;" click.X02="currentState = st1.name;"/> <s:Button height="40" label="Randomize State Names" click="randomStateNames();"/> </s:Application>
如此一來,就算不能用 Binding Expression
也能夠將 State Name 拆到外部類別的常數值了
而且任何時候都能夠修改 State Names,Inline State 功能還能正常運作
去年公司的設計作 Flash Banner 時,遇到一個奇怪的問題
大量使用 Filter, Blend Mode, Alpha, Mask 之後
(這是不良的習慣,請勿仿效)
最終渲染的結果圖片上會出現奇怪的斑點
可明明用的都是 Hard Light,圖片也都是高亮色系
怎麼反而出現暗色斑點呢?
經過反覆測試,拆解之後
發現主要會出現問題的是 Hard Light, Overlay 這兩種混色模式
當一個 MovieClip 套用這兩種混色模式,且後面的背景是純白色時
前景再疊上一層帶有 Alpha 50% 左右的色塊
就很容易產生問題
以下則是使用 Flex 4 MXML 作更進一步的測試程式:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Library> <fx:Definition name="HRule"> <s:Line width="100%"> <s:stroke><s:SolidColorStroke color="#000000" /></s:stroke> </s:Line> </fx:Definition> <fx:Definition name="Border"> <s:Rect> <s:stroke> <s:SolidColorStroke color="#000000" alpha="0.7" weight="4" joints="bevel" /> </s:stroke> </s:Rect> </fx:Definition> </fx:Library> <fx:Metadata> [SWF(width="500", height="400")] </fx:Metadata> <fx:Declarations> <s:ArrayList id="blendModes"> {[BlendMode.NORMAL, BlendMode.HARDLIGHT, BlendMode.OVERLAY, BlendMode.ADD, BlendMode.ALPHA, BlendMode.DARKEN, BlendMode.DIFFERENCE, BlendMode.ERASE, BlendMode.INVERT, BlendMode.LAYER, BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.SCREEN, BlendMode.SUBTRACT]} </s:ArrayList> <s:ArrayList id="opaqueBgColors"> {[{label: "Null", data: null}, {label: "#000000", data: 0x000000}, {label: "#808080", data: 0x808080}, {label: "#FFFFFF", data: 0xFFFFFF}, {label: "#FF0000", data: 0xFF0000}, {label: "#00FF00", data: 0x00FF00}, {label: "#0000FF", data: 0x0000FF}]} </s:ArrayList> </fx:Declarations> <s:layout> <s:HorizontalLayout verticalAlign="middle" horizontalAlign="center" gap="40" /> </s:layout> <!-- Graphics Group --> <s:Group id="graphicsGroup" width="200" height="250" alpha="{alphaSlider.value}" blendMode="normal"> <s:Group id="underGroup" top="24" bottom="44" left="4" right="4" opaqueBackground="{underOpaqueBgList.selectedItem.data}" blendMode="{underBlendModeList.selectedItem ? underBlendModeList.selectedItem : 'normal'}"> <s:Rect top="0" bottom="0" left="0" right="0"> <s:fill> <s:LinearGradient rotation="90"> <s:GradientEntry color="#FF0000" ratio="0" /> <s:GradientEntry color="#FFFF00" ratio=".15" /> <s:GradientEntry color="#00FF00" ratio=".3" /> <s:GradientEntry color="#00FFFF" ratio=".45" /> <s:GradientEntry color="#0000FF" ratio=".60" /> <s:GradientEntry color="#FF00FF" ratio=".75" /> <s:GradientEntry color="#FF0000" ratio=".9" /> <s:GradientEntry color="#FFFFFF" ratio=".90001" /> <s:GradientEntry color="#FFFFFF" ratio=".95" /> <s:GradientEntry color="#000000" ratio=".95001" /> <s:GradientEntry color="#000000" ratio="1" /> </s:LinearGradient> </s:fill> </s:Rect> </s:Group> <fx:Border top="20" bottom="40" left="0" right="0" /> <s:Label top="30" left="-20" text="Under Graphic (Colour Gradient)" rotation="-90" /> <s:Group top="4" bottom="4" left="24" right="44" cacheAsBitmap="{cacheAsBitmapCk.selected}"> <s:Rect top="0" bottom="0" left="0" right="0"> <s:fill> <s:LinearGradient> <s:GradientEntry color="#000000" ratio="0" alpha="1" /> <s:GradientEntry color="#000000" ratio="1" alpha="0" /> </s:LinearGradient> </s:fill> </s:Rect> </s:Group> <fx:Border top="0" bottom="0" left="20" right="40" /> <s:Label top="-20" left="0" text="Over Graphic (#000000, 0-100% Alpha)" /> </s:Group> <!-- Graphic Control Group --> <s:VGroup gap="10"> <s:Label text="Under Graphic Blend Mode:" /> <s:List id="underBlendModeList" height="100" dataProvider="{blendModes}" requireSelection="true" /> <s:Label text="Under Graphic Opaque Background:" /> <s:List id="underOpaqueBgList" height="100" dataProvider="{opaqueBgColors}" requireSelection="true" /> <fx:HRule width="100%" /> <s:CheckBox id="cacheAsBitmapCk" label="Over Graphic Cache As Bitmap" /> <fx:HRule width="100%" /> <s:Label text="Graphic Group Alpha: {alphaSlider.value}" /> <s:HSlider id="alphaSlider" width="80%" value="1" stepSize="0.01" minimum="0" maximum="1" /> </s:VGroup> </s:Application>
前景是黑色,Alpha 0-100% 漸層
中景是各種顏色漸層色塊,配合各種 Blend Mode 測試
背景是純白色
Online Demo:
Hard Light 模式結果:
Overlay 模式結果:
可以很明顯的發現混色結果怪怪的,就連黑白色混出來的結果也是
反覆測試之後發現有幾種方式可以繞過這個問題
以上的問題似乎會發生在 Flash Player 9, 10 所有版本上
包含目前最新版本 10.3.181.22,OS Win7
而更早期的 Flash Player 8 卻是正常的
使用 Flash Builder (Eclipse) 時,偶爾跑出一個幽靈 Perspective 設定
因為 Flash Builder 預設安裝並沒有 SVN 等額外的 Plugin
倘若直接打開過有用過 SVN 的 Workspace
會出現這樣的一個幽靈 SVN Perspective
之後就算裝了 SVN Plugin,那個幽靈 Perspective 仍然會存在而且沒有辦法使用
這問題我已經遇到好幾次了,所以打算徹底想辦法解決它並且作一個記錄
可是切到另一個乾淨的 Workspace 它又不會出現
很明顯是 Workspace 內的設定出了問題
全文搜索 Workspace 下的 .metadata 目錄
發現相關資訊是記錄在這個檔案內
Workspace\.metadata\.plugins\org.eclipse.core.runtime\.settings\org.eclipse.ui.workbench.prefs
這個檔案裡面似乎沒有存放什麼很重要的設定
所以最簡單的解決方式就是直接把它刪掉,只是字體樣式那些要重設一下
假如想保留設定,再往下檢查設定檔
可以發現裡面有兩個可疑的屬性
...
perspectives=<SVN_Repository_Exploring>
...
<SVN_Repository_Exploring>_persp=<?xml version\="1.0" encoding\="UTF-8"?>\r\n<perspective editorAreaTrimState\="2" editorAreaVisible\="..........
刪掉這兩個屬性,重啟 Flash Builder (Eclipse) 就好了
重啟之前還是要把相關的 Perspective 都關掉
很多人为了将 Flex 生成的 swf 加到 Flash 生成的 swf 中,为了使其透明,网络中通常的做法是
if(mx_internal::border && mx_internal::border.visible) { mx_internal::border.visible = false; mx_internal::border.width = 0; mx_internal::border.height = 0; } if(systemManager && systemManager.getChildByName("mouseCatcher") && systemManager.getChildByName("mouseCatcher").visible) { systemManager.getChildByName("mouseCatcher").visible = true; }
设置 mx_internal::border 的作用为使 Flex SWF 透明,
而设置 mouseCatcher 的作用为能使 Flex SWF 背景后的 Flash SWF 能够响应鼠标事件。
但如果单单这么做,问题就会发生了。
mouseCatcher 是 SystemManager 所谓的第0个 child,也就是最下面的那一个,它跟舞台一样大,并且 mouseCatcher.graphics 被 0×000000 所填充,但 alpha 为0,即透明。
SystemManager 是所谓的顶级窗口,它能查看所有并非显示捕获的键盘和鼠标事件,而其中鼠标事件的控制很大一部分要归功于这个 mouseCatcher ,它是为 SystemManager 控制 Flex 内全局 MouseEvent 的一个 Sprite,如果单纯的设置 mouseCatcher 的 visable 为 false,那么 Flex SWF 的 DragManager,TitleWindow 可拖动的等等这些事件将会失效。
全局的鼠标事件可以重新监听 stage,或者在 Flash 的 swf 内新建一个相同的 mouseCatcher 来解决。
而 DragManager 可以为大家提供现成的方法(继承 Application 重写):
//... systemManager.getSandboxRoot().addEventListener(InterManagerRequest.DRAG_MANAGER_REQUEST, handleDragManagerRequest); private function handleDragManagerRequest(event:InterManagerRequest):void { var isDragging:Boolean = event.value; setMouseCatcher(isDragging); } public function setMouseCatcher(value:Boolean):void { systemManager.getChildByName("mouseCatcher").visible = value; } //...