ActionScriptイベントリスナの外にある変数を使いたい

すいません。wonferfl本まだ買っていません。今すぐ読みたい!正宗です。
今回は僕がいつも忘れてしまうことを備忘録がわりに書いておきます。

まずはやりたいことは次のようなこと。

package {
    import flash.display.Sprite;
	import flash.display.Graphics;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
    public class FlashTest extends Sprite {
        public function FlashTest() {
            var tf:TextField = new TextField;
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.text = "ここに数字が表示されます。";
			addChild(tf);
			//直径
			var radius:Number = 10.0;
			//横の間隔
			var marginWidth:Number = 5.0;
			//縦の間隔
			var marginHeight:Number = 15.0;
			//ボタンの数
			var l:int = 5;
			for (var i:int = 0; i < l; i++) 
			{
				var sp:Sprite = createCircle(radius);
				sp.buttonMode = true;
				sp.x = ((sp.width + marginWidth) * i) + radius;
				sp.y = tf.height + marginHeight;
				addChild(sp);
				//このコードではどのボタンを押してもテキストフィールドには5が表示される
				sp.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
						tf.text = "このボタンナンバーは " + i + "です。";
				});
			}
			//なぜならfor文を抜けたこの時点での変数iの中には5が入っているから。
			//(ボタンが押されてイベントリスナーが処理を始めるのは、時間的にみて
			// このコードよりももっと後なので、その時の変数iを探しにいってしまう)
        }
		private function createCircle(radius:Number = 10.0, color:uint = 0xFF0000):Sprite {
			var sp:Sprite = new Sprite();
			var g:Graphics = sp.graphics;
			g.beginFill(color);
			g.drawCircle(0, 0, radius);
			g.endFill();
			return sp;
		}
    }
}

実験結果はこう。

どのボタンを押しても5と表示されてしまいます。
僕が出してる解決方法は次の二通り。場合によって使い分けてます。

方法その1:イベントリスナに引数を渡してやる

// forked from hacker_ij9oiq_p's flash on 2010-2-11
package {
    import flash.display.Sprite;
	import flash.display.Graphics;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
    public class FlashTest extends Sprite {
		private var _tf:TextField;
        public function FlashTest() {
            _tf = new TextField;
			_tf.autoSize = TextFieldAutoSize.LEFT;
			_tf.text = "ここに数字が表示されます。";
			addChild(_tf);
			//直径
			var radius:Number = 10.0;
			//横の間隔
			var marginWidth:Number = 5.0;
			//縦の間隔
			var marginHeight:Number = 15.0;
			//ボタンの数
			var l:int = 5;
			for (var i:int = 0; i < l; i++) 
			{
				var sp:Sprite = createCircle(radius);
				sp.buttonMode = true;
				sp.x = ((sp.width + marginWidth) * i) + radius;
				sp.y = _tf.height + marginHeight;
				addChild(sp);
				//方法その1
				//イベントリスナーに引数を渡してしまう
				sp.addEventListener(MouseEvent.CLICK, onClickHandler(i));
				//ふつうはこれでいいんだけど、メソッドを増やしたくない夜もある…
			}
        }
		
		private function onClickHandler(idNumber:int):Function {
				return function(e:MouseEvent):void {
					_tf.text = "このボタンナンバーは " + idNumber + "です。";
				}
		}
		
		private function createCircle(radius:Number = 10.0, color:uint = 0xFF0000):Sprite {
			var sp:Sprite = new Sprite();
			var g:Graphics = sp.graphics;
			g.beginFill(color);
			g.drawCircle(0, 0, radius);
			g.endFill();
			return sp;
		}
    }
}

方法その2:クロージャーに閉じ込めてしまう

// forked from hacker_ij9oiq_p's flash on 2010-2-11
package {
    import flash.display.Sprite;
	import flash.display.Graphics;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
    public class FlashTest extends Sprite {
        public function FlashTest() {
            var tf:TextField = new TextField;
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.text = "ここに数字が表示されます。";
			addChild(tf);
			//直径
			var radius:Number = 10.0;
			//横の間隔
			var marginWidth:Number = 5.0;
			//縦の間隔
			var marginHeight:Number = 15.0;
			//ボタンの数
			var l:int = 5;
			for (var i:int = 0; i < l; i++) 
			{
				var sp:Sprite = createCircle(radius);
				sp.buttonMode = true;
				sp.x = ((sp.width + marginWidth) * i) + radius;
				sp.y = tf.height + marginHeight;
				addChild(sp);
				//方法その2
				//クロージャーに閉じ込めてしまう
				(function():void {
					var idNumber:int = i;
					sp.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {
							tf.text = "このボタンナンバーは " + idNumber + "です。";
					});
				})();
			}
			/*
			 * 参照ってややこしいですね。
			 * なぜこう動くのか、僕もきちんと説明できる自信がない…
			 */
        }
		private function createCircle(radius:Number = 10.0, color:uint = 0xFF0000):Sprite {
			var sp:Sprite = new Sprite();
			var g:Graphics = sp.graphics;
			g.beginFill(color);
			g.drawCircle(0, 0, radius);
			g.endFill();
			return sp;
		}
    }
}

いや〜便利便利。
僕もまだまだ勉強中の身ですので、他にもこういうやり方もあるよ!って方法あれば是非是非教えて下さい!

コメントを残す

メールアドレスが公開されることはありません。