지난 2009년 12월 18일에 프로젝트 코드명 Squiggly(스퀴글리)가 Prerelease판으로 업데이트 되었다.
http://labs.adobe.com/technologies/squiggly/

Squiggly는 Flash Player 10, AIR 1.5이상에서 동작한다. 이 엔진은 ActionScript 3.0 기반이며 영어로 문장에서 사용된 단어를 영어사전의 그것과 비교하여 단어의 철자를 체크한다. 이번 업데이트는 TLF(Adobe Text Layout Framework)를 기반으로 하는 Flex 4 Spark 컴포넌트를 지원하도록 했다. Squiggly는 Flex Builder, Flash Builder, Flash CS4등 다양한 툴에서 사용할 수 있다. 단지 아쉬운 것은 영어단어만 체크한다는 점이다.


다음 페이지는 Squiggly 데모이다.
http://labs.adobe.com/technologies/squiggly/demo/

개발자는 언제든지 예제소스와 ASDoc을 아래 링크에서 받아볼 수 있다.


이것을 쓸지 모르겠지만 아무튼 알아두고 있어 나쁠 것도 없으니....

참고
http://adnaru.com/242

글쓴이 : 지돌스타(http://blog.jidolstar.com/640)

 

  

일본에 Spark 프로젝트가 있다. 이 프로젝트는 ActionScript Class Library를 오픈소스로 만들고 공유하는 것을 하나의 모토로 삼고 있는데 그 유명한 증강현실(FLARToolKit)을 Flash로 구현한 것도 이 프로젝트에서 진행된 것이다. 이외에도 너무 멋진 라이브러리가 이 프로젝트를 통해 만들어졌고 여기서 만들어진 몇몇 라이브러리를 실무에 적용하고 있다.

 
Flex 4에는 Spark로 구성된 새로운 컴포넌트들이 존재한다. 그런데 이 Spark가 바로 일본의 Spark 프로젝트의 이름에서 온것임이 확신된다. 즉, Flex 4의 Spark 컴포넌트는 많은 부분에서 일본이 관여했다는 것을 의미한다. 실제로 2009 Adobe MAX에서 Spark 패키지 설계에 나이 20정도 되는 젊은 일본 개발자와 관계가 있다는 것을 확인했다.

Spark 프로젝트로 일본은 Adobe의 큰 관심을 받게 되었고, 일본 RIA시장에 있어서 없어선 안될 정도로 크게 성장해 있는 상태이다. 아마 일본 RIA 분야에 있어서 자부심을 생기게 하는 훌륭한 프로젝트 팀이라고 생각한다.

이에 반해 한국은 이 정도로 활발히 진행되는 오픈 프로젝트가 전무하다. 이는 빨리빨리 주위의 한국의 개발 환경덕택에 개발자들이 다른 활동할 수 있는 여지가 없는 것이 가장 큰 문제겠고, 오픈 지향이 미덕으로 방향을 잡고 있는 마당에도 유독 소프트웨어는 폐쇄적인 것도 문제라고 생각한다. 또한 개발자들은 무슨 문서 작성하는 사람처럼 취급해 버리는 무지한 관리자들이 있는한 한국에서 일본과 같은 개방적이고 활발한 개발 커뮤니티나 오픈 소스, 오픈 프로젝트는 어려울 것이다. 당장 문제를 해결하고 이익만 추구하는 한국의 IT시장이 지속된다면 소프트웨어 개발자는 그저 노동만 하고 그에 대한 돈만 벌어가는 존재로 전락할 수 밖에 없다. 창의적인 개발자는 사라지고 그저 일만 받고 처리하는 수동적인 개발자만 늘어나게 될 것이고 결국 한 나라의 IT시장의 발전에 큰 저해요소로 작용할 것이 분명하다.

한국에도 일본 Spark 프로젝트와 같은 활발하고 멋진 팀이 자발적으로 시행될 수 있는 국가적, 기업적 지원이 적극적으로 이뤄지길 희망한다.

글쓴이 : 지돌스타(http://blog.jidolstar.com/573)

Tween은 속도가 관건입니다. 많은 오브젝트의 위치나 값을 어떻게 하면 빠르게 변경할 수 있을까에 대한 해결책을 제시하기 위해 그간 많은 Tween관련 라이브러리들이 쏟아져 나왔습니다. 지금까지 가장 빠르고 안정적으로 평가받는 TweenMax를 자주 사용했죠.

몇주전 속도적인 면에서 TweenMax를 뛰어넘은 BetweenAS3를 hika님으로부터 소개받았었죠.
BetweenAS3는 일본의 Spark 프로젝트 팀의  Yoshihiro Shindo라는 사람이 만들었습니다.

아래 링크는 BetweenAS3의 공식 홈페이지입니다.

위 링크로부터 소스를 다운로드 하거나 SVN으로 공유할 수 있고 각종 예시도 볼 수 있어서 실무에 언제든지 적용할 수 있을 정도입니다. Alpha버전이지만 거의 완벽해보이는군요.

도데체 얼마나 빠를까요? 다음 링크에서 테스트 해보세요. 속도만 비교해볼때 BetweenAS3가 압승입니다.

무슨 마술(?)을 부렸나 봅니다. ㅎㅎ
역시 일본 Spark 프로젝트 팀는 최고네요.

글쓴이 : 지돌스타(http://blog.jidolstar.com/587)
Flex 4에 대해서 자료를 모으고 공부하는 중에 매우 괜찮은 블로그를 찾았다.

http://www.hulstkamp.com/

이 사람 블로그에 가면 Flex 4기반 커스텀 컴포넌트를 만든 예제들이 정말 많다. 한동안 이 사람 블로그와 친해져야 겠다는 생각이 들었다. ㅎㅎ

올라온 자료중에 Gumbo(4.0.0.4932) 버전으로 만든 Knob Button 예제가 있었는데 Flex 4 SDK Beta버전이 정식으로 나오기 전이라 바로 실행할 수 없었다. 그래서 소스를 보면서 마이그레이션 작업을 해보았다. 아래 실행 예제는 바로 이 작업의 결과물이다.

위 프로그램은 Knob Button에 대한 데모이다. 마우스로 돌려볼 수 있다.


마이그레이션 하면서 Spark 기반 커스텀 컴포넌트를 제작하는 법에 더욱 익숙해질 수 있었다.

Flex 3의 Halo 기반 컴포넌트는 스킨이 그래픽 기반만 지원했는데 Flex 4의 Spark기반 컴포넌트들은 스킨이 그래픽적 요소 뿐 아니라, 상태변화(state), 다른 컴포넌트 배치, 데이타 표현등이 된다. 또한 FXG를 지원하고 MXML형태로 스킨을 만들 수 있기 때문에 그 확장력이 무궁무진하게 되었다. Halo 컴포넌트의 경우에는 상태변화 바꾸거나 내부 컴포넌트 배치만 달라져도 컴포넌트 자체를 확장해서 다시 만들어야 하는 불편함이 있었지만 Spark컴포넌트는 큰 변경없이 커스텀 스킨만 제작하는 것만으로도 충분히 해결할 수 있게 되었다. 이 내용은 여기에 올려놓은 소스 또는 "Spark DropDownList 사용하기"를 보면 알 수 있을 것이다.

Spark 컨테이너의 경우에는 Layout까지 동적으로 변경할 수 있도록 만들어져서 언제든지 다른 Layout으로 바꿀 수 있고 또는 커스텀 Layout를 만들어 쓸 수도 있다. 반면 Halo 기반 컨테이너는 컨테이너의 Layout을 바꾸기 위해 기존 컨테이너를 다시 확장하거나 새로 만들어야만 했다. 이 내용에 대해서는 "Spark 컨테이너의 Layout, Scrolling, Viewport 소개"를 읽어보길 바란다.

CSS는 정말 획기적으로 많이 추가 되었다. 기존 Class, Type Selector밖에 없었는데 Flex 4로 넘어오면서 ID, Descendant, Pseudo, Mutiple Class등 다양한 Selector가 추가되었다. 이에 대한 글은 "Flex 4의 CSS"를 참고하길 바란다.

아래는 위 프로그램의 소스이다. 참고바란다.


개발 환경은 Flash Builder 4 Beta 1 이다. Flash Builder는 아래 링크에서 다운 받을 수 있다.

Flex 4는 Flex 3의 컨테이너의 Scrolling 기능보다 더욱 강화되고 직관적으로 바뀌었다. 여기에 Layout, Viewport라는 개념까지 포함되어 더욱 다루기 편하도록 만들어졌다. 이 글은 Flex 4 Spark 컨테이너의 Layout, Scrolling, Viewport에 대해서 소개한다.

아래는 Flash Builder 4 환경에서 작업했다. Flash Builder 4는 아래 링크를 통해 다운받을 수 있다.

http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026  

Spark 컨테이너의 Layout

 

Flex 3에서 Halo 기반 컨테이너는 Canvas, Box, HBox, VBox, Tile등이 있었지만 Flex 4의 Spark 기반 컨테이너는 모두 Group 하나로 통일되었고 layout 속성을 통해 BasicLayout, HorizontalLayout, VerticalLayout, TileLayout을 설정하여 Flex 3의 Canvas, HBox, VBox, Tile역할을 할 수 있도록 했다. Flex 4 Spark 기반 컨테이너는 이와 같이 동적으로 Layout을 바꿀 수 있도록 만들어졌기 때문에 좀 더 유연한 컨테이너 환경을 만들 수 있다.

 

아래는 위 프로그램의 소스이다.

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
	xmlns:fx="http://ns.adobe.com/mxml/2009" 
	xmlns:s="library://ns.adobe.com/flex/spark" 
	xmlns:mx="library://ns.adobe.com/flex/halo" 
	minWidth="300" minHeight="300">
	<s:layout>
		<s:VerticalLayout/>
	</s:layout>
	<fx:Script>
		<![CDATA[
			import spark.layouts.TileLayout;
			import spark.layouts.VerticalLayout;
			import spark.layouts.HorizontalLayout;
		]]>
	</fx:Script>
	
	<s:DropDownList id="ddl" requiresSelection="true" labelField="label">
		<s:dataProvider>
			<s:ArrayList>
				<fx:Object label="TileLayout" layout="{new TileLayout()}"/>
				<fx:Object label="HorizontalLayout" layout="{new HorizontalLayout()}"/>
				<fx:Object label="VerticalLayout" layout="{new VerticalLayout()}"/>
			</s:ArrayList>
		</s:dataProvider>
	</s:DropDownList>
		
	<s:Group>
		<s:layout>{ddl.selectedItem.layout}</s:layout>
		<s:Button label="1"/>
		<s:Button label="2"/>
		<s:Button label="3"/>
		<s:Button label="4"/>
		<s:Button label="5"/>
		<s:Button label="6"/>
		<s:Button label="7"/>
		<s:Button label="8"/>
		<s:Button label="9"/>
		<s:Button label="10"/>
	</s:Group>

</s:Application>

 

Spark 컨테이너의 Scrolling

 

Flex 3의 모든 컨테이너는 Scroller기능이 내장되어 있었는데 반해 Flex 4 Spark 기반 컨테이너는 기본적으로 Scroller 기능이 없다. 그래서 컨테이너는 더욱 가벼워졌고 필요할 때 ScrollBar를 Group 태그 밖으로 감싸주기만 하는 것으로 스크롤을 추가할 수 있도록 되었다.

 

 

아래는 앞선 소스에서 Scroller만 추가한 것이다.

<s:Scroller width="200" height="100" left="1" right="1" top="1" bottom="1">
	<s:Group>
		<s:layout>{ddl.selectedItem.layout}</s:layout>
		<s:Button label="1"/>
		<s:Button label="2"/>
		<s:Button label="3"/>
		<s:Button label="4"/>
		<s:Button label="5"/>
		<s:Button label="6"/>
		<s:Button label="7"/>
		<s:Button label="8"/>
		<s:Button label="9"/>
		<s:Button label="10"/>
	</s:Group>
</s:Scroller>

 

Flex 4 Spark 컨테이너(List, Group등)는 이렇게 쉽게 Layout을 임의대로 바꿀 수 있고 Scroller도 쉽게 배치할 수 있도록 되어 있다. Flex 4 Spark 기반 컨테이너는 Flex 3의 Halo 컨테이너보다 더욱 사용하기 간편하고 직관적이며 활용도가 높다는 것을 해본 사람은 알 수 있을 것이다.

 

 

Spark 컨테이너의 Viewports

 

Spark 컨테이너에서 들어간 재미있으면서 유용한 개념이 들어갔는데 Viewport 라는 것이다. Viewport는 말그대로 보여지는 영역이다. 아래 그림만 봐도 Viewport의 의미를 쉽게 이해할 수 있을 것이다.

 

출처 : http://hansmuller-flex.blogspot.com/2009/06/introduction-to-viewports-and-scrolling.html

 

방금 설명했던 Scroller는 시각적으로 보이는 horizontalScrollbar, verticalScrollbar와 ViewPort의 horizontalScrollPosition, verticalScrollPosition 속성의 값을 묶어(Bind)준다. 그러므로 어느 한쪽이 조절되면 다른 한쪽이 변경되어지게 된다. 백문의 불여일타.... 아래 예제만 봐도 이게 무슨 말인지 바로 알게 된다.

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- 출처 :http://hansmuller-flex.blogspot.com/2009/06/introduction-to-viewports-and-scrolling.html-->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/halo">
    <s:Group>
        <s:Group left="20" top="1" width="110" height="160">
            <s:Scroller id="scrollbar" left="2" right="2" top="2" bottom="2">
                <s:Group id="vp" horizontalScrollPosition="57" verticalScrollPosition="198">
                    <mx:Image source="http://sites.google.com/site/hansmuller/Home/archive/gyro-original.jpg"/>
                </s:Group>
            </s:Scroller>
            <s:Rect left="0" right="0" top="0" bottom="0">
                <s:stroke>
                    <s:SolidColorStroke weight="1" color="0xD8D8D8"/>
                </s:stroke>
            </s:Rect>
        </s:Group>
        <s:SimpleText horizontalCenter="10" top="175" text="viewport.width = {vp.width}"/>
        <s:SimpleText verticalCenter="-10" rotation="-90" text="viewport.height = {vp.height}"/>                
    </s:Group>

    <s:VGroup left="140" top="10" gap="15">
        <s:SimpleText text="viewport.contentWidth = {vp.contentWidth}"/>
        <s:SimpleText text="viewport.contentHeight = {vp.contentHeight}"/>
        <s:SimpleText text="viewport.horizontalScrollPosition = {vp.horizontalScrollPosition}"/>
        <s:SimpleText text="viewport.verticalScrollPosition = {vp.verticalScrollPosition}"/>
    </s:VGroup>

</s:Application>

 

Spark 컨테이너인 Group은 GroupBase를 상속받고 GroupBase는 IViewport 인터페이스를 구현하고 있다. Scroller는 이 IViewport과 바인딩되어 있다는 것을 이해하는 것이 중요하다.

 

    public interface IViewport extends IVisualElement
    {
        function get width():Number;
        function get height():Number;
        function get contentWidth():Number;
        function get contentHeight():Number;
        function get horizontalScrollPosition():Number;
        function set horizontalScrollPosition(value:Number):void;
        function get verticalScrollPosition():Number;
        function set verticalScrollPosition(value:Number):void;
        function getHorizontalScrollPositionDelta(scrollUnit:uint):Number;
        function getVerticalScrollPositionDelta(scrollUnit:uint):Number;
        function get clipAndEnableScrolling():Boolean;
        function set clipAndEnableScrolling(value:Boolean):void;
    }

 

그러므로 IViewport 인터페이스를 구현한 GroupBase, Group, DataGroup, RichEditableText, SkinnableContainer, SkinnableDataContainer 등은 모두 Scroller를 사용할 수 있게 되는 것이다.

 

위 소스에서 사용자가 Scroller를 컨트롤 하지 않고 Viewport를 컨트롤하여 스크롤바를 움직이게 할 수 있다.

 

위 프로그램은 앞선 소스에서 아래 코드를 추가하면 된다

<s:VGroup top="196" gap="15" x="20">
	<s:HGroup>
		<s:SimpleText text="HScroll Controller"/>    		
		<s:HSlider id="hSlider" 
			liveDragging="true"
			minimum="0" 
			maximum="{vp.contentWidth-vp.width}" 
			value="{vp.horizontalScrollPosition}" 
			change="vp.horizontalScrollPosition=hSlider.value"/>
	</s:HGroup>

	<s:HGroup>
		<s:SimpleText text="VScroll Controller"/>    		
		<s:HSlider id="vSlider" 
			liveDragging="true"
			minimum="0" 
			maximum="{vp.contentHeight-vp.height}" 
			value="{vp.verticalScrollPosition}" 
			change="vp.verticalScrollPosition=vSlider.value"/>
	</s:HGroup>
</s:VGroup>

 

이 코드가 의미하는 바는 사용자가 ScrollBar를 직접 컨트롤하지 않아도 Viewport를 통해 ScrollBar를 프로그램적으로도 컨트롤 할 수 있다는 것을 보여주고 있다.

 

(위 프로그램에서 Slider에 Viewport의 위치 값이 초반에 제대로 업데이트 안되는 이유는 이미지 로딩시점과 관련되어 있다. 이미지 로딩 뒤에 Viewport의 위치를 변경하면 아마도 문제가 해결될 것이므로 참고바란다. )

 

 

참고내용

Spark Layouts with Flex 4 Beta

Introduction to Viewports and Scrolling in Gumbo

 

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/568)

Flex 4의 Spark 계열 DropDownList는 List를 확장한 컴포넌트이다. Flex 3에서 ComboBox와 유사하게 사용할 수 있다.

 

DropDownList는 DropDownBase대신 List를 서브클래스로 한 것이 커다란 변화이다. 이렇게 된데에는 Iwo Banas라는 사람이 기존 Gumbo의 FxComboBox을 더욱 확장이 가능하고 내부적으로 독립화된 구조의 필요성에 대한 제안을 반영한 결과이다. 이에 따라 DropDownList는 dropDown 스킨 부분을 더이상 가지지 않고 List의 dataGroup 스킨을 사용하게 되었다.

 

DropDownList 컴포넌트의 기능 및 디자인 스펙을 보고 싶다면 아래 글을 보기 바란다. 이 글만 잘봐도 DropDownList에 내부구현 및 사용방법에 대해 어느정도 이해할 수 있을 것이라 생각한다.

 

http://opensource.adobe.com/wiki/display/flexsdk/Spark+DropDownList

 

또한 API에 대한 자세한 내용은 아래 글을 참고한다.

http://livedocs.adobe.com/flex/gumbo/langref/spark/components/DropDownList.html

 

DropDownList를 사용하는 방법은 Peter deHaan의 블로그만 봐도 쉽게 학습할 수 있다.

나는 이 블로그에서 DropDownList를 언급한 글들만 골라서 사용해 보았다. Flex 3와 달라 약간 생소하지만 놀라운 스킨 적용의 확장성에 매력을 느끼게 될 것이다. 아래에서 소개하는 4개의 큰 제목을 누르면 해당 포스트로 이동한다.

 

Setting a content background color on a Spark DropDownList control in Flex Gumbo

 

이 예제는 DropDownList를 이용하는 매우 간단한 예제로 DropDownList의 DropDown되는 리스트의 배경색을 바꾸는 것이다.

 

 

Setting the symbol color on the Spark DropDownList control in Flex 4

 

 

이 예제도 그 전 예제와 비슷한 난이도의 예제로 DropDownList의 Symbol의 색을 변경하는 예제이다.

 

 

Creating a tile layout Spark DropDownList control in Flex Gumbo

 

 

이 예제는 DropDownList의 커스텀 스킨을 적용하는 방법을 보여주고 있다. 블로그에 있는 커스텀 스킨은 Gumbo버전이라 작동을 잘 안하는데 스킨은 아래 것을 받아 해보길 바란다. Peter deHaan의 예제에서 PopUp을 사용했는데 Flex 4부터는 PopUpAnchor로 바뀌었다.

 

List에 보여지는 아이템이 원래 DropDownList의 기본 Skin인 spark.skins.DropDownListSkin과 달라진 점은 PopupAnchor의 DataGroup를 VerticalLayout으로 지정하는데 여기서 사용한 Layout은 HorizontalLayout이라는 점이 다르다. 게다가 위치도 아래표시가 아니라 우측에 표시되도록 했다. 이처럼 스킨부분에서 모든 Layout을 수정할 수 있도록 한 것이 Flex 4 컴포넌트의 컨셉이다. 이것은 Flex 3의 단점을 잘 극복한 부분중에 하나이다.

 

조금 더 재미있게 바꿔보자. Application과 DropDownSkin을 아래 코드로 바꿔보자.

 

 

스킨만 바꿨을 뿐인데...(갑자기 어느 광고가 생각남.. ㅋ)

위 프로그램 처럼 DropDownList의 스킨을 바꿀 수 있다. 그것도 아주 쉽게~~

 

 

Displaying images in a Spark DropDownList control in Flex Gumbo

 

방금 예제에서 DropDownList의 스킨을 바꾸는 것을 보여주었다. 그럼 DropDownList에 이미지도 보여줄 수 있을까? 당근 아주아주 쉽게 할 수 있다.

 

 

여기에서 사용된 BitmapImage는  spark.primitives.* 패키지에 포함되는 것으로 Group을 확장한 Graphic내에 넣을 수 있다. Graphic에 넣을 수 있는 것중에는 <Rect>, <Path>, <Ellipse>등도 포함한다. 관련 내용은 아래 링크를 참고한다.

 

http://livedocs.adobe.com/flex/gumbo/langref/spark/primitives/package-detail.html

 

 

정리하며

지금까지 Flex 4의 Spark DropDownList에 대해서 살펴보았다. Flex 3와 달리 Flex 4에서는 스킨 적용시 ActionScript 3.0 기반이 아닌 MXML이라 직관적이고 편리하게 할 수 있다는 것이 큰 매력이였다. 또한 기능도 매우 확대되었다. Spark 컴포넌트라면 이와 같은 방법으로 스킨을 적용할 수 있다는 것을 이해하는 것이 중요하겠다.


테스트 환경 : Flash Builder 4 + Flex SDK 4

Flash Builder 는 아래 링크에서 다운 받자.

http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026 

글쓴이 : 지돌스타(http://blog.jidolstar.com/567)

Flex 4로 넘어오면서 생소하게 느꼈던 부분 중에 하나가 바로 자식(Child) 관리 메소드들의 변화가 아닌가 생각한다. 바뀐 부분이 무엇이고 왜 바뀌었는지 알아보도록 하겠다.

 

Flex 3에서 Canvas, Box 등의 컨테이너는 모두 mx.core.Container 클래스를 확장해서 만들어졌다. 이 Container 클래스는 flash.display.DisplayObjectContainer에서 정의되어 있는 자식관리 메소드인 numChildren, addChild(), addChildAt(), removeChild(), removeChildAt() 등을 그대로 Override(재정의) 해서 사용했다. 그런데 이렇게 재정의 한것이 문제가 컸다. Flex 3의 Container는 DisplayObjectContainer의 메소드를 그대로 사용하므로 자식은 모두 flash.display.DisplayObject로 받게 된다. 하지만 실제 자식으로 사용할 수 있는 것은 IUIComponent를 구현한 클래스만 자식으로 받을 수 있다. 즉 UIComponent만 자식으로 받을 수 있는 것이다. 아래 코드는 Flex 3의 Container에서 자식을 추가할 때 내부적으로 addingChild()메소드가 호출되는데 거기에 있는 코드이다.

var uiChild:IUIComponent = IUIComponent(child);

 

Flex 3에서 addChild()를 통해 받는 자식은 DisplayObject인데 런타임시 사용될 수 있는 컴포넌트는 IUIComponent이니 실제 Flex 3를 이용해서 개발하는 사람들은 오용할 소지가 크게 된 것이다. 나도 Container에 Sprite 객체를 넣으려고 하다가 안되서 애먹은 적이 있었는데 바로 이것때문이다.

 

Flex 4부터는 이 문제를 말끔히 없애고자 DisplayObjectContainer의 자식 관리 메소드를 재정의 하지 않고 다른 메소드를 만들었다. 그 다른 메소드는 무엇일까?

 

Flex 4에서는 아래와 같이 IVisualElementContainer 인터페이스를 정의했다. 이 인터페이스는 Flex 4의 모든 Spark, Halo 컨테이너에서 모두 구현하고 있다. 보면 알겠지만 addChild대신 addElement, removeChild대신 removeElement로 된 것이다. 결국 Child 대신 Element를 사용한거다.

package mx.core
{
public interface IVisualElementContainer
{
    function get numElements():int;
    function getElementAt(index:int):IVisualElement
    function addElement(element:IVisualElement):IVisualElement;
    function addElementAt(element:IVisualElement, index:int):IVisualElement;
    function removeElement(element:IVisualElement):IVisualElement;
    function removeElementAt(index:int):IVisualElement;
    function removeAllElements():void;
    function getElementIndex(element:IVisualElement):int;
    function setElementIndex(element:IVisualElement, index:int):void;
    function swapElements(element1:IVisualElement, element2:IVisualElement):void;
    function swapElementsAt(index1:int, index2:int):void;

}
}

 

위 인터페이스는 정확히 무엇을 의미하는가? 자식 추가할 때 이제 더이상 DisplayObject가 아니라는 것에 주목하자. DisplayObject대신 IVisibleElement를 관리하고 있다. 이것이 핵심이다. 즉 이제 런타임시가 아닌 컴파일 단계서부터 원천적으로 컨테이너에 자식들은 모두 IVisibleElement을 구현한 것만 다루겠다는 것이다. Flex 4는 Flex 3와 다르게 모든 컨테이너는 IVisibleElementContainer를 구현한 것이다. 그리고 mx.core.UIComponent는 IVisibleElement를 구현했다. 이로서 컨터이너에 붙는 자식들을 더욱 명확하게 사용할 수 있게 되었다.

 

그럼 addChild, addChildAt은 어떻게 했을까? 아래 코드는 Spark 컨테이너의 SkinnableComponent와 GroupBase에 재정의된 것이다. 즉 이들 메소드를 사용하면 런타임시 예외처리를 해준다. 이 클래스를 확장해서 만든 Spark 계열의 컨테이너인 SkinnableContainer, SkinnableDataContainer, Group, DataGroup, Panel 전부 이렇게 동작한다.

    /**
     *  @private
     */
    override public function addChild(child:DisplayObject):DisplayObject
    {
        throw(new Error(resourceManager.getString("components", "addChildError")));
    }
    
    /**
     *  @private
     */
    override public function addChildAt(child:DisplayObject, index:int):DisplayObject
    {
        throw(new Error(resourceManager.getString("components", "addChildAtError")));
    }

 

하지만 기존 Halo 기반의 컨테이너(mx.core.Container)는 addChild(), removeChild() 사용시 예외처리를 해두지 않았다. 물론 IVisualElementContainer는 구현했지만 말이다. 이는 기존 Flex 3 Halo 기반의 컴포넌트와의 호환성 문제 때문에 그리 한 것이라 판단한다.

 

Flex 4 문서를 보면 되도록이면 Halo보다 Spark 기반의 컴포넌트와 컨테이너를 사용할 것을 권장하고 있다. 지금까지 소개한 내용도 그렇게 권장하는 이유중에 하나일 것이다.

 

Flex SDK 개발자들이 Flex 2 시절 전부터 이런 문제를 알고 있었을 텐데 왜 지금에서야 바꿨는지... ㅎㅎㅎ

 

 

참고글

Spark Group - Functional and Design Specification

 

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/559)

 

 

InsideRIA에 올라온 Getting started with Spark skins 의 글을 읽고 따라해보았다.

 

아래는 Skin 소스이다. com.jidolstar.skins.MyButtonSkin으로 이름을 지었다.

<?xml version="1.0" encoding="utf-8"?>
<s:Skin 
	xmlns:fx="http://ns.adobe.com/mxml/2009" 
	xmlns:s="library://ns.adobe.com/flex/spark" 
	xmlns:mx="library://ns.adobe.com/flex/halo" 
	>
	<fx:Metadata>
		[HostComponent("spark.components.Button")]
	</fx:Metadata>
	
	<s:states>
		<mx:State name="up"/>
		<mx:State name="down"/>
		<mx:State name="over"/>
		<mx:State name="disabled"/>
	</s:states>
	
	<s:filters>
		<s:DropShadowFilter quality="3"
			alpha="0.5" alpha.over="0.3"
			distance="5" distance.over="15"
			blurX.over="15" blurY.over="15"
			inner.down="true"
		/>
	</s:filters>
	
	<s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5">
		<s:fill>
			<mx:LinearGradient rotation="90">
				<mx:entries>
					<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
					<mx:GradientEntry color="#84a381" ratio="0.50" ratio.over="0.25"/>
					<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
				</mx:entries>
			</mx:LinearGradient>
		</s:fill>
	</s:Rect>
	
	<s:SimpleText id="labelElement" color="#ffffff"
		horizontalCenter="0" verticalCenter="0"
     	left="10" right="10" top="5" bottom="5"
     />	
	
	<s:layout>
		<s:BasicLayout/>
	</s:layout>
</s:Skin>

 

아래는 위 Skin을 사용한 소스이다.

<?xml version="1.0" encoding="utf-8"?>
<s:Application 
	xmlns:fx="http://ns.adobe.com/mxml/2009" 
	xmlns:s="library://ns.adobe.com/flex/spark" 
	xmlns:mx="library://ns.adobe.com/flex/halo" 
	minWidth="500" minHeight="400"
	>
	<s:Button label="확인" skinClass="com.jidolstar.skins.MyButtonSkin"/>
</s:Application>

 

코딩하면서 느끼는 것이지만 Flex 4는 Flex 3하고 너무 다르다. ㅎㅎ  

스킨 부분에 있어서 Flex 3에서는 ProgrammaticSkin을 기반으로 Skin을 ActionScript 코드로 만들었는데 Flex 4부터는 MXML로 만들 수 있도록 했다. 이 부분이 가장 크게 달라진거다.

 

위의 Skin 코드를 살펴보면 몇가지 재미있는 점을 발견할 수 있다. 살펴보자.

 

먼저 HostComponent 메타 데이타를 볼 수 있다. Flex 4에서 나온 새로운 메타 데이타이다. 이것은 이 스킨이 어떤 컴포넌트가 Host인가 명시적으로 지칭해주는 역할을 한다. Button은 ButtonBase를 확장해서 만들었고 ButtonBase는 SkinnableComponent를 확장해서 만들어졌는데 HostComponent가 쓰이는 부분은 SkinnableComponent인 듯 보인다. 아래는 SkinnableComponent내에 정의된 attachSkin() 메소드이다. 이것은 createChildren() 혹은 commitProperties()가 호출될때 이 메소드가 호출되는데 skinClass 스타일이 바뀔 때 Skin을 붙여주는 역할을 담당한다.

 

    /**
     *  Create the skin for the component. 
     *  You do not call this method directly. 
     *  Flex calls it automatically when it calls createChildren() or  
     *  the UIComponent.commitProperties() method.
     *  Typically, a subclass of SkinnableComponent does not override this method.
     * 
     *  

This method instantiates the skin for the component, * adds the skin as a child of the component, and * resolves all part associations for the skin

* * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ protected function attachSkin():void { // Factory var skinClassFactory:IFactory = getStyle("skinFactory") as IFactory; if (skinClassFactory) setSkin( skinClassFactory.newInstance() as Skin ); // Class if (!skin) { var skinClass:Class = getStyle("skinClass") as Class; if (skinClass) setSkin( new skinClass() ); } if (skin) { skin.owner = this; // As a convenience if someone has declared hostComponent // we assign a reference to ourselves. If the hostComponent // property exists as a direct result of utilizing [HostComponent] // metadata it will be strongly typed. We need to do more work // here and only assign if the type exactly matches our component // type. if ("hostComponent" in skin) { try { Object(skin).hostComponent = this; } catch (err:Error) {} } // the skin's styles should be the same as the components skin.styleName = this; super.addChild(skin); skin.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, skin_propertyChangeHandler); } else { throw(new Error(resourceManager.getString("components", "skinNotFound", [this]))); } findSkinParts(); invalidateSkinState(); }

위 코드에 보면 hostComponent 부분이 보이는데 바로 HostComponent 메타데이터와 관련이 있어 보인다. 아직 정확한 동작방식은 모르겠다.

 

주제를 바꿔서 맨 위 코드에 <s:state>가 존재하는 것을 볼 수 있다. 이것은 상태를 의미하는 것으로 이미 Button 자체에 up, down, over, up 상태가 메타 데이터 형태로 정의 되어 있다. . ButtonBase에 보면 아래와 같은 방식으로 4개의 상태를 나타내는 메타 데이터가 정의되어 있다.

/**
 *  Up State of the Button
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("up")]

/**
 *  Over State of the Button
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("over")]

/**
 *  Down State of the Button
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("down")]

/**
 *  Disabled State of the Button
 *  
 *  @langversion 3.0
 *  @playerversion Flash 10
 *  @playerversion AIR 1.5
 *  @productversion Flex 4
 */
[SkinState("disabled")]

public class ButtonBase extends SkinnableComponent implements IFocusManagerComponent
{
   ...
}

 

저 상태변화는 SkinnableComponent 클래스에서 알아서 처리하도록 되어 있다. Button의 스킨의 상태에 따른 변화의 동작방식을 이해하기 위해 SkinnableComponent를 분석해야한다. 뭐, 분석까지 아니더라도 SkinnableComponent를 활용하는 방법만 알면 될 것이다. SkinnableComponent는 UIComponent를 확장한 것이고 모든 Spark 계열의 컴포넌트는 SkinnableComponent를 확장한 형태이다. ColorPicker나 DateChooser와 같이 예전 Halo 계열의 컴포넌트는 SkinnableComponent를 확장하지 않았다.

 

<s:filters>는 말그대로 필터를 설정하는 것을 의미한다. 여기서는 DropShadowFilter를 사용했다. Flex 4에서 추가한 새로운 기능중에 각 속성에 state를 지정할 수 있다. DropShadowFilter를 사용할 때도 아래 처럼 속성에 이 기능을 적용했다.  

 

alpha="0.5" alpha.over="0.3"

 

DropShadowFilter를 사용한 부분 중에 alpha값을 위처럼 사용했다. 보통때는 alpha값이 0.5이지만 상태가 over이면 0.3을 사용하겠다는 것을 의미한다. alpha.over="0.3" 부분이 어떻게 ActionScript 로 바뀌는가 살펴보려면 컴파일 옵션에 -keep-generated-actionscript를 해보면 알 수 있다. 그럼 컴파일을 하면 src/generated 폴더가 생성된다. 그 안에 com.jidolstar.skins 패키지로 가보면 MyButtonSkin-generated.as 파일이 있는데 열어보면 alpha.over="0.3" 가 어떻게 바뀌는가 한눈에 알 수 있다. 아래 코드는 MyButtonSkin-generated.as의 일부분이다.

 

    /**
     * @private
     **/
    public function MyButtonSkin()
    {
        super();
        // our style settings

        // properties
        this.filters = [_MyButtonSkin_DropShadowFilter1_i()];
        this.layout = _MyButtonSkin_BasicLayout1_c();
        this.mxmlContent = [_MyButtonSkin_Rect1_c(), _MyButtonSkin_SimpleText1_i()];
        this.currentState = "up";


        // events
		states = [
		  new State ({
		    name: "up",
		    overrides: [
		    ]
		  })
		  ,
		  new State ({
		    name: "down",
		    overrides: [
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_DropShadowFilter1",
		        name: "inner",
		        value: true
		      })
		    ]
		  })
		  ,
		  new State ({
		    name: "over",
		    overrides: [
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_DropShadowFilter1",
		        name: "alpha",
		        value: 0.3
		      })
		      ,
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_DropShadowFilter1",
		        name: "distance",
		        value: 15
		      })
		      ,
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_DropShadowFilter1",
		        name: "blurX",
		        value: 15
		      })
		      ,
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_DropShadowFilter1",
		        name: "blurY",
		        value: 15
		      })
		      ,
		      new mx.states.SetProperty().initializeFromObject({
		        target: "_MyButtonSkin_GradientEntry2",
		        name: "ratio",
		        value: 0.25
		      })
		    ]
		  })
		  ,
		  new State ({
		    name: "disabled",
		    overrides: [
		    ]
		  })
		];
    }

