[AS3] interface について 2

2011/11/30

こんにちは、きんくまです。

前回interfaceについて書いたのですけど、書いてて最後に思い出したのでそれをこのエントリーに載せようと思います。

前回の最後にiOSやAndroidのネイティブアプリの開発だとinterfaceを使って、イベントが通知されることが多いことを
思いだしたのでした。
それで、これをAS3で似たように作ってみるとどうなるかです。

下の図のような地図を見せるアプリを作るとします。

地図の部分をMapViewとして、これがドラッグを始めたときと、ドラッグし終わったときに、
別のクラスからそのイベントを取りたいとします。

そこで、そのイベントをinterfaceで定義します。

package
{
	public interface IMapViewDelegate
	{
		function willStartScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void;
		function didStopScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void;
	}
}

willStartScrollはスクロールを始めたとき、didStopScrollはスクロールが終わったときに実行されます。

MapViewのクラスはこうします。

package
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class MapView extends Sprite
	{
		private var _mapMask:Shape;
		private var _mapSprite:Sprite;
		public var delegate:IMapViewDelegate;
		
		public function MapView()
		{
			createView();
			_mapSprite.addEventListener(MouseEvent.MOUSE_DOWN, onViewMouseDown);
		}
		
		protected function onViewMouseDown(event:MouseEvent):void
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.startDrag(false);
			if(delegate != null){
				//delegateに伝える
				delegate.willStartScroll(this, _mapSprite.x, _mapSprite.y);
			}
		}
		
		protected function onStageMouseUp(event:MouseEvent):void
		{
			stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.stopDrag();
			if(delegate != null){
				//delegateに伝える
				delegate.didStopScroll(this, _mapSprite.x, _mapSprite.y);
			}
		}
		
		private function createView():void
		{
			_mapSprite = new Sprite();
			addChild(_mapSprite);
			var g:Graphics = _mapSprite.graphics;
			g.beginFill(0xaaaaaa, 1);
			g.drawRect(0,0,4000,4000);
			g.endFill();
			g.lineStyle(1,0);
			for(var i:int = 1; i < 40; i++){
				g.moveTo(i * 100, 0);
				g.lineTo(i * 100, 4000);
				g.moveTo(0, i * 100);
				g.lineTo(4000, i * 100);
			}
			_mapSprite.x = -2000;
			_mapSprite.y = -2000;
			
			_mapMask = new Shape();
			addChild(_mapMask);
			g = _mapMask.graphics;
			g.beginFill(0xff0000,1);
			g.drawRect(0,0,450,450);
			g.endFill();
			
			_mapSprite.mask = _mapMask;
		}
	}
}

中にdelegateというプロパティを用意して、その型をさきほど定義したIMapViewDelegateにします。
それで、delegateにイベントを伝えたいときに、その関数を実行します。

このMapViewを使うメインクラスはこうなります。

package
{
	import flash.display.Sprite;
	
	[SWF(width="450",height="450",frameRate="30")]
	public class Interface2 extends Sprite implements IMapViewDelegate
	{
		private var _mapView:MapView;
		
		public function Interface2()
		{
			_mapView = new MapView();
			_mapView.delegate = this; //ここでひもづけ
			addChild(_mapView);
		}
		
		public function willStartScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void
		{
			trace("start x:" + scrollPositionX + ", y:" +scrollPositionY);	
		}
		
		public function didStopScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void
		{
			trace("stop x:" + scrollPositionX + ", y:" +scrollPositionY);
		}
	}
}

mapViewを作ったあとに、自分自身の参照をdelegateに代入しています。
これをすると、mapViewのイベントをInterfaceに定義された関数経由で受け取ることができます。
mapView側はdelegateの実体がどんなクラスかを知らないのですが、IMapViewDelegateを実装していることを
知っているので、その関数を実行することができます。
delegateのクラスはどんなクラスでもよいので、柔軟性が高いやり方だと思います。

でも、これと同じようなことは、EventDispatcherですることができます。
その場合interfaceは必要ありません。

MapViewは

package
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class MapView extends Sprite
	{
		public static const SCROLL_START:String = "scroll_start";
		public static const SCROLL_STOP:String = "scroll_stop";
		private var _mapMask:Shape;
		private var _mapSprite:Sprite;
		
		public function MapView()
		{
			createView();
			_mapSprite.addEventListener(MouseEvent.MOUSE_DOWN, onViewMouseDown);
		}
		
		protected function onViewMouseDown(event:MouseEvent):void
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.startDrag(false);
			dispatchEvent(new Event(SCROLL_START));
		}
		
		protected function onStageMouseUp(event:MouseEvent):void
		{
			stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.stopDrag();
			dispatchEvent(new Event(SCROLL_STOP));
		}
		
		public function get scrollPositionX():Number
		{
			return _mapSprite.x;
		}
		
		public function get scrollPositionY():Number
		{
			return _mapSprite.y;
		}
		
		private function createView():void
		{
			_mapSprite = new Sprite();
			addChild(_mapSprite);
			var g:Graphics = _mapSprite.graphics;
			g.beginFill(0xaaaaaa, 1);
			g.drawRect(0,0,4000,4000);
			g.endFill();
			g.lineStyle(1,0);
			for(var i:int = 1; i < 40; i++){
				g.moveTo(i * 100, 0);
				g.lineTo(i * 100, 4000);
				g.moveTo(0, i * 100);
				g.lineTo(4000, i * 100);
			}
			_mapSprite.x = -2000;
			_mapSprite.y = -2000;
			
			_mapMask = new Shape();
			addChild(_mapMask);
			g = _mapMask.graphics;
			g.beginFill(0xff0000,1);
			g.drawRect(0,0,450,450);
			g.endFill();
			
			_mapSprite.mask = _mapMask;
		}
	}
}

イベントの文字列 SCROLL_STARTとSCROLL_STOPを用意してあげて、イベントを発行したくなったらdispatchします。

それを受け取る側はこうです。

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	[SWF(width="450",height="450",frameRate="30")]
	public class Interface2 extends Sprite
	{
		private var _mapView:MapView;
		
		public function Interface2()
		{
			_mapView = new MapView();
			_mapView.addEventListener(MapView.SCROLL_START, onMapScrollStart);
			_mapView.addEventListener(MapView.SCROLL_STOP, onMapScrollStop);
			addChild(_mapView);
		}
		
		protected function onMapScrollStart(event:Event):void
		{
			var map:MapView = event.currentTarget as MapView;
			trace("start x:" + map.scrollPositionX + ", y:" +map.scrollPositionY);	
		}
		
		protected function onMapScrollStop(event:Event):void
		{
			var map:MapView = event.currentTarget as MapView;
			trace("stop x:" + map.scrollPositionX + ", y:" +map.scrollPositionY);	
		}
	}
}

前回はinterfaceを実装していたのですが、今回は不必要になったのでとってしまいました。
代わりにaddEventListenerをして、イベントを登録しました。

今回はinterfaceとEventDispatcherの2つの仕組みで同じ機能を作ってみました。
これの使い分けなんですが、AS3ではEventDispatcherだけで問題ないかと思います。
ただ、interfaceを使うとEventDisptacherという別のクラスを使わなくてよいので、実行速度が早くなるんじゃないかなと思います。あ、実測したわけじゃないので、適当なこと書いたかも。

では。


自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る