ひこにゃんが Flex アプリケーションにカスタム プリローダーをお勧めする3つの理由

| | コメント(7)

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

コメント(7)

田中 :

申請職務:Flexプログラム要員

忙しい時にお邪魔します。会社は全部にFlexプログラム要員の30名ぐらいにいる。費用は600円/

時間しかないです。ご連絡してください。よろしくお願い致します。会社のホームページ

http://www.busycode.jp
メールアドレス cogoing@gmail.com


どうもありがとうございます、
田中

It really is good to possess the capacity to examine a great high quality article with practical specifics on topics that plenty are interested on. The stage that the information indicated are all first hand on reside experiences even guide a lot more. Proceed performing what you do as we really like readi?-

I thought that was extremeley exciting. Many thanks for your unusual details. I'll maintain using this.

This blog site is great. How did you come up witht he idea?

Gratis fragt, fine priser, fine sko, fin hjemmeside, fin leveringstid og kundeservice. Har kun handlet der to gange, men det var rigtig fint, kun

Cheers! My partner and i used more than an hour searching for this. htaccess document and lastly identified this due to your current post!

A person did not remember to mention to provide Playlist. com, just where it's not actually actually necessary for someone to sign-up and mode almost any melody you wish.

コメントする


画像の中に見える文字を入力してください。

このブログ記事について

このページは、zenithが2007年4月15日 02:29に書いたブログ記事です。

ひとつ前のブログ記事は「Eclipse + Subclipse で JVM terminated」です。

次のブログ記事は「Yahoo!がPHPエンジニアを雇う時に聞く質問、に挑戦」です。

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

Powered by Movable Type 4.12