Skip to content

ProcessIndicator for Starling

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

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

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

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

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

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

[as3] 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);
        }
    }

}

} [/as3]

결과물

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

샘플 소스

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