FlashPlatform SourceIntegration

来自Blueidea
enc0717讨论 | 贡献2011-04-16T22:11:22的版本

(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转至: 导航搜索


資源集成。

嵌入素材

以嵌入圖片為例。最簡單快速的方式。
此方法會增加swf文件的大小。慎。

(此示例您需要在項目根目錄下放置bunny.jpg文件。如果找不到指定文件會返回一個編譯錯誤。)

package {
  import flash.display.Bitmap;
  import flash.display.Shape;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
 
  [SWF(width="128", height="128")]
  /**
   * The <code>Text</code> class.<br/>
   */
  public class Test extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function Test() {
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      const B:Bitmap = new $clazz() as Bitmap; // ${1}
      addChild(B);
    }
    [Embed(source="bunny.jpg")] // ${2}
    private var $clazz:Class;
  } // <- end class ->
}

運行後效果如下。

FlashPlatformEmbedImage.png

如果需要嵌入的是swf中的庫元素。使用如下方式

// ${1} 修改賦值。如果這個元素是Sprite類型
const SP:Sprite = new $clazz() as Sprite;
// ${2} 修改元數據標簽
[Embed(source="assets.swf", symbol="MySymbol")]

外部載入素材

此示例效果和上面一樣,只是改為讀入而非綁定。

package {
  import flash.display.Loader;
  import flash.display.Shape;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.net.URLRequest;
 
  [SWF(width="128", height="128")]
  /**
   * The <code>Text</code> class.<br/>
   */
  public class Test extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function Test() {
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      $loader = new Loader();
      $loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
      $loader.load(new URLRequest("bunny.jpg"));
    }
    private var $loader:Loader;
    //==========================================================================
    //  Event listeners
    //==========================================================================
    private function loader_completeHandler(event:Event):void {
      addChild($loader);
    }
  } // <- end class ->
}

注意Flash對下載有並發連接數限制。 所以請不要使用

for(var i:int = 0; i < n; i++) {
  var loader:Loader = new Loader();
  loader.load(new URLRequest("img" + i + ".jpg"));
}

利用ApplicationDomain整合外部素材

如果您加載的swf並不是要使用這個swf舞台上的元素。 而使用它庫中的元素。 你可以在加載完成後這樣來取得一個名為MySymbol的實例。

const SYMBOL_NAME:String = "MySymbol";
const AD:ApplicationDomain = loader.contentLoaderInfo.applicationDomain;
var instance:*;
if(AD.hasDefinition(SYMBOL_NAME)) {
  var clazz:Class = AD.getDefinition(SYMBOL_NAME);
  instance = new clazz();
} else {
  instance = null;
}

如果你已經有一個ApplicationDomain你希望加載的素材都交給這個域管理。

const AD:ApplicationDomain = new ApplicationDomain();
const CONTEXT:LoaderContext = new LoaderContext(false, AD);
loader.load(request, CONTEXT);

載入其他類型文件

使用URLLoader或URLStream
(此示例您需要在項目根目錄下放置book.txt文件。如果找不到指定文件會返回一個編譯錯誤。)

package {
  import flash.display.Shape;
  import flash.display.Sprite;
  import flash.display.StageAlign;
  import flash.display.StageScaleMode;
  import flash.events.Event;
  import flash.net.URLLoader;
  import flash.net.URLLoaderDataFormat;
  import flash.net.URLRequest;
  import flash.utils.ByteArray;
 
  [SWF(width="128", height="128")]
  /**
   * The <code>Text</code> class.<br/>
   */
  public class Test extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function Test() {
      stage.align = StageAlign.TOP_LEFT;
      stage.scaleMode = StageScaleMode.NO_SCALE;
      $loader = new URLLoader();
      $loader.dataFormat = URLLoaderDataFormat.BINARY;
      $loader.addEventListener(Event.COMPLETE, loader_completeHandler);
      $loader.load(new URLRequest("book.txt"));
    }
    private var $loader:URLLoader;
    //==========================================================================
    //  Event listeners
    //==========================================================================
    private function loader_completeHandler(event:Event):void {
      const DATA:ByteArray = $loader.data;
    }
  } // <- end class ->
}

