MXML: 2007年4月アーカイブ

IBM developerWorks ぽく

いつもの画面に飽きていませんか?Flex 2 で用意されているデフォルトのローディング画面をちょっと変えたい時は、mx.preloaders.IPreloaderDsiplay インターフェイスを実装する flash.display.Sprite クラスのサブクラスを作成します。ここでは画像を使った簡単なプリローダーを作ってみます。

カスタム プリローダのサンプル

完成目標

ダウンロードの進捗状況の表現には、開始時はグレースケールの画像を表示し、徐々にフルカラー化されていく方法で再現することにします。大事な点は、著作権を回避しつつ視覚に訴える事です。

image.png

用意するもの

  • xxnyann.png
    柿の種が降ってきてあわててミドリゾウリムシを持って逃げる防空頭巾姿の白い生き物の画像ファイル。グレースケールとフルカラーの二つを用意します。彦根市のキャラクターに激似ですが、それは柿の種とミドリムシのコラボが産んだ奇跡です。
  • ダミーのファイル。実際にダウンロードした時に、プリローダー画面を確認できるようにするだけですので、ファイルサイズが大きければ何でも良いです。

実装

普通にプロジェクトを作成します。ディレクトリ構成は次のようにしました。

project.png

base.png がグレースケール画像、overlay.png がフルカラー画像、dummy.mp3 がダミー ファイルとなっています。

source/preloader/OverlayPreloaderDisplay.as
package preloader
{
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.ProgressEvent;
  import flash.events.TimerEvent;
  import flash.geom.Point;
  import flash.geom.Rectangle;
  import flash.utils.Timer;
  import flash.utils.getTimer;
  
  import mx.preloaders.IPreloaderDisplay;
  import mx.preloaders.Preloader;

  /**
   * ダウンロードの進行状況を表示します。
   */
  public class OverlayPreloaderDisplay extends Sprite implements IPreloaderDisplay
  {
    /**
     * 表示領域の横幅。
     */
    public static const CANVAS_WIDTH:Number = 300;

    /**
     * 表示領域の縦幅。
     */
    public static const CANVAS_HEIGHT:Number = 200;
    
    /**
     * フェードアウト時間(ms)。
     */
    public static const CLOSE_EFFECT_DELAY:Number = 400;
    
    // 背景画像。
    [Embed(source="../../assets/base.png")]
    private var _baseImageClass:Class;
    private var _baseBitmap:Bitmap;

    // 上書き画像。
    [Embed(source="../../assets/overlay.png")]
    private var _overlayImageClass:Class;
    private var _overlayBitmap:Bitmap;
    
    private var _canvas:Bitmap;
    
    /**
     * OverlayPreloaderDisplay が Preloader の子として追加されると、Preloader によって呼び出されます。
     * これが、ダウンロードプログレスバー設定の開始点となります。
     */
    public function initialize():void
    {
      this._baseBitmap = new this._baseImageClass();
      this._overlayBitmap = new this._overlayImageClass();

      var canvasBitmapData:BitmapData = new BitmapData(CANVAS_WIDTH, CANVAS_HEIGHT);
      this._canvas = new Bitmap(canvasBitmapData);
      this.addChild(this._canvas);
    }
    
    /**
     * 表示画面を更新します。
     */
    protected function updateDisplay():void
    {
      var canvasBitmapData:BitmapData = this._canvas.bitmapData;

      // 進捗率
      var progress:Number = this.getProgress();
      var bounds:Number = Math.floor(canvasBitmapData.height * (1 - progress));

      // 背景描画
      var baseRect:Rectangle = new Rectangle();
      baseRect.left = 0;
      baseRect.top = 0;
      baseRect.right = this._baseBitmap.bitmapData.width;
      baseRect.height = bounds;

      var basePoint:Point = new Point(0, 0);

      canvasBitmapData.copyPixels(this._baseBitmap.bitmapData, baseRect, basePoint);

      // 上書き描画
      var olRect:Rectangle = new Rectangle();
      olRect.left = 0;
      olRect.top = bounds;
      olRect.right = this._baseBitmap.bitmapData.width;
      olRect.height = this._baseBitmap.bitmapData.height - bounds;

      var olPoint:Point = new Point(0, olRect.top);
      
      canvasBitmapData.copyPixels(this._overlayBitmap.bitmapData, olRect, olPoint);
      
      // 位置調整
      this._canvas.x = (this._stageWidth - this._canvas.width) / 2;
      this._canvas.y = (this._stageHeight - this._canvas.height) / 2;
    }
    
    private var _loadCompletedSize:uint = 0;
    private var _loadTotalSize:uint = 0;
    
    /**
     * 現在のダウンロード進捗状況の割合を数値で取得します。
     * 
     * @return 進捗開始持は 0、進捗が進むつれ最大 1 まで増えます。
     */
    private function getProgress():Number
    {
      if (isNaN(this._loadTotalSize) || this._loadTotalSize == 0)
      {
        return 0;
      }
      else
      {
        return this._loadCompletedSize / this._loadTotalSize;
      }
    }
    
    private var _closeEffectTimer:Timer;
    private var _closeStartTime:int;
    
    /**
     * 進捗画面を閉じる準備をします。
     */
    private function beginClose():void
    {
      this._closeStartTime = flash.utils.getTimer();
      this._closeEffectTimer = new Timer(30);
      this._closeEffectTimer.addEventListener(TimerEvent.TIMER, this.closeEffectTimer_timer);
      this._closeEffectTimer.start();
    }
    
    /**
     * 進捗画面を閉じます。
     */
    private function endClose():void
    {
      // 進捗画面を消すよう Preloader にお知らせ。
      this.dispatchEvent(new Event(Event.COMPLETE));
    }
    
    /**
     * 進捗画面を閉じるエフェクトを更新するたびに呼び出されます。
     */
    private function closeEffectTimer_timer(event:TimerEvent):void
    {
      var elapsed:int = flash.utils.getTimer() - this._closeStartTime;
      var alpha:Number = 1 - Math.min(1, elapsed / CLOSE_EFFECT_DELAY);
      
      this.alpha = alpha;

      if (alpha == 0)
      {
        this._closeEffectTimer.stop();
        this.endClose();
      }
    }
    
    /**
     * プリローダの進捗が進むたびに呼び出されます。
     */
    protected function preloader_progressHandler(event:ProgressEvent):void
    {
      this._loadCompletedSize = event.bytesLoaded;
      this._loadTotalSize = event.bytesTotal;
      
      this.updateDisplay();
    }
    
    /**
     * プリローダの進捗が完了した時に呼び出されます。
     */
    protected function preloader_completeHandler(event:Event):void
    {
      this.beginClose();
    }
    
    // 以下、下駄&雪駄
    
    private var _preloader:Preloader;
    
    public function set preloader(value:Sprite):void
    {
      this._preloader = Preloader(value);
      
      this._preloader.addEventListener(ProgressEvent.PROGRESS, this.preloader_progressHandler);
      this._preloader.addEventListener(Event.COMPLETE, this.preloader_completeHandler);
    }
    
    private var _stageHeight:Number;
    
    public function get stageHeight():Number
    {
      return this._stageHeight;
    }
    
    public function set stageHeight(value:Number):void
    {
      this._stageHeight = value;
    }
    
    private var _stageWidth:Number;
    
    public function get stageWidth():Number
    {
      return this._stageWidth;
    }
    
    public function set stageWidth(value:Number):void
    {
      this._stageWidth = value;
    }
    
    public function get backgroundImage():Object
    {
      // 手抜き
      return null;
    }
    
    public function set backgroundImage(value:Object):void
    {
      // 手抜き
    }
    
    public function get backgroundSize():String
    {
      // 手抜き
      return null;
    }
    
    public function set backgroundSize(value:String):void
    {
      // 手抜き
    }
    
    public function get backgroundAlpha():Number
    {
      // 手抜き
      return 0;
    }
    
    public function set backgroundAlpha(value:Number):void
    {
      // 手抜き
    }
    
    public function get backgroundColor():uint
    {
      // 手抜き
      return 0;
    }
    
    public function set backgroundColor(value:uint):void
    {
      // 手抜き
    }
  }
}
source/MyCustomPreloader.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preloader="preloader.OverlayPreloaderDisplay" layout="vertical" viewSourceURL="srcview/index.html">
  <mx:Script>
    <![CDATA[
      [Embed(source="../assets/dummy.mp3")]
      private var sound:Class;
    ]]>
  </mx:Script>

  <mx:Style>
    Application
    {
      vertical-align: middle;
      background-gradient-colors: #ffffff, #ffffff;
    }
    
    Label
    {
      font-size: 40px;
    }
  </mx:Style>

  <mx:Label text="COMPLETED"/>
