2013年3月28日木曜日

ANEでnative twitterを利用する SWC編

FlashBuilderでSWCの作成を行います
3つのファイルを追加します
・NativeTwitter.as
・TweetCompositionResult.as
・TWRequestMethod.as

ビルドを行い、binディレクトリにNativeTwitter.swcが作成されます

XCodeで作成したlibNativeTwitter.aファイルをbinフォルダへコピーします。
これは後程aneファイルを作成する時に必要になります。

この時点ではまだNativeTwitter.aneは作成されていませんが、
ANE作成時にこのディレクトリへ出力するようにしています。

ANEでnative twitterを利用する XCode編

XCodeを起動し、Cocoa Touch Static Libraryプロジェクトを選択します

nativetwitter-ane内にある2つのソースコードを追加します
・NativeTwitter.h
・NativeTwitter.m

FlashRuntimeExtensions.hを追加します
FlashRuntimeExtensions.hの場所は(リンク先へ)を参考にして下さい

Accounts.frameworkTwitter.framework
TARGETSBuild Phases内にあるLink Binary With Librariesで追加します

ビルドを行い、libNativeTwitter.aを作成します

ANEでnative twitterを利用する

native twitterをANEを通して利用してみます。

native twitterサンプルアプリ画面
開発環境
MacOS 10.7.5
XCode 4.6
FlashBuilder 4.6
ANEtty 1.0.1 Preview(リンク先へ)
nativetwitter-ane(リンク先へ)




ANEttyでnative twitterを試してみる


ANEtty 1.0.1 Previewを利用して、native twitterのサンプルを動作させてみました。
 
1.ANEでnative twitterを利用する(リンク先へ)

2.ANEでnative twitterを利用する XCode編(リンク先へ)

3.ANEでnative twitterを利用する SWC編(リンク先へ)

4.ANEでnative twitterを利用する ANEtty編(リンク先へ)

5.ANEでnative twitterを利用する ビルド編(リンク先へ)

6.ANEでnative twitterを試してみる(リンク先へ)

実機でnative twitterを試してみる

NativeTwitterSample.ipaを実機にインストールして試してみました。
端末の設定のTwitterにユーザーアカウントを作成しておく必要があります。
Compose tweetをタップすると、Twitterダイアログが表示されます。

実際にTweetできるか試してみました。



2013年3月14日木曜日

adt+Rakeでビルドやインストールをする

Rakeを使用してiOS端末へ以下の作業を行います。
・リリースビルドとインストール
アンインストール

AIRで使用するadtコマンドは(リンク先へ)から調べることが出来ます。


作成したプロジェクトのbin-debug内にRakefileとSettings.rbを配置します。
※画像キャプチャ内にdevicesファイルというのがありますが使用しません。

Settings.rb
task :set do
    $provisionFilePath = "~/certs/provision.mobileprovision";
    $p12FilePath = "~/certs/provision.p12"; 
    $storepw = "password"; 
    $ipaFileName = "./MobileSample.ipa";
    $appXmlFileName = "./MobileSample-app.xml";
    $swfFileName = "./MobileSample.swf";
    $extdirPath = "./";
    $platformsdkPath = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk";
    $defaultPngPath = "./Default.png";
    $deviceHandle = "1";
end
Settings.rbはRakefile内で使用します。 リリースビルドに必要な各種項目を定義します。

Rakefile
load "./Settings.rb"

$bl = " ";

task :default => [:set,:build,:ipaInstall];

task :build do
    adt = "adt";
    adt += $bl+"-package";
    adt += $bl+"-target"+$bl+"ipa-app-store";
    adt += $bl+"-provisioning-profile"+$bl+"#{$provisionFilePath}";
    adt += $bl+"-storetype"+$bl+"pkcs12";
    adt += $bl+"-keystore"+$bl+"#{$p12FilePath}";
    adt += $bl+"-storepass"+$bl+"#{$storepw}";
    adt += $bl+"#{$ipaFileName}";
    adt += $bl+"#{$appXmlFileName}";
    adt += $bl+"#{$swfFileName}";
    adt += $bl+"#{$defaultPngPath}";
    adt += $bl+"-extdir"+$bl+"#{$extdirPath}";
    adt += $bl+"-platformsdk"+$bl+"#{$platformsdkPath}";
    sh "#{adt}";