注意$loader.data在URLLoaderDataFormat常量不同時需要以不同的類型訪問
URLLoaderDataFormat.BINARY >> ByteArray
URLLoaderDataFormat.TEXT >> String
URLLoaderDataFormat.VARIABLES >> Object

RSL運行時共享庫

Runtime shared libraries是種緩存方案。當項目代碼量比較大的時候可以使用。
它就好像你看過的網頁打開會比較快那樣。
一個典型的例子是Flex framework的項目。因為Flex組件會很輕易的上400k。
因此RSL機制將這組件部份的不變代碼分離出去。
然後編譯時將這個swc引入進來。這樣如果用戶本地有這個swc文件。那麼這部份的文件下載時間就節省下來了。

-runtime-shared-libraries xxx.swc

注意Library swc經常發生更新的。不建議使用。因為用戶必須要更新此文件。起不到緩存的初衷。

CSL編譯時共享庫

Compile-time shared libraries方案以一種封閉源代碼的方式在編譯時引用外部定義的組件或者類。
可以從Flash Builder創建Library Project或者使用Flex SDK的compc命令創建。
通常遇到使用有這種庫的項目有以下特性:
1.開發人員的壓箱底私人收藏。
2.類似Alternativa3D這樣的閉源商業組件庫。以此種方式發佈並將swc混效後發佈出去。開發人員可以正常使用但無法得知組件實現細節。
3.團隊開發中封裝好的不需要頻繁改動的組件。
4.程序員潔癖綜合症候群 以及 面向對象綜合症候群。

Flash IDE 結合 Flash Builder

以下示例在Flash IDE中創建一個swf並交給Flash Builder控制。 創建Main.fla 假設舞台上有一只動態文本。

FlashPlatform FF1.png

FlashPlatform FF2.png

這個時候點擊舞台候進入Actionscript settings去掉勾選自動聲名舞台實體。
因為這樣我們可以在代碼控制中加入。不然這個代碼在Flash Builder可是會報錯的。構造函數中的tf訪問在FlashIDE中時可以的。但是FlashBuilder不行。

public var tf:TextField;
FlashPlatform FF3.png

然後指定一個Document class給這個Flash DocClass

package {
  import flash.display.Sprite;
  import flash.text.TextField;
  public class DocClass extends Sprite {
    public function DocClass() {
      trace(tf); // 你會驚奇的發現tf不是空已經被賦值了。
    }
    public var tf:TextField;
  }
}

接下來。你就可以把這個類DocClass.as以及生成的Main.swf拷貝到Flash Builder的src下並在src下新建類Test.as並設置為啓動類。