</mx:Application>

サンプル

出来上がった物はこちらです。サンプル

まとめ

  • カスタム プログレスバー クラスを作る。
    class MyPreloaderDisplay extends flash.display.Sprite implements mx.preloaders.IPreloaderDisplay
  • 手抜きをしたかったら mx.preloaders.DownloadProgressBar を継承(ActionScript 3 ならではの手軽さ)。
  • アプリケーション ルートの MXML で使用するカスタム プログレスバー クラスを指定。
    <mx:Application preloader="クラスパス">
  • ロードが終わったらカスタム プログレスバーが disptchEvent(new Event(Event.COMPLETE)) を発行する。そうでないといつまでもローディング画面が消えない。
  • Preloader が発行するイベントの詳細がないような…。以下発生するイベント。
    • Event.COMPLETE
    • FlexEvent.INIT_COMPLETE
    • FlexEvent.INIT_PROGRESS
    • FlexEvent.PRELOADER_DONE
    • ProgressEvent.PROGRESS
    • RSLEvent.RSL_COMPLETE
    • RSLEvent.RSL_ERROR
    • RSLEvent.RSL_PROGRESS

このアーカイブについて

このページには、2007年4月以降に書かれたブログ記事のうちMXMLカテゴリに属しているものが含まれています。

前のアーカイブはMXML: 2006年10月です。

次のアーカイブはMXML: 2008年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

MXML: 2007年4月: 月別アーカイブ

Powered by Movable Type 4.12