2013年2月5日火曜日

Feathers Example ボタンスキンの変更

このサンプルはFeathersのサイト
「Extending Feathers Themes(リンク先へ)
を参考にしています。

スキンの状態を赤青緑の矩形に変更してみます。


スキン用の矩形




FeathersThemeライブラリプロジェクトにスキン用の矩形と
カスタムテーマExtendMetalWorksThemeを追加します。

ExtendMetalWorksTheme.as
package feathers.themes
{
 import feathers.controls.Button;
 import feathers.themes.MetalWorksMobileTheme;

 import starling.display.DisplayObjectContainer;
 import starling.display.Image;
 import starling.textures.Texture;

 public class ExtendMetalWorksTheme extends MetalWorksMobileTheme
 {
  public static const ALTERNATE_NAME_MY_CUSTOM_BUTTON:String = "my-custom-button";

  [Embed(source="./assets/buttons/blue.png")]
  public var blueClass:Class;        

  [Embed(source="./assets/buttons/green.png")]
  public var greenClass:Class;        

  [Embed(source="./assets/buttons/red.png")]
  public var redClass:Class;        

  public function ExtendMetalWorksTheme(root:DisplayObjectContainer, scaleToDPI:Boolean=true)
  {
   super(root, scaleToDPI);
  }

  override protected function initialize():void
  {
   super.initialize();
   this.setInitializerForClass(Button, myCustomButtonInitializer, ALTERNATE_NAME_MY_CUSTOM_BUTTON);
  }        

  private function myCustomButtonInitializer(button:Button):void
  {
   button.defaultSkin = new Image( Texture.fromBitmap(new blueClass()) );
   button.downSkin = new Image( Texture.fromBitmap(new greenClass()) );
   button.hoverSkin = new Image( Texture.fromBitmap(new redClass()) );

   button.defaultLabelProperties.textFormat = this.smallUILightTextFormat;
   button.disabledLabelProperties.textFormat = this.smallUIDisabledTextFormat;
   button.selectedDisabledLabelProperties.textFormat = this.smallUIDisabledTextFormat;
  }
 }
}
FeathersThemeライブラリプロジェクトをビルドします。

FeathersExampleプロジェクトのButtonExample.as内でカスタムテーマの変更をします。
ButtonExample.as
protected var theme:ExtendMetalWorksTheme;
   themeIndex = 2;
   switch(themeIndex)
   {
    case 0:
     //this.theme = new MetalWorksMobileTheme(this.stage);
     break;
    case 1:
     //this.theme = new AeonDesktopTheme(this.stage);
     break;
    case 2:
     this.theme = new ExtendMetalWorksTheme(this.stage);
     break;
   }

Feathers Example Buttonについて

Buttonサンプルについて
このサンプルはFeathersのサイト
「How to use the Feathers Button component(リンク先へ)
を参考にしています。
FeathersExampleのソースコード(リンク先へ)

FeathersExampleを実行すると以下の画面になります。
テーマの設定
Buttonの処理を行う前にテーマを設定します。
このサンプルでは、FeathersThemeライブラリ(リンク先へ) を利用しています。
ButtonExample.as
    case 0:
     this.theme = new MetalWorksMobileTheme(this.stage);
     break;
テーマはMetalWorksMobileThemeを使用します。

テーマの変更
MetalWorksMobileThemeからAeonDesktopThemeに切り替えてみます。