package {
  import flash.display.Sprite;
  import flash.display.Loader;
  import flash.events.Event;
  import flash.net.URLRequest;
  public class Test extends Sprite {
    public function Test() {
      $loader = new Loader();
      $loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
      $loader.load(new URLRequest("Main.swf");
    }
    private var $loader:Loader;
    private function completeHandler(event:Event):void {
      const OBJ:DocClass = $loader.content as DocClass;
      trace(OBJ.tf); // 很驚奇的發現tf也被複賦值了。
      tf.text = "Flash Builder 也有春天。";
      addChild(OBJ);
    }
  }
}

複製一個類。這樣不美觀。而且要改同時要改兩份。這樣對於有潔癖症的開發人員來說很吐血。所以我們有一種方法來解決這個問題。
見 逻辑和素材整合案 之 運行時綁定。

逻辑和素材整合案 之 編譯時綁定

Compile-time Binding

最常見的資源素材整合方案。
Flash IDE中給庫中的設計好的圖形原件(Symbol)設定Linkage
會得到一個對話框

FlashPlatform Linkage.png

如果不是Bitmap我建議一律使用Sprite來取代MovieClip。除非這個原件超過1frame
這樣我們就可以在外部的程序中控制設計師設計好的素材了。

逻辑和素材整合案 之 運行時綁定

Runtime Binding 洗腦流。
此方法是軒辰原創。非常殘暴和變態的資源整合方案。
貓糧同學曾經也提及此種做法。
它的優點是可以完全不在Fla編譯時引用外部任何類文件。因此編譯速度奇快無比。

 缺點:
 如果顯示構件發生更變。Fla還是需要重新編譯並且外部控制類也需要改變。你會在以下內容種得知原因。
 
 優點:
 FlashPlatform Numbers1.jpg多個swf使用同一個LinkageClass可以大幅度減肥。
 FlashPlatform Numbers2.jpg擁有超高速的Fla編譯速度。這個編譯提速甚至只用到以前1%的時間。大項目更是成指數倍縮減時間。
 FlashPlatform Numbers3.jpg你不再需要使用ApplicationDomain.getDefinition。直接new對象就可以了。


我們經常在Linkage的Class字段填一個標識來創建元素
FlashPlatform LinkageClass.png

 比如填入MySymbol然後在Fla中通過new MySymbol()創建元素。
 這個寫法直到Flash CS5都是支持的。
 這個寫法Flash IDE會返回一個警告。但完全不影響使用
 
 這個警告產生的原因是不管在Class字段填入的是甚麼。
 實際上Flash IDE都會在編譯的時候自動生成一個空類比如以上情況編譯出來的swf會包含這個類
package {
  import flash.display.Sprite;
  public class MySymbol extends Sprite {}
}


細心的觀眾會發現Flash CS3中如果這麼寫

com.Symbol

會返回一個錯誤操作無法執行。 但是有趣的是在Flash CS5中這麼寫只返回一個警告。卻不會和以前一樣返回一個錯誤而無法操作。
圖中的對話框點擊勾勾後彈出得說明文件不存在但可以成功設置的證據。
這可幫了大忙了
於是現在我可以寫com.MySymbol就算外部沒有這個類也可以通過。(PS:說不定Adobe的哪個哥們也是這麼幹所以加入這個支持)
FlashPlatform LinkageClassWithPackage.png
OK 成功。
FlashPlatform LinkageClassWithPackageResult.png

實際上發生了甚麼。Flash IDE都會在編譯的時候自動生成一個空類com.MySymbol
由於指定的Linkage class實際並不存在。因此編譯速度奇快。要問有多快。比物價上漲更快。

package com {
  import flash.display.Sprite;
  public class MySymbol extends Sprite {}
}

那麼如果您的項目種有非常多的界面需要設定Linkage並且代碼量不小。
那麼如果因為修改到其中一個字符串而重新編譯。這就非常浪費時間了。
我看到很多Flash IDE寫的項目都是在頻繁Ctrl + Enter時候浪費了大量的時間。

如果兩個一模一樣的類被加載進來了。按先到先得原則,FlashPlayer會保留先加載的類而丟棄後加載的同名類。
於是我要說到一個概念, "Runtime Binding"

 大多數的OOP都存在一個概念。"類對象"。
 比如在AS中的類對象var classObj:Class
 英文原文稱為"There is a class named class."
 "這裡有一個類,這個類的類名是類"
 乍一看很繞口。還是和往常一樣。我畫個圖來表示這句話的意思。
FlashPlatform ThereIsAClassNamedClass.jpg

一個類定義由唯一的package名+class名標識。內存中對於一個類定義有且只有一份。
現在Flash IDE生成的com.MySymbol是空的邏輯。
根據只有一份類定義的原理。如果我們假設內存中已經有一個com.MySymbol類。讓它來代替Flash IDE生成的com.MySymbol
那麼AVM在運行將Flash IDE編譯的swf時就會發現它的com.MySymbol無法在內存中放置。

FlashPlatform AVMClassExist.jpg


根據這個假設。接下來我們創建一個例子。打開Flash IDE在庫中創建一個動態文本。Linkage成com.MySymbol然後用別的swf加載它並加以控制。

FlashPlatform LinageTest2.png

動態文本實例名為tf

FlashPlatform LinageTest1.png

去掉自動聲名舞台實體

如果不明白為甚麼要去掉這個勾。參見Flash IDE 結合 Flash Builder一節
FlashPlatform LinageTest3.png

我在舞台上也放置了一個實例,不給實例名稱

FlashPlatform LinageTest4.png

這個fla編譯出來的swf會包含這樣一個類。

package com {
  import flash.display.Sprite;
  import flash.text.TextFeild;
  public class MySymbol extends Sprite {
    public var tf:TextField;
  }
}

這樣我們就得到了一個swf。接下來我們在完全不改動它的情況下來修改它的內部邏輯。

lashPlatform LinageTest5.png
單獨運行test.swf運行結果
lashPlatform LinageTest6.png

現在我們創建一個Shell來加載test.swf
拷貝test.swf到Flash Builder > ActionScript3 Project 並新建com.MySymbol類。項目看起來時這樣的。
lashPlatform LinageTest7.png

編輯com.MySymbol類到以下所示。

package com {
  import flash.display.Sprite;
  import flash.text.TextField;
 
  public class MySymbol extends Sprite {
    /** Constructor */
    public function MySymbol() {
      trace("Flash IDE的同名類已被我幹死, 甚麼?tf不是null");
      tf.text = "Flash IDE的同名類已被我幹死, 甚麼?tf不是null";
    }
    public var tf:TextField;
  } // <- end class ->
}

編輯RuntimeBinding.as類到以下所示。

package {
  import com.MySymbol;
 
  import flash.display.Loader;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.net.URLRequest;
 
  public class RuntimeBinding extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function RuntimeBinding() {
      // ${1}
      $loader = new Loader();
      $loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
      $loader.load(new URLRequest("test.swf"));
    }
    private var $loader:Loader;
    //==========================================================================
    //  Event listeners
    //==========================================================================
    private function completeHandler(event:Event):void {
      // ${2}
    }
  } // <- end class ->
}