private function _MyButtonSkin_DropShadowFilter1_i() : spark.filters.DropShadowFilter
{
	var temp : spark.filters.DropShadowFilter = new spark.filters.DropShadowFilter();
	temp.quality = 3;
	temp.alpha = 0.5;
	temp.distance = 5;
	_MyButtonSkin_DropShadowFilter1 = temp;
	return temp;
}

 

결국 alpha.over="0.3"는 State 객체를 만들어 각 상태에 따른 속성으로 지정해버리도록 만들어진다. currentState를 "up"으로 설정하고 상태가 바뀔 때마다 등록된 State를 실행하도록 되는 것이다. 이걸 보니깐 원리가 확실히 이해가 된다.

 

다시 돌아가서....

<s:Rect>과 <s:SimpleText>는 mxmlContent=[_MyButtonSkin_Rect1_c(), _MyButtonSkin_SimpleText1_i()] 의 형태로 추가 된다. mxmlContent도 Flex 4부터 추가된 속성인데 생소하기 짝이 없다. ㅎㅎ 좀 알아보기 위해 또 뒤져봤다.

 

//----------------------------------
//  mxmlContent
//----------------------------------

private var mxmlContentChanged:Boolean = false;
private var _mxmlContent:Array;

[ArrayElementType("mx.core.IVisualElement")]