ButtonExample.as
protected var theme:AeonDesktopTheme;
   themeIndex = 1;
   switch(themeIndex)
   {
    case 0:
     //this.theme = new MetalWorksMobileTheme(this.stage);
     break;
    case 1:
     this.theme = new AeonDesktopTheme(this.stage);
     break;

Buttonの表示と操作の流れ

ButtonExample.as
   this.button = new Button();
   this.button.label = "クリック";
   this.button.nameList.add(ExtendMetalWorksTheme.ALTERNATE_NAME_MY_CUSTOM_BUTTON);
   this.button.defaultIcon = new Image(Texture.fromBitmapData(icon.bitmapData));
   this.button.iconPosition = Button.ICON_POSITION_TOP;
   this.button.gap = 0;
   this.button.horizontalAlign = Button.HORIZONTAL_ALIGN_LEFT;
   this.button.verticalAlign = Button.VERTICAL_ALIGN_MIDDLE;
   this.button.width = 200;
   this.button.height = 80;

   this.button.labelOffsetX = this.button.iconOffsetX + icon.width;
   this.button.labelOffsetY = -(this.button.height/2);

   this.button.addEventListener(Event.TRIGGERED, button_triggeredHandler);
   this.button.addEventListener(Event.CHANGE, button_changeHandler);
   this.addChild(this.button);
  • インスタンスの作成
  • labelプロパティの設定
  • レイアウト、大きさの設定
  • タッチイベントの設定
  • 画面に対してaddChildを行う

タッチイベントハンドラーの作成 ButtonExample.as
  protected function button_triggeredHandler(event:Event):void
  {
   const label:Label = new Label();
   label.text = "Hi, I'm Feathers!\nHave a nice day.";
   Callout.show(label, this.button);
  }

labelOffsetについて
labelOffsetを利用しない場合は以下のような表示になります。
アイコンとラベルを水平に表示させる為、labelOffsetで調整をしています。

ButtonExample.as
   this.button.labelOffsetX = this.button.iconOffsetX + icon.width;
   this.button.labelOffsetY = -(this.button.height/2);

Feathers Example プロジェクトの作成3


プロジェクトの追加に上記3つのFlexライブラリプロジェクトを選択します。

icon.png
プロジェクトファイルにicon.pngを追加します。


FeathersExample-app.xml
        

        direct
renderMode設定をdirectに設定します。

ビルドを行います。

Feathers Example プロジェクトの作成2

ListExample.as
package
{
 import feathers.controls.List;
 import feathers.data.ListCollection;
 import feathers.themes.MetalWorksMobileTheme;

 import starling.display.Sprite;
 import starling.events.Event;

 public class ListExample extends Sprite
 {
  public function ListExample()
  {
   this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
  }

  protected var theme:MetalWorksMobileTheme;

  protected var list:List;

  protected function addedToStageHandler(event:Event):void
  {
   this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);

   this.theme = new MetalWorksMobileTheme(this.stage);

   var list:List = new List();
   list.width = this.stage.stageWidth;
   list.height = this.stage.stageHeight;
   this.addChild(list);

   var listData:Object;
   var listTable:Array = [];
   for(var i:int=0;i<1000;i++)
   {
    listData = { text : i.toString()+":"+"アイテム" };
    listTable.push(listData);
   }
   var groceryList:ListCollection = new ListCollection(listTable);
   list.dataProvider = groceryList;
   list.itemRendererProperties.labelField = "text";
   list.itemRendererType = BasicItemRenderer;
  }
 }
}
BasicItemRenderer.as
package
{
 import feathers.controls.Label;
 import feathers.controls.List;
 import feathers.controls.Scroller;
 import feathers.controls.renderers.IListItemRenderer;
 import feathers.core.FeathersControl;

 import flash.geom.Point;
 import flash.text.TextFormat;

 import starling.display.Quad;
 import starling.events.Event;
 import starling.events.Touch;
 import starling.events.TouchEvent;
 import starling.events.TouchPhase;

 public class BasicItemRenderer extends FeathersControl implements IListItemRenderer
 {
  public function BasicItemRenderer()
  {
   this.addEventListener(TouchEvent.TOUCH,touchHandler);
   this.addEventListener(Event.REMOVED_FROM_STAGE,removedFromStageHandler);
  }

  protected var itemLabel:Label;

  protected var itemBG:Quad;

  protected var _index:int = -1;

  protected var touchPointID:Number;

  private static const HELPER_POINT:Point = new Point();

  protected var hasScrolled:Boolean = false;

  public function get index():int
  {
   return this._index;
  }

  public function set index(value:int):void
  {
   if(this._index == value) return;
   this._index = value;
   this.invalidate(INVALIDATION_FLAG_DATA);
  }

  protected var _owner:List;

  public function get owner():List
  {
   return List(this._owner);
  }

  public function set owner(value:List):void
  {
   if(this._owner == value) return;
   if(this._owner) this._owner.removeEventListener(Event.SCROLL, owner_scrollHandler);
   this._owner = value;
   if(this._owner) this._owner.addEventListener(Event.SCROLL, owner_scrollHandler);
   this.invalidate(INVALIDATION_FLAG_DATA);
  }

  protected var _data:Object;

  public function get data():Object
  {
   return this._data;
  }

  public function set data(value:Object):void
  {
   if(this._data == value)
   {
    return;
   }
   this._data = value;
   this.invalidate(INVALIDATION_FLAG_DATA);
  }

  protected var _isSelected:Boolean;

  public function get isSelected():Boolean
  {
   return this._isSelected;
  }

  public function set isSelected(value:Boolean):void
  {
   if(this._isSelected == value) return;
   this._isSelected = value;
   this.invalidate(INVALIDATION_FLAG_SELECTED);
   this.dispatchEventWith(Event.CHANGE);
  }

  override protected function initialize():void
  {
   if(!this.itemBG)
   {
    this.itemBG = new Quad(1,1,Math.floor(Math.random() * 0xffffff));
    this.addChild(this.itemBG);
   }

   if(!this.itemLabel)
   {
    this.itemLabel = new Label();
    this.addChild(this.itemLabel);
   }
  }

  override protected function draw():void
  {
   const dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA);
   const selectionInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SELECTED);
   var sizeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SIZE);

   if(dataInvalid) this.commitData();
   sizeInvalid = this.autoSizeIfNeeded() || sizeInvalid;
   if(dataInvalid || sizeInvalid) this.layout();
  }

  protected function autoSizeIfNeeded():Boolean
  {
   const needsWidth:Boolean = isNaN(this.explicitWidth);
   const needsHeight:Boolean = isNaN(this.explicitHeight);
   if(!needsWidth && !needsHeight) return false;
   this.itemLabel.width = NaN;
   this.itemLabel.height = NaN;
   this.itemLabel.validate();
   var newWidth:Number = this.explicitWidth;
   if(needsWidth) newWidth = this.itemLabel.width;
   this.explicitHeight = 150;
   var newHeight:Number = this.explicitHeight;
   if(needsHeight) newHeight = this.itemLabel.height;

   this.itemBG.width = newWidth;
   this.itemBG.height = newHeight;

   return this.setSizeInternal(newWidth, newHeight, false);
  }

  protected function commitData():void
  {
   if(this._data)
   {
    this.itemLabel.text = this._data.text.toString();
   } else {
    this.itemLabel.text = "";
   }
  }

  protected function layout():void
  {
   this.itemBG.width = this.actualWidth;
   this.itemBG.height = this.actualHeight;

   this.itemLabel.width = this.actualWidth;
   this.itemLabel.height = this.actualHeight;
   this.itemLabel.y = (this.actualHeight / 2) - (this.itemLabel.y / 2);
  }

  protected function touchHandler(event:TouchEvent):void
  {
   const touches:Vector.<touch> = event.getTouches(this);
   if(touches.length == 0)
   {
    //hover has ended
    return;
   }
   if(this.touchPointID >= 0)
   {
    var touch:Touch;
    for each(var currentTouch:Touch in touches)
    {
     if(currentTouch.id == this.touchPointID)
     {
      touch = currentTouch;
      break;
     }
    }
    if(!touch)
    {
     return;
    }
    if(touch.phase == TouchPhase.ENDED)
    {
     this.touchPointID = -1;
     this.itemBG.visible = true;
     if(!this.hasScrolled)
     {                    
      touch.getLocation(this, HELPER_POINT);
      //check if the touch is still over the target
      //also, only change it if we're not selected. we're not a toggle.
      if(this.hitTest(HELPER_POINT, true) != null && !this._isSelected)
      {
       this.isSelected = true;
       trace(_index);
      }
     }

     return;
    }
   }
   else
   {
    for each(touch in touches)
    {
     if(touch.phase == TouchPhase.BEGAN)
     {
      this.touchPointID = touch.id;
      this.hasScrolled = false;

      return;
     }
    }
   }
  }

  protected function removedFromStageHandler(event:Event):void
  {
   this.touchPointID = -1;
  }

  protected function owner_scrollHandler(event:Event):void
  {
   this.hasScrolled = true;
  }        

 }
}