運行!但是你會發現MySymbol類構造函數中的任何事情都沒有執行。
因為自定義的com.MySymbol類是再test.swf載入之後加入。因為內存中已經存在這個類定義。自定義的com.MySymbol就被幹掉了。
所以在${1}處加入代碼。這個寫法用於載入這個類定義。import是導入但不是載入類定義。因此要這麼寫。

com.MySymbol;

會得到以下輸出

 Flash IDE的同名類已被我幹死, 甚麼?tf不是null

這個輸出是由test.swf舞台上的MySymbol實例構造時輸出。我們已經把自定義邏輯注入到任何com.MySymbol對象的實例化過程。

然後顯示被改變的文本構造
在${2}處加入代碼

addChild(new MySymbol());

會得到以下效果。
lashPlatform LinageTest8.png
如果您得到的時亂碼。在test.swf發佈前對tf做如下設置
lashPlatform LinageTest9.png


如果這個邏輯還不夠簡單。改寫RuntimeBinding.as類到以下所示,您也可以得到以上運行效果。

package {
  import com.MySymbol;
  import flash.display.Loader;
  import flash.display.Sprite;
  import flash.net.URLRequest;
 
  public class RuntimeBinding extends Sprite {
    /** Constructor */
    public function RuntimeBinding() {
      com.MySymbol;
      const LD:Loader = new Loader();
      addChild(LD);
      LD.load(new URLRequest("test.swf"));
    }
  } // <- end class ->
}

最後要說的東西有關於類中的顯示屬性時如何自動綁定的。您可能會疑問為甚麼 tf 在代碼中並沒有賦值但在構造函數中並不是期待的null。

lashPlatform LinageTest10.png

lashPlatform LinageTest11.png

以上文字出於個人見解。如果紕漏處還請不吝賜教。筆者信箱是:purple.starpulse@gmail.com