아이폰이 널리 사용되어지면서 함께 많이 사용하게 된 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 의 집중분석이었으나 연속 포스팅으로 텐션이 떨어져서 소스 공유 정도로 마무리 하겠습니다.