Feathers Example プロジェクトの作成1

ボタンとリストのサンプルを作成します。
4つのソースコードと画像を1つ用意します。
FeathersExample.as
package
{
 import flash.display.Sprite;
 import flash.display.StageAlign;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.geom.Rectangle;

 import starling.core.Starling;
 import starling.utils.HAlign;
 import starling.utils.VAlign;

 [SWF(width="960",height="640",frameRate="60",backgroundColor="#2f2f2f")]
 public class FeathersExample extends Sprite
 {
  public function FeathersExample()
  {
   if(this.stage)
   {
    this.stage.scaleMode = StageScaleMode.NO_SCALE;
    this.stage.align = StageAlign.TOP_LEFT;
   }
   this.mouseEnabled = this.mouseChildren = false;
   this.loaderInfo.addEventListener(Event.COMPLETE, loaderInfo_completeHandler);
  }

  private var _starling:Starling;

  private function loaderInfo_completeHandler(event:Event):void
  {
   var exampleIndex:int;
   Starling.handleLostContext = true;
   Starling.multitouchEnabled = true;

   exampleIndex = 0;
   switch(exampleIndex)
   {
    case 0:
     this._starling = new Starling(ButtonExample, this.stage);
     break;
    case 1:
     this._starling = new Starling(ListExample, this.stage);
     break;
   }
   this._starling.enableErrorChecking = false;
   this._starling.showStatsAt(HAlign.RIGHT,VAlign.BOTTOM,2);
   this._starling.start();

   this.stage.addEventListener(Event.RESIZE, stage_resizeHandler, false, int.MAX_VALUE, true);
   this.stage.addEventListener(Event.DEACTIVATE, stage_deactivateHandler, false, 0, true);
  }

  private function stage_resizeHandler(event:Event):void
  {
   this._starling.stage.stageWidth = this.stage.stageWidth;
   this._starling.stage.stageHeight = this.stage.stageHeight;

   const viewPort:Rectangle = this._starling.viewPort;
   viewPort.width = this.stage.stageWidth;
   viewPort.height = this.stage.stageHeight;
   try
   {
    this._starling.viewPort = viewPort;
   }
   catch(error:Error) {}
  }

  private function stage_deactivateHandler(event:Event):void
  {
   this._starling.stop();
   this.stage.addEventListener(Event.ACTIVATE, stage_activateHandler, false, 0, true);
  }

  private function stage_activateHandler(event:Event):void
  {
   this.stage.removeEventListener(Event.ACTIVATE, stage_activateHandler);
   this._starling.start();
  }

 }
}