/**
 *  The visual content children for this Group.
 *
 *  

The content items should only be IVisualItem objectss. * An mxmlContent Array should not be shared between multiple * Group containers because visual elements can only live in one container * at a time.

* *

If the content is an Array, do not modify the Array * directly. Use the methods defined by Group class instead.

* * @default null * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get mxmlContent():Array { if (_mxmlContent) return _mxmlContent.concat(); else return null; } /** * @private */ public function set mxmlContent(value:Array):void { if (createChildrenCalled) { setMXMLContent(value); } else { mxmlContentChanged = true; _mxmlContent = value; // we will validate this in createChildren(); } } /** * @private * Adds the elements in mxmlContent to the Group. * Flex calls this method automatically; you do not call it directly. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ private function setMXMLContent(value:Array):void { var i:int; // if there's old content and it's different than what // we're trying to set it to, then let's remove all the old // elements first. if (_mxmlContent != null && _mxmlContent != value) { for (i = _mxmlContent.length - 1; i >= 0; i--) { elementRemoved(_mxmlContent[i], i); } } _mxmlContent = (value) ? value.concat() : null; // defensive copy if (_mxmlContent != null) { var n:int = _mxmlContent.length; for (i = 0; i < n; i++) { var elt:IVisualElement = _mxmlContent[i]; // A common mistake is to bind the viewport property of an Scroller // to a group that was defined in the MXML file with a different parent if (elt.parent && (elt.parent != this)) throw new Error(resourceManager.getString("components", "mxmlElementNoMultipleParents", [elt])); elementAdded(elt, i); } } }

 

위 코드는 Group 클래스이다. mxmlContent는 Skin 클래스의 부모 클래스인 Group에 구현되어 있는 속성인 것이다. mxmlContent에 값을 설정하면 setMXMLContent() 함수가 호출되어 기존의 MXML 추가된 Element들을 지우고 새로운 Element인 Rect과 SimpleText를 추가한다. 또한 IVisibelElement인 컴포넌트만 자식으로 추가하도록 되어 있고 다른 부모를 가지는 컴포넌트인 경우에 예외처리도 한다. 결국 mxmlContent은 Group에서 사용된 MXML형태의 비주얼 컴포넌트를 Flex 컴파일러가 AS3로 추가해주기 위해 만들어진 것이다.

 

<s:Application> 부분에 버튼 추가한 부분은 어떻게 변해 있을까? 살펴보면 아래 처럼 되어 있다.

private function _sparkskin_Button1_c() : spark.components.Button
{
	var temp : spark.components.Button = new spark.components.Button();
	temp.label = "확인";
	temp.setStyle("skinClass", com.jidolstar.skins.MyButtonSkin);
	if (!temp.document) temp.document = this;
	return temp;
}

skinClass가 원래 Style속성이다. SkinnableComponent를 보면 [Style(name="skinClass", type="Class")]로 정의 되어 있다. 이는 validateSkinChange() 함수에서 붙여주게 된다.

 

/**
 *  @private
 */
private function validateSkinChange():void
{
    // If our new skin Class happens to match our existing skin Class there is no
    // reason to fully unload then reload our skin.  
    var skipReload:Boolean = false;
    
    if (_skin)
    {
        var factory:Object = getStyle("skinFactory");
        var newSkinClass:Class;
        
        if (factory)
            newSkinClass = (factory is ClassFactory) ? ClassFactory(factory).generator : null;
        else
            newSkinClass = getStyle("skinClass");
            
        skipReload = newSkinClass && 
            getQualifiedClassName(newSkinClass) == getQualifiedClassName(_skin);
    }
    
    if (!skipReload)
    {
        if (skin)
            detachSkin();
        attachSkin();
    }
}

 

내가 한가지 흥미를 느꼈던 것은 바로 <s:SimpleText> 부분이였다. Flex 4의 Button은 Label의 컴포넌트를 바꿀 수 없었는데 Flex 4에서는 스킨 기능을 이용해 Text엔진을 바꿀 수 있다. 가령 <s:SimpleText> 대신 <s:RichText>로 바꿔보길 바란다. 잘 된다. 이는 TextGraphicElement를 확장한 SimpleText와 RichText만 가능하다. 만약 TextArea와 같은 것을 사용하면 아래와 같은 에러가 나오게 된다.

 

TypeError: Error #1034: 유형 강제 변환에 실패했습니다. spark.components::TextArea@70c36d1을(를) spark.primitives.supportClasses.TextGraphicElement(으)로 변환할 수 없습니다.

 

이런 식의 사용은 Spark 컴포넌트 전체에서 사용할 수 있다. 참 좋은 방법이다.

 

 

생각하기

 

Flex 4는 Flex 3 처럼 무리하게 ActionScript 3.0까지 분석할 필요까지 느끼게 해주지 않도록 하는 것이 컨셉인 것 같다. 하지만 내가 좀 독특한 건가 예전 버릇을 버리지 못해 어찌 돌아가는지 알고 싶어서 Flex 4 SDK를 훑어보게 되었다. 하지만 이렇게 저렇게 분석하면서 살펴봤지만 아직도 모르는게 산적해있다. 먼저 사용하는 법부터 좀 공부해야겠다. ㅎㅎ

 

관련글

Getting started with Spark skins

Creating Spark Skins

The Spark Group and Spark SkinnableContainer containers

 

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/558)

 

+ Recent posts