end

task :ipaInstall do
    adt = "adt";
    adt += $bl+"-installApp";
    adt += $bl+"-platform"+$bl+"ios";
    adt += $bl+"-platformsdk"+$bl+"#{$platformsdkPath}";
    adt += $bl+"-device"+$bl+"#{$deviceHandle}";
    adt += $bl+"-package"+$bl+"#{$ipaFileName}";
    sh "#{adt}";
end

task :ipaUninstall,'appid'
task :ipaUninstall do |t, args|
    Rake::Task["set"].invoke;
    adt = "adt";
    adt += $bl+"-uninstallApp";
    adt += $bl+"-platform"+$bl+"ios";
    adt += $bl+"-platformsdk"+$bl+"#{$platformsdkPath}";
    adt += $bl+"-device"+$bl+"#{$deviceHandle}";
    adt += $bl+"-appid"+$bl+"#{args['appid']}";
    sh "#{adt}";
end

作成したRakefileを実行すると、リリースビルド後に端末へインストールします。
$>rake
bin-debugにディレクトリを移動し、rakeコマンドを呼び出します。

次にアプリのアンインストールを行います。
$>rake 'ipaUninstall[com.app.MobileSample]'
ipaUninstallタスクを指定し、applicationIDを引数に渡します。

2013年2月5日火曜日

Feathers - デスクトップとモバイルで使えるUIコントロールフレームワーク

Stage3D
FlashPlayer11.0(AIR 3.0)よりGPUによる2D, 3Dのハードウェアアクセラレーション
レンダリング機能が付きました。
この機能により、描画処理の向上に期待ができるようになります。
Stage3DはFlashPlayerとAIRで使う事ができます。

Starling Framework
Stage3Dの機能を使うにはAGALというアセンブラ命令をActionScriptに書きます。
Starling FrameworkではAGALを意識する事なく、普段ActionScriptを書くようなスタイルでスプライト機能やアニメーションを利用できます。

Feathers
Starling Frameworkの機能を利用したUIフレームワークです。
ボタンやリストのようなUI、画面の管理を行う事ができます。

動作パフォーマンス
Feathersを利用した動作パフォーマンスですがNexus7で検証した所
おおむね良好だと思います。動画を撮影しましたのでご覧下さい。
AIR3.5 Feathers UISample

導入編
Feathersを導入してアプリケーションを開発する方法はいくつかありますが
FlashBuilderを利用した開発手法を取り上げます。
Feathersについて簡単な概要とStarling FrameworkとFeathersの
ライブラリプロジェクトの設定方法を記載しています。



UIコンポーネント ボタン編
ボタンの組み込み方法とスキンの変更処理を記載しています。

ボタンとリストのサンプルソースコード作成と素材を組み込みます。
サンプルソースコードを元にボタンの概要とスキンの変更処理について解説しています。

UIコンポーネント リスト編
リストコンポーネントとアイテムレンダラについて記載しています。

画面の処理 スクリーン編
複数の画面間の移動を行う処理を記載しています。

スクリーンのサンプルソースコードを作成します。

サンプルソースコードを元にスクリーンの解説をしています。

Feathers Example まとめ

はじめに

Feathers ListとButtonのサンプル

Feathers Screenのサンプル

Feathers ScreenExample Screenについて

このサンプルはFeathersのサイト
「How to use the Feathers Screen Component(リンク先へ) 」
「How to use the Feathers ScreenNavigator component(リンク先へ) 」
「Feathers ScreenNavigator Transitions(リンク先へ) 」
を参考にしています。