ButtonExample.as
package 
{
 import feathers.controls.Button;
 import feathers.controls.Callout;
 import feathers.controls.Label;
 import feathers.themes.*;

 import flash.display.Bitmap;

 import starling.display.Image;
 import starling.display.Sprite;
 import starling.events.Event;
 import starling.textures.Texture;

 public class ButtonExample extends Sprite
 {
  [Embed(source='./icon.png')]
  public var ButtonIconSkin:Class;

  public function ButtonExample()
  {
   this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
  }

  protected var theme:MetalWorksMobileTheme;

  protected var button:Button;

  protected function addedToStageHandler(event:Event):void
  {
   this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);

   var icon:Bitmap = new ButtonIconSkin();
   var themeIndex:int;

   themeIndex = 0;
   switch(themeIndex)
   {
    case 0:
     this.theme = new MetalWorksMobileTheme(this.stage);
     break;
    case 1:
     //this.theme = new AeonDesktopTheme(this.stage);
     break;
    case 2:
     //this.theme = new ExtendMetalWorksTheme(this.stage);
     break;
   }
   this.button = new Button();
   this.button.label = "クリック";
   this.button.nameList.add(ExtendMetalWorksTheme.ALTERNATE_NAME_MY_CUSTOM_BUTTON);
   this.button.defaultIcon = new Image(Texture.fromBitmapData(icon.bitmapData));
   this.button.iconPosition = Button.ICON_POSITION_TOP;
   this.button.gap = 0;
   this.button.horizontalAlign = Button.HORIZONTAL_ALIGN_LEFT;
   this.button.verticalAlign = Button.VERTICAL_ALIGN_MIDDLE;
   this.button.width = 200;
   this.button.height = 80;

   this.button.labelOffsetX = this.button.iconOffsetX + icon.width;
   this.button.labelOffsetY = -(this.button.height/2);

   this.button.addEventListener(Event.TRIGGERED, button_triggeredHandler);
   this.button.addEventListener(Event.CHANGE, button_changeHandler);
   this.addChild(this.button);
   this.button.validate();

   this.button.x = (this.stage.stageWidth - this.button.width) / 2;
   this.button.y = (this.stage.stageHeight - this.button.height) / 2;
  }

  protected function button_triggeredHandler(event:Event):void
  {
   const label:Label = new Label();
   label.text = "Hi, I'm Feathers!\nHave a nice day.";
   Callout.show(label, this.button);
  }

  private function button_changeHandler(event:Event):void
  {
   var button:Button = event.currentTarget as Button;
   trace( "button.isSelected has changed:",button.isSelected);
  }
 }
}

