난 이 Gauge 컴포넌트의 소스를 보자마자 컴포넌트 학습용으로 정말 좋다고 판단했다. 그 이유는 다음과 같다.
각종 스킨 타입을 제작 및 적용하는 방법을 학습할 수 있다. Gauge 컴포넌트는 단순히 UIComponent에 그래픽 속성만 부여하여 만들어진 컴포넌트가 아닌 Skin 및 style을 이용하여 다양한 모양의 Gauge를 만들어낼 수 있도록 만들었다. Gauge컴포넌트는 총 3부분으로 구성된다. 바늘(Needle), 바늘커버(Cover), 프레임(Frame) 이 그것이다. 이 구성을 PNG,JPG,SWF와 같은 그래픽 스킨 또는 프로그램적으로 만들어진 스킨(ProgrammaticSkin ,Border , 및 RectangularBorder 등을 확장함)을 적용이 가능하도록 되어 있다.
기본 프로그램 스킨
그래픽 이미지 스킨 적용
다른 프로그램 스킨 적용
컴포넌트 확장 학습을 할 수 있다. 기본으로 만들어진 Gauge 컴포넌트는 바늘이 한개이다. 이를 확장하여 다음 그림과 같은 2개의 바늘을 가진 컴포넌트를 아주 쉽게 만들 수 있다.
Filter을 이용한 Grapic 학습에 도움을 준다. 아래 그림의 Gauge는 실제로 Graphics 이용해 그림을 그리고 나서 BevelFilter를 이용해 음영 표현을 한것과 하지 않은 것을 보여주고 있다.
BevelFilter를 사용했을 때
Filter를 사용하지 않았을 때
2. 선행학습
컴포넌트를 제작하기 위해 먼저 ActionScript를 이용한 확장 컴포넌트를 만드는 방법에 대해서 먼저 학습할 필요가 있다. 특별히 UIComponent를 확장해서 만들것이므로 Flexdocs.kr 에서 제공하는 "확장된 컴퍼넌트의 작성에 대해"를 먼저 학습하기 바란다.
그리고 선행적으로 학습해야할 것은 스킨을 적용하는 방법이다. 아래 내용에 대해서 어느정도 숙지하고 있어야 Gauge컴포넌트를 이해하는데 도움이 된다. 아래 링크를 참고하여 필요한 지식을 얻자!
Gauge 컴포넌트를 만든다. - 스타일, 이벤트 메타데이타 지정 - Skin 및 Rotate 이펙트 인스턴스 변수 선언 - 생성자 : 마우스 이벤트 Listener - value, maximum, minmum, liveDragging 속성 setter, getter 제작 - createChildren() : 스킨 인스턴스 생성 및 addChild, Rotate 이펙트 인스턴스 생성(target은 바늘이 된다.) - measure() : 기본 폭, 높이를 50으로 지정한다. - updateDisplayList() : 각 스킨의 위치 및 사이즈를 지정하고 값에 따라 바늘 각도에 따라 표시해줌, Cover의 그림자 효과 설정
Gauge예제에서는 Border 부모 클래스로 선택했다. 아래는 GaugeSkin 클래스의 기본골격이 되겠다.
위에서 두개의 메소드 updateDisplayList()와 borderMetrics()만 완성하면 되겠다. 먼저 borderMetircs() 메소드를 살펴보자. 이 메소드는 Border에 정의된 메소드로 재정의(override)해서 사용하면 된다. 경계선의 두께를 반환하면 되므로 스타일속성중 borderThickness값을 이용하면 되겠다.
스타일 속성으로 배경색,배경투명도,경계선색,경계선두께,경계선투명도,바늘색,바늘두께,바늘투명도,커버색,커버투명도를 설정하기로 했다. 이들의 각각의 이름을 다음과 같이 정한다.
backgroundColor : frame의 배경색
backgroundAlpha : frame의 투명도
borderColor : frame의 경계선색
borderAlpha : frame의 투명도
borderThickness : frame의 두께
needleColor : 바늘의 색
needleAlpha : 바늘의 투명도
coverColor : 바늘 커버의 색
coverAlpha : 바늘 커버의 투명도
위에서 지정한 스타일 변수명은 이 GaugeSkin을 addChild하는 부모 클래스에서 정의한 Skin 메타데이타를 참고하게 된다. 부모 클래스에서는 GaugeSkin을 frameSkin이라는 Style명으로 사용한다고 가정할 때 아래와 같이 GaugeSkin을 사용하게 된다.
위 코드의 중간에 newSkin는 GaugeSkin의 인스턴스가 되며 그 속성인 styleName에 this를 참조시키는 것을 볼 수 있다. 여기서 this는 부모 클래스를 의미한다. 이것을 하게 되면 GaugeSkin에서 getStyle()메소드를 통해 부모 클래스에서 설정한 Style명을 참고할 수 있다. 가령 위에 [Style(name="backgroundColor")]이 정의되어 있으므로 GaugeSkin에서 getStyle("backgroundColor")로 접근이 가능하다는 것이다.
작성된 updateDisplayList()메소드를 분석하자. 크게 두부분으로 나뉜다. Style값을 획득하는 부분과 Skin name에 따라서 그림을 그려주는 부분이다.
윗 부분을 살펴보면 아래와 같은 코드가 반복되는 것을 볼 수 있다. var bgColor:Number = getStyle("backgroundColor"); if( isNaN(bgColor) || !StyleManager.isValidStyleValue(bgColor)) bgColor = 0xFFFFFF;
이 부분은 부모클래스에서 정의한 Style명 backgroundColor 의 값을 getStyle()메소드로 읽고 있다. 하지만 부모에서 유효한 값을 사용하지 않았거나, 값이 지정되지 않았다면 bgColor를 기본값으로 0xFFFFFF로 해준다.
위 코드에서 아래 부분을 살펴보자. 아래 부분은 GaugeSkin의 name 속성의 값이 frameSkin, needleSkin, coverSkin 일때 서로 다른 그림을 그려준다. 잘 보면 name속성이 frameSkin인 경우 해당 Style속성인 backgroundColor, backgroundAlpha, borderThickness, borderColor, borderAlpha값을 이용하여 정해진 폭과 높이에 따라 타원모양을 배경에 그려주고 있다. 같은 방법으로 needleSkin인 경우 전체크기의 약 45%정도의 바늘크기를 가진 선을 그려주고 coverSkin일때는 바늘을 덮어줄 수 있도록 중심부분에 전체 폭,높이에 대해 약 1/4 크기의 타원형 cover를 그려준다. 프로그램 스킨에서 그림을 그리는 방법 및 적용방법에 대해서는 프로그램스킨>프로그램에 의한 묘화 를 참고하면 되겠다.
부모 클래스에서는 이 GaugeSkin을 3개 인스턴스화해서 각각 이름을 frameSkin, needleSkin, coverSkin으로 하여 addChild하게 된다.
5. 이벤트를 만들자.
만들어질 이벤트는 Gauge에서 마우스 이벤트가 발생했을때 현재값과 새로운 값을 담는 역할을 한다. 그 외에는 별 내용이 없으므로 소스만 확인해보자.
6. Gauge 컴포넌트를 만들자.
앞서 GaugeSkin의 부모인 Gauge의 약간 맛배기로 보았다. Gauge는 UIComponent를 확장하여 사용할 것이다. 6-1 Style 및 이벤트 메타데이터 지정 & needle 회전이펙트 및 Skin 인스턴스 참조 변수 선언
아래는 Gauge가 가져야 하는 기본 구조와 Style 및 Event 메타데이타를 정의한 코드이다. 앞서 지정한 GaugeSkin에 대한 Class를 참고해야하므로 Style 메타데이타에 frameSkin, coverSkin, needleSkin을 정의했다. type은 Class이다.
그리고 이미 지정한 Style의 이름과 type 및 format을 지정한다.
class에 private변수로 frameSkin, coverSkin, needleSkin을 IFlexDisplayObject로 선언했다. 앞서 메타데이터로 지정한 frameSkin, coverSkin, needleSkin 스타일에 대응하여 이 변수에 인스턴스가 생성되게 된다. 이 메타데이터는 우리가 앞서 정의한 GaugeSkin일 수 있고 이미지 또는 다른 Skin이 될 수 있겠다.
6-2 스킨적용
스킨을 addChild해야하므로 UIComponent의 createChildren() 메소드를 재정의해서 사용한다. 아래 코드에서 보면 이미 선언한 3개 스킨 변수에 createSkin()함수를 이용하여 인스턴스를 생성하는 것을 볼 수 있다. createSkin()메소드가 하는 일은 1번째 인자로 스킨이름으로 스킨이 이미 만들어져 있는지 검사후 만약 없으면 기본 스킨 클래스인 GaugeSkin 클래스를 적용하게 한다.
아래 프로그램에서 중요한 점은 바로 styleableSkin.styleName = this; 이다. styleName을 this로 안해주면 BaseSkin또는 다른 프로그램 스킨에서 Gause.as에서 정의한 스타일 메타데이타 값들을 getStyle()로 참조할 수 없다.
6-3 게이지 그리기
아래 코드를 보자. 앞서 인스턴스화하고 addChild 한 frameSkin, needleSkin, coverSkin의 배치하고 있다. 특별히 coverSkin인 경우 Style 속성중 coverDropShadowEnabled가 true일때 cover의 그림자 효과를 주고 있다. 또한 needleSkin인 경우 value값에 따라 회전하고 있으며 이때 회전효과를 주기 위해 Rotate 이펙트 인스턴스를 사용하는 것을 볼 수 있다.
여기서 중요한 부분은 바로 Skin들 자체가 컴포넌트화 되어서 사용되었다는 점이다. 더욱 생각해야할 부분은 이 스킨들이 우리가 만든 GauseSkin뿐 아니라 Gauge를 인스턴스화 할때 Style을 어떻게 해주냐에 따라 전혀 다른 모습을 보여줄 수 있다는 것이다.
그 외에 마우스 컨트롤하는 부분과 getter, setter 정의에 대한 설명은 생략한다. 여기서 설명하고자 하는 주제와 약간 벗어날 수 있기 때문이다. 이에 대해서는 개별적으로 소스를 분석해봤으면 한다.
7. GaugeSkin만 적용한 Gauge 컴포넌트를 사용해보자.
아래와 같이 Gauge를 테스트하는 프로그램을 만들었다. 첫번째 게이지는 어떤 스타일도 적용하지 않은 기본 게이지이다. 라이브 Drag가 되고 Drag 할때 두번째 게이지의 값과 연동되도록 했다. 두번째와 세번째는 인라인방법과 스타일시트를 이용한 방법을 이용해 스타일을 적용했다.
8. Embed 이미지를 frame과 cover에 적용해보기
아래와 같은 두개의 이미지를 준비하여 Embed하는 방법을 통해서 Gauge의 스킨을 적용할 수 있다. 그렇게 되면 frameSkin과 coverSkin에 GaugeSkin의 인스턴스를 대신하여 Embed된 이미지가 적용이된다. 물론 그렇게 할경우 최종적인 swf파일이 용량이 커지게 된다.
BitmapCover.png
BitmapFrame.png
9. 다이얼 형태의 프로그램 스킨 만들어보기
기본적으로 제공되는 GaugeSkin대신 Dial형태의 Skin을 만들어보겠다. 다이얼 형태는 아래 그림처럼 coverSkin과 needleSkin이 붙어있으므로 이중 한가지만 그림을 그리면 되는데, Rotate 이펙트가 needleSkin에 적용되므로 needleSkin만 그리면 되겠다. 무슨 말인지는 아래 해당 코드를 보면 쉽게 알 수 있을것이다.
DialSkin은 GaugeSkin과 다르게 updateDisplayList()에서 Gauge에서 정의된 Style속성을 전혀 사용하지 않고 단독적으로 색,굵기,투명도등을 가지고 있다. 즉, 외부에서 Style속성값을 적용시킬려고 해도 적용시킬 수 없다는 뜻이다. Style속성을 적용하기 원한다면 DialSkin을 GaugeSkin처럼 수정하면 되겠다.
또 특이한 점은 name이 framsSkin과 needleSkin일때만 그림을 그린다. coverSkin은 그림을 그리지 않고 있다. 즉, DialSkin에서는 coverSkin의 존재가 무의미하기 때문이다.
filters에 BevelFilter 를 쓰고 있기 때문에 DialSkin을 좀더 볼륨감있게 표현할 수 있다. 제작한 DialSkin은 아래와 같은 방법으로 ClassReference를 이용해 적용하면 되겠다.