Feathers ScreenExampleのソースコード(リンク先へ)

Main.as
this._navigator.addScreen(MAIN_MENU,new ScreenNavigatorItem(MainMenuScreen,MAIN_MENU_EVENTS));
addScreenメソッド
ScreenNavigatorItemコンストラクタで必要とされる最初の引数は、 画面のIDと画面のインスタンスまたはすでにインスタンス化されているのDisplayObjectです。

イベントを利用した画面のナビゲーション
ScreenNavigatorItemクラスの第2引数(Object型)でイベントを定義します。
 イベントを送出すると、ScreenNavigatorは自動的に別の画面に移動する処理を
行います。

MainMenuScreen.as
   this._list.dataProvider = new ListCollection(
    [
    { text: "Sub 1", event: SHOW_SUB1_SCREEN },
    { text: "Sub 2", event: SHOW_SUB2_SCREEN },
    ]);
Main.asで定義したイベントをリストのdataProviderに設定します。
  private function list_changeHandler(event:Event):void
  {
   const eventType:String = this._list.selectedItem.event as String;
   this.dispatchEventWith(eventType);
  }
リストのアイテムが選択された時に画面に移動する為のイベントを送出します。

ownerプロパティによる画面のナビゲーション
Sub1Screen.as
  private function onBackButton():void
  {
   this.owner.showScreen(Main.MAIN_MENU);
  }
Screenクラスが持つownerプロパティのshowScreenメソッドで別画面の移動を行います。

トランジションの設定
Main.as
   this._transitionManager = new ScreenSlidingStackTransitionManager(this._navigator);
   this._transitionManager.duration = 0.4;
画面の移動を行う時のトランジションとしてScreenSlidingStackTransitionManagerを使用します。

Feathers ScreenExample 作成3