Starling FrameworkとFeathersの導入

FeathersのUIサンプルを作る為の準備をします。
開発環境はFlashBuilder 4.6です。

Starling FrameworkとFeathersを
Flexライブラリプロジェクトとして導入します。
AIR3.5SDKを利用します。(リンク先へ)

Starling Framework(リンク先へ)

プロジェクト名はStarlingFrameworkとして作成します。
StarlingFrameworkのソースコードをsrcフォルダにコピーします。
ビルドを行った後、binフォルダにStarlingFramework.swcが出来上がります。

プロジェクト名はFeathersとして作成します。
Feathersのソースコードをsrcフォルダにコピーします。
プロジェクトの追加からStarlingFrameworkを指定します。
ビルドを行った後、binフォルダにFeathers.swcが出来上がります。

Feathers Theme(リンク先へ)
Feathersはテーマを1つ設定する必要があります。
テーマはUI全体の見た目を定義する機能です。

プロジェクト名はFeathersThemeとして作成します。
使用したいテーマのソースコードをライブラリに追加します。
プロジェクトの追加からStarlingFrameworkとFeathersを指定します。
ビルドを行った後、binフォルダにFeathersTheme.swcが出来上がります。

Feathersについて

FeathersはStarling Frameworkを利用したUIコンポーネントです。


用意されているUIコンポーネント一覧
  • Button
  • ButtonGroup
  • Callout
  • Check
  • GroupedList
  • Header
  • Label
  • List
  • Custom Item Renderers
  • PageIndicator
  • PickerList
  • ProgressBar
  • Radio
  • Screen
  • ScreenNavigator
  • ScreenNavigator Transitions
  • ScrollBar
  • ScrollContainer
  • Scroller
  • ScrollText
  • SimpleScrollBar
  • Slider
  • TabBar
  • TextInput
  • ToggleSwitch

導入条件
・Flash PlayerまたはAdobe AIRが動作する環境 Flash(リンク先へ) AIR(リンク先へ)
・Flash Proffesional (リンク先へ)またはas3で開発ができる環境
・Starling Frameworkライブラリを用意する (リンク先へ)
・Feathersライブラリを用意する(リンク先へ)

