ProcessIndicator for Starling

아이폰이 널리 사용되어지면서 함께 많이 사용하게 된 UI 중 하나가 ProcessIndicator 입니다. iOS 클래스 명으로는 UIActivityIndicatorView 가 바로 그것이죠.

이전에 사용하던 이 ProcessIndicator  를 Starling 용으로 만들어 보았습니다.  스탈링의 DisplayObject 와 기존 DisplayObject 는 거의 대동소이하므로 아무생각 없이 패키지만 스탈링으로 바꾸었습니다. 결과는…

잘 나오는데.. 어라 뭔가 이상하다~~~ 보통 이런 인디케이터의 경우 슬라이스의 각만큼 회전을 해야 합니다. 그런데 이 회전을 시키는 부분이 기존의 플래시와 스탈링이 약간 다릅니다. 기존의 rotation 은 360 즉 일반적인 각도로 회전하지만 스탈링의 rotation 은 라디안 값으로 회전합니다. 간단한거지만 아는데 반나절 걸렸음;;;; 그래서 원래 소스를 아래와 같이 변경 해야 합니다.

rotation = rotation + (360 / slices) % 360; // degree 로 계산
rotation = deg2rad(( rad2deg(rotation) + (360 / slices)) % 360); // radian 으로 계산

어떤분들은 degree 와 radian 을 왔다갔다 할 필요가 있는지 궁굼해 하 실 수 있는데. 네, 없습니다. 처음부터 기준을 radian 으로 나눠 주면 저렇게 복잡하게 할 필요는 없습니다. 다만 spinnerSkin 을 플래시의 Shape 으로 만들기 때문에 이때는 degree 기준으로 슬라이스를 만들어야 합니다. 결국 조삼모사~ (제가 바꾸기 귀잖아서 그런거 절대 아님.)

전체 소스는 아래와 같습니다.

package
{
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import starling.display.DisplayObjectContainer;
	import starling.display.Image;
	import starling.events.Event;
	import starling.textures.Texture;
	import starling.utils.deg2rad;
	import starling.utils.rad2deg;

	public class ProcessIndicator extends DisplayObjectContainer
	{
		private var _spinnerSkin:Image;

		private var timer:Timer;
		private var slices:int;
		private var radius:int;
		private var _image:Image;

		public function ProcessIndicator(slices:int = 12, radius:int = 6)
		{
			super();
			this.slices = slices;
			this.radius = radius;

			spinnerSkin = makeSpinner();

			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
		}

		private function onAddedToStage(event:Event):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
			timer = new Timer(1000/30);
			timer.addEventListener(TimerEvent.TIMER, onTimer, false, 0, true);
			timer.start();
		}

		private function onRemovedFromStage(event:Event):void
		{
			removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			timer.reset();
			timer.removeEventListener(TimerEvent.TIMER, onTimer);
			timer = null;
		}

		private function onTimer(event:TimerEvent):void
		{
			spinnerSkin.rotation = deg2rad(( rad2deg(spinnerSkin.rotation) + (360 / slices)) % 360);
		}

		private function makeSpinner():Image
		{
			var i:int = slices;
			var degrees:int = 360 / slices;
			var slider:flash.display.Sprite = new flash.display.Sprite;
			while (i--)
			{
				var slice:Shape = getSlice();
				slice.alpha = Math.max(0.2, 1 - (0.1 * i));
				var radianAngle:Number = (degrees * i) * Math.PI / 180;
				slice.rotation = -degrees * i;
				slice.x = (Math.sin(radianAngle) * radius)+radius*2;
				slice.y = (Math.cos(radianAngle) * radius)+radius*2;

				slider.addChild(slice);
			}

			var bitamp:BitmapData = new BitmapData(slider.width, slider.height, true, 0);
			bitamp.draw(slider);

			var texture:Texture = Texture.fromBitmapData(bitamp, false, false);
			var spinner:Image = new Image(texture);
			spinner.pivotX = spinner.width >> 1;
			spinner.pivotY = spinner.height >> 1;

			return spinner;
		}

		private function getSlice():Shape
		{
			var slice:Shape = new Shape();
			slice.graphics.beginFill(0x222222);
			slice.graphics.drawRoundRect(-1, 0, 2, radius, radius*2, radius*2);
			slice.graphics.endFill();

			return slice;
		}

		//=====================================================================
		//
		// getter & setter
		//
		//=====================================================================

		public function get spinnerSkin():Image
		{
			return _spinnerSkin;
		}

		public function set spinnerSkin(value:Image):void
		{
			if(_spinnerSkin == value)
			{
				return;
			}

			if(_spinnerSkin)
			{
				removeChild(_spinnerSkin);
			}

			_spinnerSkin = value;

			if(this._spinnerSkin && this._spinnerSkin.parent != this)
			{
				_spinnerSkin.visible = true;
				_spinnerSkin.touchable = false;
				addChild(_spinnerSkin);
			}
		}

	}
}

결과물

원래 이번 포스팅의 목적은 radian 과  degree 의 집중분석이었으나 연속 포스팅으로 텐션이 떨어져서 소스 공유 정도로 마무리 하겠습니다.

샘플 소스

[button url=”http://sewonist.com/wp-content/uploads/2013/03/ProcessIndicator.zip”]Download ProcessIndicator.zip[/button]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.