Sub2ChildScreen.as
package screens
{
 import feathers.controls.Button;
 import feathers.controls.Header;
 import feathers.controls.Label;
 import feathers.controls.Screen;
 import feathers.controls.ScrollContainer;
 import feathers.layout.AnchorLayout;
 import feathers.layout.VerticalLayout;

 import starling.display.DisplayObject;
 import starling.events.Event;

 public class Sub2ChildScreen extends Screen
 {
  private var _container:ScrollContainer;
  private var _header:Header;
  private var _backButton:Button;

  public var horizontalAlign:String = VerticalLayout.HORIZONTAL_ALIGN_LEFT;
  public var verticalAlign:String = VerticalLayout.VERTICAL_ALIGN_TOP;
  public var gap:Number = 2;
  public var paddingTop:Number = 0;
  public var paddingRight:Number = 0;
  public var paddingBottom:Number = 0;
  public var paddingLeft:Number = 0;

  public function Sub2ChildScreen()
  {
   super();
  }

  override protected function initialize():void
  {
   this._container = new ScrollContainer();
   this._container.layout = new AnchorLayout();
   this._container.verticalScrollPolicy = ScrollContainer.SCROLL_POLICY_ON;
   this._container.snapScrollPositionsToPixels = true;
   this.addChild(this._container);

   this._header = new Header();
   this._header.title = "Sub2 ChildScreen";
   this.addChild(this._header);

   this._backButton = new Button();
   this._backButton.label = "Back";
   this._backButton.addEventListener(Event.TRIGGERED, backButton_triggeredHandler);

   this._header.leftItems = new &ltdisplayobject>
    [
    this._backButton
    ];

   this.backButtonHandler = this.onBackButton;

   var label:Label = new Label();
   label.text = "Sub2のサブビューです";
   this._container.addChild(label);
  }

  override protected function draw():void
  {
   this._header.width = this.actualWidth;
   this._header.validate();

   this._container.y = this._header.height;
   this._container.width = this.actualWidth;
   this._container.height = this.actualHeight - this._container.y;
  }

  private function onBackButton():void
  {
   this.owner.showScreen(Main.SUB2_SCREEN);
  }

  private function backButton_triggeredHandler(event:Event):void
  {
   this.onBackButton();
  }
 }
}
Sub2Screen.as
package screens
{
 import feathers.controls.Button;
 import feathers.controls.Header;
 import feathers.controls.Screen;
 import feathers.controls.ScrollContainer;
 import feathers.layout.VerticalLayout;

 import starling.display.DisplayObject;
 import starling.display.Quad;
 import starling.events.Event;

 public class Sub2Screen extends Screen
 {
  public static const SHOW_CHILD_SCREEN:String = "showChildScreen";

  private var _container:ScrollContainer;
  private var _header:Header;
  private var _backButton:Button;
  private var _childButton:Button;

  public var horizontalAlign:String = VerticalLayout.HORIZONTAL_ALIGN_LEFT;
  public var verticalAlign:String = VerticalLayout.VERTICAL_ALIGN_TOP;
  public var gap:Number = 2;
  public var paddingTop:Number = 0;
  public var paddingRight:Number = 0;
  public var paddingBottom:Number = 0;
  public var paddingLeft:Number = 0;

  public function Sub2Screen()
  {
   super();
  }

  override protected function initialize():void
  {
   const layout:VerticalLayout = new VerticalLayout();
   layout.gap = this.gap;
   layout.paddingTop = this.paddingTop;
   layout.paddingRight = this.paddingRight;
   layout.paddingBottom = this.paddingBottom;
   layout.paddingLeft = this.paddingLeft;
   layout.horizontalAlign = this.horizontalAlign;
   layout.verticalAlign = this.verticalAlign;

   this._container = new ScrollContainer();
   this._container.layout = layout;
   this._container.verticalScrollPolicy = ScrollContainer.SCROLL_POLICY_ON;
   this._container.snapScrollPositionsToPixels = true;
   this.addChild(this._container);

   this._header = new Header();
   this._header.title = "Sub2 Screen";
   this.addChild(this._header);

   this._backButton = new Button();
   this._backButton.label = "Back";
   this._backButton.addEventListener(Event.TRIGGERED, backButton_triggeredHandler);

   this._header.leftItems = new &ltdisplayobject>
    [
    this._backButton
    ];

   this._childButton = new Button();
   this._childButton.label = "ChildScreen";
   this._childButton.addEventListener(Event.TRIGGERED, childButton_triggeredHandler);

   this._header.rightItems = new &ltdisplayobject>
    [
    this._childButton
    ];

   this.backButtonHandler = this.onBackButton;

   var redQuad:Quad = new Quad(100,100,0xff0000);
   var greenQuad:Quad = new Quad(100,100,0x00ff00);
   var blueQuad:Quad = new Quad(100,100,0x0000ff);

   this._container.addChild(redQuad);
   this._container.addChild(greenQuad);
   this._container.addChild(blueQuad);
  }

  private function childButton_triggeredHandler():void
  {
   this.dispatchEventWith(SHOW_CHILD_SCREEN);
  }

  override protected function draw():void
  {
   this._header.width = this.actualWidth;
   this._header.validate();

   this._container.y = this._header.height;
   this._container.width = this.actualWidth;
   this._container.height = this.actualHeight - this._container.y;
  }

  private function onBackButton():void
  {
   this.owner.showScreen(Main.MAIN_MENU);
  }

  private function backButton_triggeredHandler(event:Event):void
  {
   this.onBackButton();
  }
 }
}
StarlingFramework,Feathers,FeathersThemeの
Flexライブラリプロジェクトの登録を行います。(リンク先へ)
ビルドを行います。

Feathers ScreenExample 作成2