この作成手順は2013年2月時点のものです。今後仕様変更の可能性があります。
ソースと関連ファイルの使用結果について、一切の保証と責任は負いません。

AIR3.5 Feathers UISample

Feathersを利用したUIのサンプル動画を作成しました。
端末はNexus7のバージョン4.2.1です。

2013年1月27日日曜日

Adobe Scout まとめ

・Adobe Scoutの概要とインストール(リンク先へ)
・モバイル版Adobe Scoutの導入(リンク先へ)
・Telemetry機能について(リンク先へ)
・Adobe Scoutの使用(リンク先へ)

2013年1月26日土曜日

Adobe Scoutの使用

Tipsを紹介します

Adobe Scoutの画面内の各項目の詳細は
Adobe Scout ファーストステップガイドに記載してあります。(リンク先へ)

フレームタイムラインの始点1フレームと終点5フレームに合わせて
5フレーム分の計測結果を右側の「概要」に表示しています

フレームタイムラインをアクティブにした後、カーソルキーの左右キーで
タイムラインの1フレームずつ計測結果を見る事ができます

Telemetry機能について

Telemetryを有効にしないと以下の項目がプロファイルできません

・ActionScriptコードの情報
・DisplayListレンダリング
・Stage3Dの詳細情報
下部のデータ設定で項目が「無効」になっています

Flash Builder 4.7でTelemetryを有効にする
Flash Builder 4.7では、Telemetryを有効にするチェックを入れてビルドを
行う事で、上記3つの項目が「有効」になります


Flash Builder 4.6以前またはFlash ProffesionalでTelemetryを有効にする
CUI形式のPythonスクリプトまたは
GUI形式のSWF Scout Enablerを使用します
・Pythonスクリプトを利用する(リンク先へ)
・SWF Scout Enablerを利用する(リンク先へ)

Telemetryを有効にするとデータ設定の各項目が「有効」なります

モバイル版Adobe Scoutの導入

モバイルからプロファイルする場合、以下の方法になります
各モバイルOS毎に用意されているScoutコンパニオンアプリケーションを
インストールしてデスクトップPCでプロファイルを行う

Adobe Scout Companion app for iOS(リンク先へ)
Adobe Scout Companion app for Android(リンク先へ)
Adobe Scout Companion app for Kindle(リンク先へ)

AndroidにScout コンパニオンアプリケーションを入れる

プロファイルを行うまでの手順
・Scout を実行しているコンピュータと同じ Wi-Fi ネットワークにデバイスが接続されていることを確認してから、Scout コンパニオンアプリケーションを起動します。
・「有効」をタップします。
・Scout を実行しているコンピュータの検索が実行され、すべての Scout インスタンスのリストが表示されます。
・コンピュータの名前が表示されたら、それをクリックして接続します。
・AIR3.4以降でビルドを行ったモバイルコンテンツを起動すると、Scoutに新しいセッションが表示されます。

Adobe Scoutの概要とインストール

Adobe Scout は、デスクトップ (Flash Player) とモバイルデバイス (Adobe AIR) の両方で実行する Flash コンテンツ用の次世代プロファイラーです

Adobe Scoutの入手から実際に使用するまでの手順をまとめていきます

導入条件
・Flash PlayerまたはFlash Debug Playerの11.4以降をインストールする(リンク先へ)
・Adobe AIRの3.4以降をインストールする(リンク先へ)

導入方法
・Adobe IDを利用してログインする(リンク先へ)
Adobe Scoutを選択してダウンロードする

・ダウンロード完了後、インストールを行う

Adobe Scout ファーストステップガイドでは仕組みや使用方法が詳細に
記載されています(リンク先へ)

この作成手順は2013年1月時点のものです。今後仕様変更の可能性があります。
ソースと関連ファイルの使用結果について、一切の保証と責任は負いません。