MainMenuScreen.as
package screens
{
 import feathers.controls.Header;
 import feathers.controls.List;
 import feathers.controls.Screen;
 import feathers.data.ListCollection;
 import feathers.skins.StandardIcons;

 import starling.events.Event;
 import starling.textures.Texture;

 public class MainMenuScreen extends Screen
 {
  public static const SHOW_SUB1_SCREEN:String = "showSub1Screen";
  public static const SHOW_SUB2_SCREEN:String = "showSub2Screen";

  private var _header:Header;
  private var _list:List;

  public function MainMenuScreen()
  {
   super();
  }

  override protected function initialize():void
  {
   this._header = new Header();
   this._header.title = "Screen Test";
   this.addChild(this._header);

   this._list = new List();
   this._list.dataProvider = new ListCollection(
    [
    { text: "Sub 1", event: SHOW_SUB1_SCREEN },
    { text: "Sub 2", event: SHOW_SUB2_SCREEN },
    ]);
   this._list.itemRendererProperties.labelField = "text";
   this._list.addEventListener(Event.CHANGE,list_changeHandler);
   this._list.itemRendererProperties.accessorySourceFunction = accessorySourceFunction;
   this.addChild(this._list);
  }

  override protected function draw():void
  {
   this._header.width = this.actualWidth;
   this._header.validate();

   this._list.y = this._header.height;
   this._list.width = this.actualWidth;
   this._list.height = this.actualHeight - this._list.y;
  }

  private function accessorySourceFunction(item:Object):Texture
  {
   return StandardIcons.listDrillDownAccessoryTexture;
  }

  private function list_changeHandler(event:Event):void
  {
   const eventType:String = this._list.selectedItem.event as String;
   this.dispatchEventWith(eventType);
  }
 }
}
Sub1Screen.as
package screens
{
 import feathers.controls.Button;
 import feathers.controls.Header;
 import feathers.controls.Screen;
 import feathers.controls.ScrollContainer;
 import feathers.layout.HorizontalLayout;

 import starling.display.DisplayObject;
 import starling.display.Quad;
 import starling.events.Event;

 public class Sub1Screen extends Screen
 {
  private var _container:ScrollContainer;
  private var _header:Header;
  private var _backButton:Button;
  private var _settingsButton:Button;

  public var horizontalAlign:String = HorizontalLayout.HORIZONTAL_ALIGN_LEFT;
  public var verticalAlign:String = HorizontalLayout.VERTICAL_ALIGN_TOP;
  public var gap:Number = 2;
  public var paddingTop:Number = 0;
  public var paddingRight:Number = 0;
  public var paddingBottom:Number = 0;
  public var paddingLeft:Number = 0;

  public function Sub1Screen()
  {
   super();
  }

  override protected function initialize():void
  {
   const layout:HorizontalLayout = new HorizontalLayout();
   layout.gap = this.gap;
   layout.paddingTop = this.paddingTop;
   layout.paddingRight = this.paddingRight;
   layout.paddingBottom = this.paddingBottom;
   layout.paddingLeft = this.paddingLeft;
   layout.horizontalAlign = this.horizontalAlign;
   layout.verticalAlign = this.verticalAlign;

   this._container = new ScrollContainer();
   this._container.layout = layout;
   this._container.horizontalScrollPolicy = ScrollContainer.SCROLL_POLICY_ON;
   this._container.snapScrollPositionsToPixels = true;
   this.addChild(this._container);

   this._header = new Header();
   this._header.title = "Sub1 Screen";
   this.addChild(this._header);

   this._backButton = new Button();
   this._backButton.label = "Back";
   this._backButton.addEventListener(Event.TRIGGERED, backButton_triggeredHandler);

   this._header.leftItems = new 
    [
    this._backButton
    ];

   this.backButtonHandler = this.onBackButton;

   var redQuad:Quad = new Quad(100,100,0xff0000);
   var greenQuad:Quad = new Quad(100,100,0x00ff00);
   var blueQuad:Quad = new Quad(100,100,0x0000ff);

   this._container.addChild(redQuad);
   this._container.addChild(greenQuad);
   this._container.addChild(blueQuad);
  }

  override protected function draw():void
  {
   this._header.width = this.actualWidth;
   this._header.validate();

   this._container.y = this._header.height;
   this._container.width = this.actualWidth;
   this._container.height = this.actualHeight - this._container.y;
  }

  private function onBackButton():void
  {
   this.owner.showScreen(Main.MAIN_MENU);
  }

  private function backButton_triggeredHandler(event:Event):void
  {
   this.onBackButton();
  }
 }
}

Feathers ScreenExample 作成1

プロジェクト名はFeathersScreenです。
パッケージの構成です。

FeathersScreenExample.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;

 [SWF(width="960",height="640",frameRate="60",backgroundColor="#2f2f2f")]
 public class FeathersScreenExample extends Sprite
 {
  private var _starling:Starling;        

  public function FeathersScreenExample()
  {
   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 function loaderInfo_completeHandler(event:Event):void
  {
   Starling.handleLostContext = true;
   Starling.multitouchEnabled = true;
   this._starling = new Starling(Main, this.stage);
   this._starling.enableErrorChecking = false;
   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();
  }
 }
}
Main.as
package 
{
 import feathers.controls.ScreenNavigator;
 import feathers.controls.ScreenNavigatorItem;
 import feathers.controls.ScrollContainer;
 import feathers.motion.transitions.ScreenSlidingStackTransitionManager;
 import feathers.themes.MetalWorksMobileTheme;

 import screens.MainMenuScreen;
 import screens.Sub1Screen;
 import screens.Sub2ChildScreen;
 import screens.Sub2Screen;

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

 public class Main extends Sprite
 {
  public static const MAIN_MENU:String = "mainMenu";
  public static const SUB1_SCREEN:String = "sub1Screen";
  public static const SUB2_SCREEN:String = "sub2Screen";
  public static const SUB2_CHILD_SCREEN:String = "sub2ChildScreen";

  private var _theme:MetalWorksMobileTheme;
  private var _container:ScrollContainer;
  private var _navigator:ScreenNavigator;
  private var _menu:MainMenuScreen;
  private var _transitionManager:ScreenSlidingStackTransitionManager;

  private static const MAIN_MENU_EVENTS:Object =
   {
    showSub1Screen: SUB1_SCREEN,
    showSub2Screen: SUB2_SCREEN
  }

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

  private function addedToStageHandler(event:Event):void
  {
   this._theme = new MetalWorksMobileTheme(this.stage);

   this._navigator = new ScreenNavigator();

   this._navigator.addScreen(SUB1_SCREEN,new ScreenNavigatorItem(Sub1Screen));

   this._navigator.addScreen(SUB2_SCREEN,
    new ScreenNavigatorItem(Sub2Screen,{showChildScreen:SUB2_CHILD_SCREEN}));
   this._navigator.addScreen(SUB2_CHILD_SCREEN,new ScreenNavigatorItem(Sub2ChildScreen));

   this._navigator.addScreen(MAIN_MENU,new ScreenNavigatorItem(MainMenuScreen,MAIN_MENU_EVENTS));

   this.addChild(this._navigator);

   this._transitionManager = new ScreenSlidingStackTransitionManager(this._navigator);
   this._transitionManager.duration = 0.4;

   this._navigator.showScreen(MAIN_MENU);
  }
 }
}

Feathers Screenサンプルについて

画面の管理や切り替えを行う為のScreenクラスを使ったサンプルを作成します。

サンプルアプリ概要

  • メインはリスト形式でビューを遷移する為のリストアイテムが2件あります。
  • Sub1をタップすると、Sub1 Screenに遷移します。
  • Sub2をタップすると、Sub2 Screenに遷移します。
  • ChildScreenボタンを押すとSub2 Child Screenへ遷移します。
  • Backボタンを押す事で1つ前のScreenへ戻ります。