이번 Adobe MAX 2009에서 Flash Player 10.1을 공개했습니다. 이번 키노트를 통해 더욱 최적화되고 강력해진 Flash player에 대해 설명을 들을 수 있었는데요, 주목할 점은 다양한 모바일등의 다양한 플렛폼 지원과 최적화 부분이겠네요.

MAX현장에서 직접 찍은 사진입니다. ^^
위 화면에서 Flash Player 10.1의 추가 및 향상된 기능을 보실 수 있습니다. 스마트 폰을 지원하며 멀티터치 기능이 가능해집니다. 또한 메모리, 전력, 하드웨어 가속에 대해서 최적화 했으며 Http 비디오 스트리밍등을 지원하게 됩니다.

각종 스마트폰에 지원이 되는 만큼 애플리케이션의 저전력 구동은 핵심이 될겁니다.  일반 비디오는 3.4시간, 활동적인 에니메이션이 들어간 애플리케이션인 경우 6.5시간, 저전력 모드에서는 14.5시간 볼 수 있다고 언급하고 있습니다.

Flash Player 10.1과 기존 Flash Player 10의 메모리 사용양을 나타냅니다. 같은 자원을 쓰고서도 거의 1/2~2/3가량 용량이 줄어들었습니다. Flash Player 10.1이 모바일에 지원되기 위한 최적화의 노력의 성과겠네요.

Adobe는 이번 키노트를 통해 Flash Player가 더이상 데스크탑이 아닌 모바일에서도 같은 동작을 한다는 것을 강조하고 있습니다. 지금까지 Flash Lite로만 모바일 기기 애플리케이션을 만들었지만 Flash Player 10.1부터는 일반 Flash로도 충분히 모바일에서 돌아가는 플래시 컨텐츠를 생산할 수 있게 되었습니다. 이렇게 Flash Player가 다양한 모바일에 지원된데에는 오픈 스크린 프로젝트(Open Screen Project)의 추진과 관련 있습니다. 흥미로운 점은 이 프로젝트에 구글이 동참했다는 점입니다. 오픈 스크린 프로젝트는 더이상 PC, 모바일만을 뜻하지 않으며 다양한 플렛폼에서 다양한 경험을 제공하는 것을 목표로 합니다. 이 오픈 스크린 프로젝트는 구글, 삼성, LG를 비롯해 노키아, NVIDIA등 수많은 회사가 참여하고 있으며, 앞으로 이들이 내높는 다양한 플랫폼에 Flash Player를 지원하게 되어 어디서든 Flash 컨텐츠를 접할 수 있게 됩니다. 하지만 아쉽게도 아이폰의 제작 회사인 Apple은 이 프로젝트에 포함되지 않았군요.


하지만 가만히 있을 Adobe가 아니죠.


위 그림 하나로 다들 쓰러졌다는....
그러니깐 Flash IDE에서 Flash컨텐츠를 개발해서 그대로 iPhone용으로 익스포트 시켜주는 기능이 CS5부터 추가된다고 합니다. 멀티터치 기능도 추가되겠다. 결국 이 기능도 iPhone을 겨냥한듯합니다. ㅎㅎ

Flash Player 10.1 에 대한 다양한 글이 이미 인터넷에 퍼졌습니다.

Flash Player 10.0 에 대해서는 아래 Adobe MAX 키노트 동영상을 참고하세요.

음... Flash Player 10.1 은 내년 초에 나온다고 하네요. ^^

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



최근 매우 좋은 기회가 생겨서 미국 LA에서 열리는 Adobe MAX 행사를 다녀올 수 있게 되었습니다. 약 1주일간 다녀올 예정입니다. 잘 다녀와서 좋은 정보 많이 얻고 돌아오도록 노력하겠습니다.


 

실무에 바로 쓸 수 있는있는 유용한 Flash/Flex 기반 ActionScript 3.0 라이브러리들입니다. 3번째 라이브러리도 조만간 소개하겠습니다. ^^

 

http://opencast.naver.com/FL188/12

 

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

 

실무에 바로 쓸 수 있는 유용한 Flash/Flex 기반 ActionScript 3.0 라이브러리들입니다.

이들을 이용해서 많은 테스트들이 이뤄졌으면 합니다. 물론 관련내용을 블로깅해서 트랙백도 걸어주시면 좋겠어요 ^^

 

다음주에는 다른 라이브러리를 소개하겠습니다.

 

http://opencast.naver.com/FL188/11

 

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

 

 

 

구를 표현하는데 있어서 많은 3D 예제들이 그냥 경도, 위도로 나눠서 텍스쳐 입히는 것이 대부분입니다. 사실 그게 가장 쉬운 방법이고 완성된 구를 표현할 것 같으면 그편이 좋습니다.

 

Geodesic Sphere에 대한 프로그램 코드는 그리 많지 않은 것 같더군요. 대신 건축물은 왜 이렇게 많은지.. ㅎㅎ 이 형태의 구로 만든 건축물이 매우 이쁘고 독특해보이죠. 디즈니랜드의 Epcot Dom이 매우 유명한 Geodesic Sphere의 한종류이죠.

 

 

응용하면 아래처럼 의자도 만들 수 있네요. 왠지 세련되어 보입니다.

 

Geodesic이라는 말은 두점간에 최단선(측지선)을 의미합니다. 이 원리를 이용해 만든 구가 바로 Geodesic 구라는 것이지요.

 

구글 어스(Google Earth)와 같은 3D 프로그램에서 구의 형태를 어떻게 표현했다고 생각하시나요? 정확히는 모르겠지만 Geodesic Sphere 기법을 이용했을 거라 추측합니다. 구글어스에 보이는 지도는 타일 이미지들의 모임입니다. 만약 경도,위도로 그 타일 이미지를 나눠버리면 결국 극쪽(북극, 남극)쪽에 불러와야할 타일 이미지는 엄청 많아지겠죠. 확대할수록 더할 겁니다. 이런 점에 비춰볼때 3D에서 Geodesic Sphere의 선택이 필요할 것 같아집니다.

 

Geodesic Sphere를 표현하는 방법은 다양합니다. TETRAHEDRON, OCTAHEDRON, ICOSAHEDRON 기반등을 이용해 그려나갈 수 있지요. 아래처럼요.

 

출처 : http://www.geepers.co.uk/software/geodesicsphere.html

 

아직 3D 지도에 어떤 방식이 좋을지 명확히 판단하지 못했지만 OCTAHEDRON 또는  ICOSAHEDRON  기반이 아닐까 생각합니다.

 

 

회전 : 마우스 드래그

폴리곤수 조절 : 방향키 Up/Down

 

Flash 3D API의 drawTriangle 메소드 기반으로 그려본겁니다. 일반 경도, 위도만을 가지고 그릴때보다 복잡한 편이라 애를 먹었습니다. 극부분이 이미지가 찌그러져보이는 것은 UV설정이 잘못되었기 때문입니다. 이부분에 대한 보완이 필요합니다. 어쨌든 3D 지도 기반을 만들기 위한 첫단추를 꽨 셈이군요. ^^

 

소스코드 아래 링크에서 볼 수 있습니다.

http://wonderfl.net/code/73c32973543fd5d1827ae725ae92f143784b5f97 

 

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

 

Flash에서 이미지 처리는 지금까지 BitmapData 조작이였다. 싱글스레드 기반의 Flash Player는 이미지 조작을 하기에 좋은 퍼포먼스를 가지기 어려웠다. 하지만 Flash Player 10부터 멀티스레드를 기반으로하는 Adobe Pixel Bender를 지원함에 따라 이제 이미지 프로세싱에 Flash의 막강화력을 보여줄 수 있게 되었다. Adobe Pixel Bender에 대한 국내 문서는 그다지 많지 않은 편이지만 앞으로 Flash/Flex/ActionScript 개발자들에게 각광받는 기술이 될 것임에 틀림없겠다.

 

Opencast 보러 가기 : http://opencast.naver.com/FL188/10

 

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

한글 에러 : ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."

 

영문 에러 : ArgumentError: Error #2180: It is illegal to move AVM1 content (AS1 or AS2) to a different part of the displayList when it has been loaded into AVM2 (AS3) content.


위와 같은 에러를 본 적이 있는가?

 

이것은 Flash Player 9 버전으로 만들어진 애플리케이션에서는 발생하지 않는다. Flash Player 10 버전으로 만들어진 애플리케이션에서 위와 같은 문제가 발생한다.

 

위 에러가 발생하는 조건은 다음과 같다.

 

  1. AVM2(ActionScript Virtual Machine 2)기반으로 만들어진 Flash 애플리케이션
    AVM2 기반이라는 것은 ActionScript 3.0 기반으로 만들어진 애플리케이션을 의미한다.
  2. Flash Player 10 기반으로 컴파일한다.
    컴파일 옵션에서 Flash Player 버전을 9.0.124 등이 아닌 10.0.0을 기반으로 한다.
  3. 위 두 조건하에서 AVM1기반으로 만들어진 SWF파일을 flash.display.Loader를 이용해 로드한다.
    AVM1 기반이라는 것은 ActionScript 1.0 또는 ActionScript 2.0 기반으로 만든 SWF파일을 의미한다.
  4. Loader.contentLoaderInfo.content를 addChild() 한다.
    Loader는 외부의 이미지, 플래시 파일등을 로드해서 디스플레이 객체로 등록하는 일종의 wrapper역할을 담당한다. 이들을 로드완료하면 Loader.contentLoaderInfo.content에 해당 객체의 참조를 얻을 수 있다. 일반적으로 화면에 출력하기 위해 addChild( Loader ) 식으로 하면 되는데 이렇게 안하고 addChild( Loader.contentLoaderInfo.content ) 를 하는 경우다. 이것이 가능한 이유는 Loader.contentLoaderInfo.content 도 DisplayObject이기 때문이다.

 

위 조건에 맞춰서 ActionScript 3.0 기반으로 코딩을 아래와 같이 해보겠다.

 

package
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.URLRequest;
	
	public class AVM1LoadTest extends Sprite
	{
		private var loader:Loader;
		
		public function AVM1LoadTest()
		{
			loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
			loader.load( new URLRequest("avm1asset.swf") );	
		}
		
		private function onLoadComplete( event:Event ):void
		{
			var loaderInfo:LoaderInfo = event.target as LoaderInfo;
			trace( loader.numChildren );
			trace( loaderInfo.content.parent );
			var avm1asset:DisplayObject = loaderInfo.content;
			addChild( avm1asset );
			trace( loader.numChildren );
			trace( loaderInfo.content.parent );
		}
	}
}

 

 

위 프로그램을 Flash Player 9 버전으로 컴파일하고 실행해보자. (실행되는 Flash Player 버전은 10이어야 한다. 당연히 실험 애플리케이션이 Flash Player 9버전 뿐 아니라 10버전도 만들 것이기 때문이다.)

 

아래 화면과 같이 AVM1 기반 SWF를 예쁘게 출력해준다.

 

 

onLoadComplete() 안을 살펴보면 addChild() 앞뒤로 똑같은 trace()문이 있다. 이것은 addChild() 전후로 loader.numChildren과 loaderInfo.content.parent(이것은 loader.content.parent와 같다)의 변화를 살펴보기 위함이다. loader.numChildren은 Loader 객체에 로드된 AVM1객체가 자식으로 등록되어 있는 경우에는 1이 출력되고 그 반대는 0이 된다. loaderInfo.content.parent는 로드된 AVM1객체의 부모가 무엇인지 가리킨다.

 

결과는 다음과 같다.

 

1
[object Loader]
0
[object AVM1LoadTest]

 

무엇을 의미하는가? addChild()를 하기 전에는 AVM1객체는 Loader의 자식이지만 addChild() 후로는 AVM1LoadTest객체의 자식이 된다. AVM1LoadTest는 위 프로그램의 메인 클래스이다. 이것은 당연한 결과로 자식은 두개 이상의 부모를 가질 수 없는 것을 의미한다. Flash Player 9버전으로 만든 경우에는 이러한 코딩이 가능했다.

 

조건을 바꿔보자. 이제 위 프로그램을 Flash Player 10 버전으로 컴파일하고 실행해보자. 언급했던 에러가 발생한다.

 

"ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."

 

무엇을 의미하는가? Flash Player 10 기반으로 만들어진 애플리케이션은 AVM1 객체를 AVM2 기반의 디스플레이 객체로의 이동을 불허한다. 즉 Loader에서 떠나지 못함을 의미한다.

 

Flash Player 10기반에서 AVM2 SWF를 로드한 경우는 addChild( loader.contentLoaderInfo.content ) 가 가능할까? 이것은 된다!

 

Flash Player 10에서 AVM1기반의 SWF는 부모로 Loader이어야만 하는 이유는 무엇일까? 사실 여기에 대한 답변을 나는 아직 찾지 못했다. 혹시 아시는 분은 댓글 및 트랙백을 부탁한다.

 

일본에서는 이미 이 문제로 고민한 사람이 꽤 있었다. 재미있게도 AVM1을 AVM2로 강제로 바이트 단위로 버전을 수정해 버려 로드해버리는 커스텀 Loader까지 만들어 배포하는 사람이 있었다. 좋은 방법인지는 모르겠지만 아무튼 이런 연구가 활발히 진행되는 일본이 부럽당.. ㅡㅡ

 

Illustrator의 벡터형식 데이터를 읽어들이는 방법(Loader 클래스편)

Illustrator의 벡터형식 데이터를 읽어들이는 방법(Embed 태그 편)

BeInteractive - AVM2에서 AVM1의 SWF를 억지로 로드하는 ForcibleLoader 

BeInteractive - AVM2에서 AVM2의 SWF를 억지로 로드하는 방식 해설

ForcibleLoader 다운로드

ForcibleLoader 브라우징하기

 

흑... 이것도 spark 프로젝트....


아래 내용도 보면 좋을거다.

로드한 SWF 내부에 작성된 ActionScript 3.0 클래스 이름 찾기

 

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

 

 

Flex Builder 또는 Flash Builder 에서 유용하게 사용할 수 있는 플러그인을 소개했습니다. 더 많을 것이라 생각하는데 함께 공유했으면 좋겠네요. 댓글, 트랙백 모두 환영합니다.

http://opencast.naver.com/FL188/8


참고로 Flash Builder는 아래 링크로 가서 다운받으세요.
http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026 


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

 

Flash/Flex 개발에 있어서 데이타와 UI의 의존성을 줄여주어 장기적으로 유지보수 및 효율적 관리를 하기 위해 MVC 디자인 패턴이 기본적으로 적용된 프레임워크를 직접 제작하거나 무료로 공개된 것들을 사용할 수 있다. 여기서 소개하는 프레임워크들은 Flex, Flash 개발하는데 MVC를 적절하게 활용해볼 수 있는 것들이다. 유용한 정보가 되길 바라며 관련된 많은 글들이 올라왔으면 한다.

 

http://opencast.naver.com/FL188/7

Flash가 Cross OS, Cross Browser를 지향하지만 한글문제를 비롯해 각종 몇가지 기능을 제대로 수행하지 못하는 경우가 있다. 마우스 휠(Mouse Wheel)도 그와 같은 맥락이다.

 

Mac 운영체제에서는 마우스 휠 기능이 전혀 먹지 않는다. MS Window에서 wmode가 transparent일때 Internet Explorer를 제외하고 다른 브라우저에서는 마우스 휠 기능을 사용할 수 없다.

 

이 문제를 해결할 수 있는 유일한 방법은 Javascript를 이용하는 방법이다. ActionScript3의 ExternalInterface를 이용해 Javascript와 통신해서 마우스 휠 이벤트를 사용하는 것이다. 자바스크립트를 이용하는 방법은 내 블로그에도 몇번 글을 올렸다.

 

ActionScript 3.0 만으로 쿠키를 제어 - Actionscript Cookie Util 소개

[Flex/Flash] ActionScript 3.0 으로 브라우저 종류 알아내기

 

Pixelbreaker 블로그에 Mac에서 마우스 휠을 해결하는 방법을 다룬 유명한 글이 올라와 있다.

 

AS3.0 MouseWheel on Mac OS

 

하지만 이 방법은 Mac에만 유용하고 wmode=transparent 일때 대처를 하지 못한다. 또한 js파일을 따로 html에 포함 해야하니 사용하기 귀찮다.

 

위 소스를 개선하여 어떤 경우에라도 마우스 휠을 사용할 수 있으면서 따로 js파일을 포함안하고 actionscript 코드만으로 해결한 소스가 있다. 일본 Spark 프로젝트 팀에서 만든 SWFWheel이라는 하나의 클래스 코드인데 사용하기도 쉽고 깔끔하다.

 

공식홈페이지 : http://www.libspark.org/wiki/SWFWheel 

 

아래에서 공개한 JS파일을 AS 3.0 코드에 포함한다. 아래 링크를 보면 쉽게 이해할 수 있겠다.

swfwheel.js : http://www.libspark.org/browser/as3/SWFWheel/trunk/zoo/swfwheel.js

SWFWheel.as : http://www.libspark.org/browser/as3/SWFWheel/trunk/src/org/libspark/ui/SWFWheel.as

 

다운로드는 아래 링크에서 AS파일만 다운받아 사용하면 되겠다.

http://www.libspark.org/svn/as3/SWFWheel/trunk/src/org/libspark/ui/

 

 

사용법은 너무 간단하다.

import org.libspark.ui.SWFWheel;
SWFWheel.initialize(stage);

 

위처럼 하고 ActionScript 를 통해 마우스 휠 이벤트를 stage로 부터 등록하여 사용한다.

stage.addEventListener( MouseEvent.MOUSE_WHEEL, mouseWheelHandler );

SWFObject를 이용해 아래와 같은 방법으로 Flash Content를 삽입한다.

var flashvars = {};
var params = {};
var attributes = {
    id: "myDynamicContent",
    name: "myDynamicContent"
};

swfobject.embedSWF("myContent.swf", "myContent", "300", "120", "9.0.0", "expressInstall.swf", flashvars, params, attributes);

 

단, 위 방법이 잘될 수 있도록 하기 위해 allowScripAccess는 같은 도메인의 swf인 경우 sameDomain, 다른 도메인의 swf인 경우 always로 지정해야한다. allowNetworking은 항상 all로 설정해야한다. 이에 대한 자세한 내용은 다음글을 참고한다.

 

위젯도 마음대로 못다는 네이버 블로그

 

 

SWFWheel 클래스는 browserScroll 속성이 있다. 이것을 이용해면 Flash 위에서 MouseWheel을 이용할 때 Flash를 담은 브라우저의 스크롤링을 허용할 것인가 설정할 수 있다. 기본값은 false이다.

 

네이버 오픈캐스트의 메인 프로그램이 Flash로 만들어져 있는데 마우스 휠 기능이 어떤 운영체제든 브라우져든 잘 동작한다. 아마도 이 SWFWheel을 사용하지 않았나 생각된다.

 

이렇게 꽁수 안부리고 마우스 휠이든 한글문제든 Flash Player가 문제 없이 잘 동작하면 얼마나 좋을까?

 

[생각더하기]일본의 Spark 프로젝트의 이름에 눈길이 간다. Flex 4의 새로운 컴포넌트는 Spark로 명명했다. 그럼 Flex 4가 만들어질때 일본의 Spark 프로젝트의 역할이 컷던 것 아닐까? 실제로 Spark 프로젝트는 일본내에서 매우 활발하고 유명한 Flash 관련 프로젝트이다. 그 유명한 증강현실(FLARToolKit)도 이 프로젝트에서 만들어진 것이다. 우리 나라에는 왜 이런 멋진 프로젝트 팀이 없는 것일까? 안타깝다.

 

 

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

 

 

 

 

 

 

Flash 3D에 관련된 3D 엔진 및 학습할 만한 사이트를 골라 네이버 오픈캐스트에 발행했습니다. 요즘 Flash 3D에 푹빠져 있는데 관심있는 분들과 함께 공부하고 싶네요. 전 3D 초급수준이라 공부할 것도 많네요. ㅎㅎ

 

http://opencast.naver.com/FL188/5

 

좋은 자료 되길 바랍니다.

 

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

 


Flash 3D 엔진하면 Away3D, Papervision3D 정도로 생각했다. 최근에서 ND3D라는 것도 접했는데... Away3D, Papervision3D 처럼 기능이 좋으면 속도/메모리가 문제이고 ND3D처럼 속도가 좋으면 기능이 문제이다.

그런데... 오늘 Alternativa3D를 접하고 그냥 Flash 3D의 새 장을 본듯한 느낌이 들었다.(예전에도 봤는데... 그때는 3D에 관심이 없어서 그냥 지나쳤다... ㅎㅎ)

Alternativa3D는 러시아에서 만든 Flash 3D 엔진이다.
이 엔진은 Flash Player 9과 10에 대응하도록 무료로 SWC로 배포하고 있다. 비상업적 개발의 경우에는 무료이나 상업적으로 개발시에는 유료이다. Flash로 3D를 만든다면 그냥 이거 구입하는게 좋겠다는 생각이 들었다.

기본 3D 엔진 뿐 아니라, 물리엔진, 소리엔진등을 제공하고 각종 인터렉션등에도 대응하도록 되어 있다. 속도는 왜이렇게 빠른가? 렌더링에 있어서 정말 최적화를 이뤘다고 해도 과언이 아니다. 게임엔진으로는 그만이다.

얼마나 대단한지 아래 사진을 클릭해서 데모 프로그램을 보자.

아래는 Tanki Online이라는 프로그램이다. Alternativa3D를 이용했고 온라인 멀티게임이다. 물리엔진을 적극 써서 흥미를 더했다. 아래 사진 클릭하면 해당 페이지로 간다.



볼 것도 없다. 현재로선 네가 최고다!

Alternativa3D 공식페이지 : http://alternativaplatform.com/en/alternativa3d/

근데 발음은 어떻게 하지? 알터네이티바?

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

3D 환경맵핑(Environment Mapping)의 한 기법으로 Cube Maps가 있다. 말그대로 직육면체의 각면에 대한 텍스쳐 이미지로 3D 환경을 조성하는 방법인데 아래 이미지를 보면 바로 이해할 수 있다.

 

Cube Map

출처 : developer.com

 

Cube Maps는 환경 맵핑 방법중 가장 빠르고 매우 사실적으로 묘사할 수 있는 방법으로 내가 알기로는 게임등에 자주 사용하는 것으로 알고  있다. 단점은 모서리 부분에서 약간 티가 난다는 점이다.

 

Flash Player 10부터 3D API를 제공한다. 이 API들을 적절히 활용하면 Papervision3D나 Away3D와 같은 라이브러리를 이용하지 않고도 어려움 없이 Cube Maps을 구현할 수 있다.

 

아래 프로그램은 Flash Player 10에서 제공하는 3D API만 이용해서 Cube Map을 구현한 것이다. 마우스 드래그로 회전이 가능하다.

 

마우스로 Drag하면 회전이 됩니다.
(Mac에서 화면이 작게 나오는데 아직 이유를 모르겠네요 ^^;)

 

위와 같은 프로그램을 만들기 위해 6면을 가지는 텍스쳐 이미지가 필요하다. 인터넷에 많이 있는데 아래처럼 Layout이 여러종류가 있다.

 

Cube Map Layout

Cube Map Layout 출처 : cgtextures.com

 

나는 가장 일반적이고 자주 쓰이는 Horizontal Cross방식 대신에 Horizontal Strip(NVidia DDS Exporter Layout)을 이용했다. 이 방식은 아래같이 구성된다.

 

Cube Map Layout 출처 : cgtextures.com

 

이러한 이미지가 어떻게 Cube Map을 만들 수 있는지 아래 화면을 보면 쉽게 알 수 있다.

 

마우스로 Drag하면 회전이 됩니다.

 

아래는 위 프로그램에 대한 소스이다.

 

 

package
{
	import flash.display.*;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.*;
	import flash.utils.getTimer;
	
	[SWF(frameRate=60, backgroundColor=0x000000)]

	/**
	 * Cube Map 예제 
	 * @author Yongho, Ji (http://blog.jidolstar.com/574)
	 * @since 2009.08.05
	 */
	public class CubeSky extends Sprite
	{
		[Embed(source="sky.jpg")]
		private const SKY:Class;
		
		//투영된 Vectex 정보
		private var projected:Vector.<Number>;
		
		//World 변환 행렬 
		private var world:Matrix3D;
		
		//투영을 위한 변환행렬 
		private var projection:Matrix3D;
		
		//Mesh 데이터 
		private var mesh:GraphicsTrianglePath
				
		//Texture
		private var texture:BitmapData = (new SKY as Bitmap).bitmapData;
		
		//Viewport (3D 렌더링 대상)
		private var viewport:Shape;
		
		//Viewport의 Z축 위치 
		private var viewPortZAxis:Number = 0;		
		
		public function CubeSky()
		{
			//Viewport 
			viewport = new Shape;
			addChild( viewport );
			
			//투영 변환 행렬 
			var p:PerspectiveProjection = new PerspectiveProjection()
			p.fieldOfView = 30;
			projection = p.toMatrix3D();

			//World 변환 행렬
			world = new Matrix3D();
			
			var check3D:Vector3D = Utils3D.projectVector(
										projection, 
										new Vector3D(1.0,0,1.0)
									);			
									
			//Mesh 데이타 
			mesh = createCubeMesh( check3D.x );
			projected = new Vector.<Number>(0,false);
			viewPortZAxis = check3D.x;// * 4;
			
			//이벤트 핸들러 등록 
			stage.addEventListener( Event.RESIZE, resize );
			stage.addEventListener( Event.ENTER_FRAME, render );		
			//stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );	
			stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseEvent );
			resize();
		}
		
		private function resize( event:Event = null ):void
		{
			viewport.x = stage.stageWidth/2;
			viewport.y = stage.stageHeight/2;	
		}		
		
		private function render( event:Event = null ):void
		{
            world.identity(); //단위행렬로 전환 
			world.appendRotation( zRotation, Vector3D.Z_AXIS );
			world.appendRotation( 90+xRotation, Vector3D.X_AXIS );
            world.appendTranslation(0, 0, viewPortZAxis); //이동 
            world.append(projection); //투영 변환 적용 
            
            // mesh 데이터를  투영하여  projected 생성 
            // uvtData도 갱신된다. 갱신되는 데이터는 T값이다.             
            Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
  
            // texture를 이용해 렌더링
            viewport.graphics.clear();
            //viewport.graphics.lineStyle( 1, 0xff0000 );
        	viewport.graphics.beginBitmapFill( texture, null, false, true );
            viewport.graphics.drawTriangles( projected, mesh.indices, mesh.uvtData, mesh.culling );
        	//Cube는  z축을 굳이 정렬할 필요 없다.
            //viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
		}
		
		/**
		 * 마우스 휠 처리 
		 */ 
		private function onMouseWheel( event:MouseEvent ):void
		{
			viewPortZAxis += event.delta * 10;
		}	

		private var xRotation:Number = 0;
		private var zRotation:Number = 0;
		private var prevX:Number;
		private var prevY:Number;		
		/**
		 * 마우스 이벤트 - x,y축 회전
		 */ 
		private function onMouseEvent( event:MouseEvent ):void
		{
			switch( event.type )
			{
				case MouseEvent.MOUSE_DOWN:
					stage.addEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.addEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_MOVE:
					if( event.buttonDown == false )
					{
						stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
						stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
						break;
					}
					var dx:Number = mouseX - prevX;
					var dy:Number = mouseY - prevY;
					zRotation += dx;
					xRotation += dy;
					if( xRotation > 90 ) xRotation = 90;
					if( xRotation < -90 ) xRotation = -90;
					prevX = mouseX;
					prevY = mouseY;
					break;
				case MouseEvent.MOUSE_UP:
					stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
					stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
					break;
			}	
		}
	}
}

import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;

/**
 * Cube Mesh 데이타 작성 
 * @param size 한변의 사이즈
 * @return mesh 데이터 
 */ 
function createCubeMesh( size:Number = 512 ):GraphicsTrianglePath
{
	var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
	var indices:Vector.<int> = new Vector.<int>( 0, false );
	var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.POSITIVE );
	var s:Number = size/2;
	var w:Number = size * 6;
	var u00:Number = 0;
	var u01:Number = (size-1)/w;
	var u10:Number = size/w;
	var u11:Number = (size*2-1)/w;
	var u20:Number = (size*2)/w;
	var u21:Number = (size*3-1)/w;
	var u30:Number = (size*3)/w;
	var u31:Number = (size*4-1)/w;
	var u40:Number = (size*4)/w;
	var u41:Number = (size*5-1)/w;
	var u50:Number = (size*5)/w;
	var u51:Number = (size*6-1)/w;
	
	mesh.vertices.push( 
		//Right		
		s,-s,-s,	//0
		s,-s,s,		//1
		s,s,s,		//2
		s,s,-s, 	//3

		//Left
		-s,s,-s, 	//4
		-s,s,s, 	//5
		-s,-s,s, 	//6
		-s,-s,-s, 	//7
		
	
		//Top
		-s,-s,s, 	//8
		-s,s,s, 	//9
		s,s,s, 		//10
		s,-s,s, 	//11
		
		//Bottom
		-s,s,-s, 	//12
		-s,-s,-s, 	//13
 		s,-s,-s, 	//14
		s,s,-s, 	//15
		
		//Front
		-s,-s,-s, 	//16
		-s,-s,s, 	//17
		s,-s,s, 	//18
		s,-s,-s, 	//19

		//Back
		s,s,-s, 	//20
		s,s,s, 		//21
		-s,s,s, 	//22
		-s,s,-s 	//23
	);
	
	
	mesh.indices.push( 
		0,1,2, 0,2,3, 	//Right
		4,5,6, 4,6,7,  	//Left
		8,9,10, 8,10,11, //Top
		12,13,14, 12,14,15, //Bottom
		16,17,18, 16,18,19, //Front
		20,21,22, 20,22,23 //Back
	 );
		
	mesh.uvtData.push( 
		//Right
		u00, 1, 1,
		u00, 0, 1,
		u01, 0, 1,
		u01, 1, 1,

		//Left
		u10, 1, 1,
		u10, 0, 1,
		u11, 0, 1,
		u11, 1, 1,

		//Top
		u20, 1, 1,
		u20, 0, 1,
		u21, 0, 1,
		u21, 1, 1,

		//Bottom
		u30, 1, 1,
		u30, 0, 1,
		u31, 0, 1,
		u31, 1, 1,

		//Front
		u40, 1, 1,
		u40, 0, 1,
		u41, 0, 1,
		u41, 1, 1,

		//Back
		u50, 1, 1,
		u50, 0, 1,
		u51, 0, 1,
		u51, 1, 1
	 );

	
	return mesh;
}

/**
 * GraphicsTrianglePath를 기반으로, Z축으로 sort된 인덱스를 돌려준다.
 * 이 작업을 해주어야 z축 깊이에 따라 Triangle이 제대로 그려진다. 
 * @param mesh 정보 
 * @return sort된 index 데이터 
 */
function getSortedIndices( mesh:GraphicsTrianglePath ):Vector.<int> 
{
    var triangles:Array = [];
    var length:uint = mesh.indices.length;
    
    //z축 sort를 위한 기반 제작 
    for ( var i:uint=0; i < length; i += 3 ) 
    {
        var i1:uint = mesh.indices[ i+0 ];
        var i2:uint = mesh.indices[ i+1 ];
        var i3:uint = mesh.indices[ i+2 ];
        var z:Number = Math.min( mesh.uvtData[i1 * 3 + 2], mesh.uvtData[i2 * 3 + 2], mesh.uvtData[i3 * 3 + 2] );
        if (z > 0) 
        { 
        	triangles.push({i1:i1, i2:i2, i3:i3, z:z}); 
        }
    }
    
    //z축으로 sort
    triangles = triangles.sortOn("z", Array.NUMERIC);
    
    //sort된 값을 이용해 Vector값 만듬 
    var sortedIndices:Vector.<int> = new Vector.<int>(0, false);
    for each (var triangle:Object in triangles) 
    {
        sortedIndices.push(triangle.i1, triangle.i2, triangle.i3);
    }
    return sortedIndices;
}

 

 

아래 이미지는 위 프로그램에서 사용한 이미지이다.

 

위 프로그램에서는 Graphics.drawTriangle() 메소드를 이용했다. 하지만 이 함수를 사용하지 않고도 만들수 있다. 왜냐하면 Flash Player 10부터는 DisplayObject에 대해 x,y,z축 회전 및 이동 API가 추가되었기 때문이다. 그러면 위 프로그램 소스처럼 힘들게 vertex, index, uvt 데이타를 만들지 않아도 될 것이다.

 

내 생각에 Flash에서 3D 개발을 위해 Graphics.drawTriangle()와 DisplayObject의 3D API, Matrix3D 등을 서로 섞어가면서 만드는 것이 좋을 것 같다.

 

개발환경 : Flash Builder 4 Beta 1 (Flash CS4 에서도 개발할 수 있음.)

 

참고내용

 

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

 

단지 25줄의 ActionScript 코드로 얼마나 멋진 애플리케이션을 만드는가 가리는 대회이다. 웹서핑하다가 우연치 않게 본 페이지에서 25줄이라고는 믿기지 않을 정도의 결과물들을 보고 이 세상에는 정말 천재들이 많구나라는 생각이 들었다.

 

대회 규정 및 이전 대회 결과물들은 아래 홈페이지를 통해 볼 수 있다.

 

http://www.25lines.com

 

아래에서 소개하고 있는 ActionScript 결과물들은 2009년 1월 결승전에 올라온 작품들이다. 작품 하나하나마다 25줄의 코드라고는 믿기지 않는 작품들임을 볼 수 있을 것이다. 만든것도 대단하지만 창의적인 면에 있어서도 높은 점수를 주고 싶다.

 

올라온 소스 코드를 분석해보려고 읽기 쉽게 정리하면서 봐도 이해못하는... ㅜㅜ

언젠간.. 나도 저 대열에....

 

Entry 001
Code
SWF

Entry 002
Code
SWF

Entry 007
Code
SWF

Entry 008
Code
SWF

Entry 009
Code
SWF

Entry 014
Code
SWF

Entry 021
Code
SWF

Entry 023
Code
SWF

Entry 027
Code
SWF

Entry 029
Code
SWF

Entry 030
Code
SWF

Entry 032
Code
SWF

 

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

 

Flex Explorer만 선별하여 네이버 오픈케스트에 발행했습니다. Flex, AIR 개발자라면 꼭 들어가서 볼 필요있는 것만 나름대로 엄선했으니 참고 바랍니다. Flex를 처음 공부하는 사람에게는 더욱 필요한 정보가 될 것입니다.  현재 Flex 4 Beta가 나와 있는 상태이지만 Flex 2, Flex 3에 대한 Explorer로 포함합니다. 이는 Flex SDK 버전에 상관없이 유용할 것이 판단했기 때문에 넣었습니다.

 

그럼 즐 Flex/AIR 하세요.

 

http://opencast.naver.com/FL188/4

 

제 오픈케스트는 1주일에 한번씩 발행됩니다.

마음에 드신다면 꼭 구독도 하세요 ^^

 

이외에 아래 링크들도 유용합니다.

 

 

 

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

 

Adobe 에서 ACC(Adobe Community Champion)들에게

깜짝 선물을 줬는데

나는 HYATT 호텔식사권을 받았다.

 

 

Adobe의 박민형 전무님의 정성어린 친필이 담긴 편지와 함께온 하야트 호텔식사권이다. ㅎㅎ

다른 ACC분들은 가족끼리 갈 수 있는 케리안베이 이용권을 받았다.

울 아내가 임신중이라 거긴 갈 수 없어서 식사권을 받은 것이다. ㅋㅋ

 

울 아내는 이거 보고 너무 좋아서 어쩔 줄 몰라했는데...

내가 "이거 그렇게 좋은거야?" 물어보니깐....

"헉" 하더라...

그러면서 하야트가 뭔지 모르냐고 묻는다...

나의 대답 : "응"

......

 

아무튼 하야트도 모르는 무식한 남편이지만

아내가 좋아하니 나도 기분 좋다.

시간나면 함께 데이트 가야 겠다. ㅎ

네이버 오픈케스트에 "Adobe ColdFusion에 대해 알아봐요."라는 제목으로 발행했습니다.

 

 

국내에서는 이상하게도 ColdFusion에 대해 많이 무관심한 편입니다. 알고 보면 정말 멋진 RIA 기술인데도 말이죠. 개발은 쉽게하고 유지보수도 편하게 하는 것을 좋아하시는 분들에게 ColdFusion은 큰 매력으로 자리 잡을 수 있을 것이라 생각합니다. 물론 개인 뿐 아니라 기업 입장에서도 효율적인 개발 및 유지보수 차원에서 도입한다면 좋을 것이라 생각합니다.

 

아래 링크에 접속하시면 보실 수 있습니다.

 

http://opencast.naver.com/FL188/3

 

한번 ColdFusion의 매력에 푹~ 빠져보시기 바랍니다.

Adobe에서 TLF(Text Layout Framework)의 소스를 공개했다.

 

먼저 어떤 것인지 데모 버전을 보자.

아래 링크로 보면 되겠다.

 

http://labs.adobe.com/technologies/textlayout/fma/Shell.swf

 

TLF는 Flash Player 10, AIR 1.5 기반의 새로운 Text 엔진이다. Adobe Flash CS4 및 Flex 4 에서 사용할 수 있다. Flash API에서 제공하는 TextField의 부족한 부분을 개선하고 일반 워드 처럼 Text 편집 작업을 할 수 있도록 하는 것이 이 프레임워크의 목적이 아닌가 싶다.

 

Adobe Labs에 다운로드 페이지에서 직접 받을 수 있다. 소스는 최신 Flex 4 SDK에 포함되어 있다고 한다. Night Build 버전으로 다운로드 받을 수 있고 [여기]를 참조한다.

 

TLF의 Beta 1 버전은 다운로드 받으면 CS4, Flex 3, Flex 4 환경에서 개발할 수 있고 꼭 Flash Player 10 버전으로 컴파일 옵션으로 지정해야한다. 그리고 3개의 SWC파일인 textLayout_conversion.swc, textLayout_core.swc, textLayout_edit.swc 를 라이브러리로 포함하면 되겠다.

 

관련 페이지는 다음 링크를 참고한다.

 

- Text Layout Framework(Adobe Open Source)

- Text Layout Framework(Adobe Labs)

 

다음 TLF 기반 예제들도 참고할만 하겠다.

- AIR기반의 Times Reader 2.0 http://timesreader.nytimes.com/timesreader/
- Acrobat.com Presentations http://labs.adobe.com/technologies/presentations/
- makebook http://www.makebook.com/

 

하지만 아직 한국에서 사용하기에는 무리이다. 한글입력을 할 때, 자음, 모음이 먼저 보여지지 않는 것도 있고 기능적으로도 부족한 점이 많다. Open Source라지만 근본적인 한글 입력문제는 Flash Player 10 자체에서 해결해 주어야 할 듯 싶다.

 

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

 

코드네임 Strobe였던 OSMF(Open Source Media Framework)가 2009년 7월 21일 기점으로 오픈 소스로 공개되었다. 현재 버전은 0.3 2.0(2013-12-17)이다.

 

OSMF는 3개 부분의 구조로 나뉜다. User Interface, Monetization workflows, media delivery가 그것이다.

 

OSMF는 최적의 Media Player를 개발할 수 있는 프레임워크로 플레이어 개발의 복잡성을 줄어준다.

재생컨트롤, 비디오 네비게이션, 버퍼링, 다이나믹 스트리밍, 리포트 분석과 같은 플러그 인 설치등을 지원한다. 무엇보다 이 모든 기능이 오픈소스화 되어 어느 개발자든지 만들어진 소스를 공유하고 수정하며 다시 배포할 수 있게 되었다. 정확한 라이센스는 MPL에 따른다.

 

공식사이트는 아래 링크를 참고한다.

http://osmf.org/


아래는 OSMF 블로그이다. 

http://blogs.adobe.com/osmf/ 


다운로드는 아래 링크를 참고한다.

http://sourceforge.net/projects/osmf.adobe/files/


빠르게 익숙해지려면 아래 링크 동영상을 보도록 하자.

http://www.gotoandlearn.com/play.php?id=129 

http://tv.adobe.com/show/rich-media-player-development-with-osmf/ 


아래 링크에서 OSMF Examples를 볼 수 있다.

http://mediapm.edgesuite.net/osmf/swf/ExamplePlayer.swf  


Flash Builder, Flash CS3, CS4 환경이면 OSMF API로 개발이 가능하겠다.

본인도 OSMF를 다운로드 받아서 예제 소스를 가지고 Flash Builder 4 에서 만들어 보았다. Flex 소스가 Flex 3라서 Flash Builder 4에서는 SDK를 3.4 버전으로 맞춰줘야 한다.


참고: 아래소스는 0.3버전 시절겁니다. 그냥 참고만 하세요. (2013-12-17)

 

 

CompositionPlayer.zip

 

위 결과물은 OSMF 배포된 소스 안에 있는 샘플로 만든 것이다. UI는 형편없지만 다양한 방법로 Media 컨텐츠를 읽어와 재생하고 볼륨조정, Pan 조정도 가능하다는 것을 보여주고 있다.

 

아래 프로그램은 순수하게 ActionScript 3.0 프로젝트로만 만들어 본것이다.

 

FlashMediaPlayerTest.zip

 

필요하다면 OSMF를 프로젝트에 복사해서 사용해도 된다.

 

아니면 아래 처럼 라이브러리 형태로 배포된 SWC를 자신의 프로젝트의 libs에 추가해서 사용해도 된다.

 

예제만 잘 보면 활용방법도 쉽게 얻을 수 있다. ^^

이제 Media 관련 플레이어를 제작하는데 많은 노력없이 OSMF만 이용해도 충분히 만들 수 있게 되었다. 너무 멋지다 이런 멋진 플레이어의 소스를 볼 수 있다는 것이 말이다.

 

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

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)

 

 

2번째로 네이버 오픈케스트에 Adobe RIA 추천사이트를 올렸습니다. 제가 자주 가고 또 유용하다고 생각되는 사이트만 10개 엄선했습니다. 네이버 메인에서 구독할 수 있습니다. 1주일에 한번씩 업데이트합니다. 많은 구독 부탁드려요 ^^

 

지돌스타의 Adobe RIA 오픈 캐스트 보기 : http://opencast.naver.com/FL188

 

7월 13일에서 14일까지 한국에서 인터넷으로 접속시에 Adobe 관련 사이트 접속에 장애 있었다. 요즘 DDoS때문에 Adobe가 한국 IP를 차단한 줄로 예상하고 외국 Proxy에 돌려서 접속해 보니 잘 접속되었다. 결국 Adobe가 막은 줄 알았다. 그런데 의외의 정보를 접했다.

 

7월13일~14일까지 국내 인터넷 서비스망을 통한 인터넷 접속시 www.adobe.com/kr 포함한 www.adobe.com 웹사이트 접속이 불가능하였으나, 어제 오후 7시부로 다시 접속이 가능해졌습니다.


KISA(한국정보보호진흥원) 상황실에서 근래 있었던 DDoS 공격을 의심, 어도비에게 사전공지 없이 독단적으로 어도비 홈페이지 IP 접속을 차단시켰던 것으로 확인 되었습니다.
따라서 어도비 홈페이지가 DDoS와 전혀 상관이 없음을 인정하고 다시 차단된 IP를 해제, 접속 에러 문제가 해결되었습니다.


며칠 사이 어도비의 수많은 중요한 고객분들께 불편을 끼친 점을 안타깝게 생각하며, 이는 폐사의 불찰이 아니었음을 양해하여 주시기 바랍니다.

애꿎은 Adobe를 나무랐던 것이다. KISA(한국정보보호진흥원)이 DDoS 공격 의심으로 Adobe 한국지사를 비롯한 관련 업체에게 아무런 공지없이 그냥 IP를 차단한거다.

 

정말 어이없다. 도데체 저 단체는 한국내 있는거 맞나? 막으려면 알려주고나 막지. Adobe가 작은 업체인가? 정말 무식한거 아닌가? 답답하기만 하다. 세금내는게 아깝다. 몇일간 Adobe에 접속 못해서 습득하지 못한 정보 때문에 시간 낭비 했는데 아무래도 KISA가 책임져야한다고 생각이 든다. 좀!!!! 제대로 일좀 하십시다.

서기님의 블로그를 보다가 너무 멋진 그림을 봐서 따왔다.

 

Life Cycle of the Flex UIComponent Base Class

 

그림의 출처 : http://danorlando.com/?p=122

 

Flex의 모든 비주얼 컴포넌트는 Sprite를 확장한 UIComponent를 기반으로 한다. 생성할 대 호출되는 함수와 발생되는 이벤트에 대해서 하나의 그림으로 표현되어 있다. Flex 커스텀 컴포넌트를 작성해야할 때 Life Cycle을 이해하는 것은 반드시 필요하다. 그러므로 눈에다 팍팍 익혀두면 도움이 될 것이다.

 

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

 

 

Adobe가 Coldfusion 9 Beta 및 새 Builder를 배포했습니다.

 

Adobe Labs(http://labs.adobe.com)에서 다운로드 받을 수 있습니다.

 

벌써 설치기까지 나왔는데 Coldfusion에 큰 관심과 애정을 가지고 계신 장창학 님의 블로그에서 보실 수 있습니다.

 

Coldfusion 9 Beta 및 Coldfusion Builder 간단 설치기

 

Coldfusion은 빠르고 쉬운 웹서비스를 개발하는 방법중에 하나입니다.

매우 강력함에도 불구하고 한국에서는 많이 쓰이지 않고 있는데요.

그래서 아쉽게도 한글로된 문서도 많이 없는 편입니다.

그래도 몇몇 선구자님들이 계셔서 다행입니다.

 

제 생각에 그 선구자 중에서 장창학님이 대표적인 것 같습니다.

그래서 Coldfusion에 대해서는 장창학님의 블로그에 잘 정리되어 있습니다.

관심있으신 분은 꼭 훑어보시고요.

 

용스님이 쓰신 ColdFusion 웹서비스 간단하게 만들어보는 예제도 있네요.

ColdFusion CFC로 웹서비스 간단하게 만들어보기

 

위키피디아에 ColdFusion에 대해서 자세히 나와있네요. ^^

 

http://en.wikipedia.org/wiki/ColdFusion

 

카페도 있습니다. 이것 역시 장창학님이 운영하시는... 대단하십니다.

http://cafe.naver.com/opencfml.cafe

 

아무튼 공부하고자 마음만 먹으면 할 수 있습니다. ^^

 

 

 

Adobe RIA에 대한 정보에 대한 소통의 도구로 네이버 오픈캐스트를 개설했습니다. Flex, Flash, ActionScript 3.0을 주로 다룰 것이고 그와 관련된 LCDS, BlazeDS, ColdFusion등, 다양한 Adobe RIA와 관련된 기술도 공유하고자 합니다.

 

제 오픈 케스트에 올라왔으면 하는 글 있으면 언제든지 추천해주시고요.

구독도 많이 해주세요~~~

더욱 좋은 정보 공유를 위해 앞장서도록 노력하겠습니다. ^^

 

지금 생각하는 주제를 몇가지 적어보면

한글문제, 3D, 컴포넌트 제작, 보안, Stratus, Alchemy, LCDS, BlazeDS, CF.... , 또... 이들에 대한 세부 주제까지... 정말 많네요. ㅎㅎ

 

지돌스타의 Adobe RIA 오픈캐스트 : http://opencast.naver.com/FL188

 

 

 

일전에 Flex 4 CSS에 대한 글을 올렸습니다. Flex 4의 CSS는 Flex 3까지의 CSS의 한계를 극복하도록 만들어졌죠. 그런데 이 CSS가 기능이 확장된 계기가 있었습니다. 갑자기 궁금해지지 않나요? Flex 개발에 관련된 Adobe 개발자들이 머리를 짜서 하자고 했을까요? 아니면 어떤 요청이 있었을까요? 답은 Adobe Bug reporting 시스템에 있었습니다.

 

Adobe에서는 Adobe Bug Reporting 시스템을 운영하고 있습니다. 들어가보면 알겠지만 Flex, BlazeDS, Flash Player, ActionScript Compiler 주제로 개설되어 있고 개발자들의 요구사항 및 버그를 이곳에서 전부 받고 같은 요구사항이 있는 사람들이 투표하는 방식으로 진행되어 투표수가 많으면 Adobe에서 우선순위를 가려서 개발착수에 들어가지요.

Adobe Bug 리포팅 시스템 첫화면

 

 

Flex 4의 고급 CSS도 이런 과정을 통해 탄생된 겁니다.

 

https://bugs.adobe.com/jira/browse/SDK-14385

 

Flex CSS 지원해달라는 요구사항 페이지

 

Jacob Wright라는 사람이 리포팅을 했고 투표수가 75입니다. 그만큼 CSS의 기능이 더욱 확장되었으면 좋겠다는 사람의 수가 많은 거지요. 투표수가 많으니 Adobe 측에서도 무시할 수 없게 된 것이고 이번 Flex 4에서 지원하게 된 것입니다.

 

 

 

버그 리포팅에 참여하자.

 

그럼 우리도 참여할 수 있을까요?

때론 그럴겁니다 영어 못하기 때문에 참여 못한다고....

하지만 꼭 그렇지 않습니다. 자주 들어가 내용을 살펴보고 투표로써 작은 관심을 가져주는 것만으로도 충분합니다. 개발자중에서도 영어 잘하는 사람이 이런 글을 올립니다. 그럼 그 사람의 요청이 있으면 가서 투표해주면 되는 겁니다. 절대 어려운 일이 아닙니다. 아래에 버그 리포팅에서 추천(투표)의 중요성에 대해 알 수 있을 겁니다.

 

[열이아빠]플렉스 버그 리포팅에서 추천의 중요성

 

Adobe Flex/AIR 관련되어 한글문제가 꽤 있습니다. 이 한글문제를 해결하기 위해서도 이 버그 리포트 시스템을 이용하면 됩니다. 이미 Flash Platform 한글문제 공동대응팀이 생겼고 지속적으로 이 문제를 해결하기 위해 다양한 각도로 일하고 있습니다. 대응팀 총괄을 맡고 있는 이희덕씨 블로그에 관련 글이 많습니다.

 

[희희덕덕]한글문제 이슈 관련글

 

아래글은 열이아빠님이 쓰신 플렉스 버그 검색하는 방법입니다..

 

[열이아빠]플렉스 버그 검색해보기

 

투표에 참여해서 Flex/AIR/Flash 한글문제를 해결하는 방법입니다.

 

[희희덕덕]여러분의 참여로 한글 문제를 함께 해결해 봅시다!

 

 

앞으로 Flash Platform 한글문제 공동대응팀은 관련 자료를 종합하고 관리할 것입니다.

 

 

정리하며

 

여러분도 Adobe RIA 기술에 대한 불만 또는 원하는 사항들이 있을겁니다. 앞에서 설명드린데로 잘 정리해서 Adobe Bug Reporting 시스템에 올리시거나 투표를 적극적으로 참여함으로써 성취할 수 있는 겁니다. 한국 개발자들은 유독 영어 울렁증이 있다고들 호소하는데(저를 비롯) 사실 핑계라고 생각합니다. 필요하면 공부하면 되고요. 이런 일들은 꼭 영어를 잘해서 하는 일도 아니거든요. 한국 Adobe RIA 기술에 관련된 시장은 일본에 비해 거의 8배 정도 부족합니다.  그리고 적극성도 일본에 비해 많이 뒤쳐지며 나오는 컨텐츠의 창의력도 훨씬 뒤지는 편입니다. 왜 유독 일본어만 Flex Livedocs가 있을까요? Adobe는 시장성이 없는 한국에 한글문서 작성 인력을 투자하기에는 그들도 아깝다는 생각을 하는겁니다. 물론 Adobe는 그런 마음을 가지면 안되는 것이지만 또한 적극적이지 못한 우리도 반성해야될 일이라고 생각합니다. 우리가 안으로만 숨지않고 적극적으로 활동하면 Adobe에서 한국시장을 무시 못하게 될겁니다. 그런 날이 오길 반드시 바랍니다.

 

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

Flex 4는 Flex 3를 완전히 갈아 엎었다는 느낌이 든다. 기존 컴포넌트에서 없어진 것도 있고 새로 추가 된 것도 상당히 많다. 익숙해지려면 적지 않은 시간투자가 필요할 것 같다.

 

CSS에 네임스페이스 추가

 

Flex 4부터 CSS에 네임스페이스가 추가가 되었다. 이러한 구분이 필요한 것은 컴포넌트가 기존 halo 컴포넌트 외에 Spark 컴포넌트가 새로 추가가 되면서 필요해진 것이다. 즉 두개의 컴퍼넌트가 다른 네임스페이스를 사용하고 있기 때문에 CSS도 다른 네임스페이스를 쓰게 하는 것이다. 다음 소스를 보면 이해가 쉽겠다.

 

<?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="1024" minHeight="768">
    <mx:DateChooser id="main_calendar" x="20" y="20"/>
    <s:Button label="submit" x="220" y="20"/>
	<fx:Style>
	 
	    @namespace s "library://ns.adobe.com/flex/spark";
	    @namespace mx "library://ns.adobe.com/flex/halo";
	 
	    s|Button {
	        color: #FF0000;
	    }
	 
	    mx|DateChooser {
	        color: #0000FF;
	    }
	 
	</fx:Style>    
</s:Application>

 

 

<fx:Style>내에 @namespace를 정의하고 네임스페이스명|컴포넌트 의 형태로 CSS를 정의할 수 있도록 되었다. 그다지 어려운 사실은 아니다. ^^

 

이런 구조로 만들어진데에는 Flex 4가 Flash Catalyst와 연동이 되면서 디자이너와 개발자의 협업에 충실해지기 위한 거라고 한다. 예를 들어 Slider의 경우 liveDragging속성이 CSS쪽에서는 제어할 수 없던 반면 이번 Flex 4부터는 할 수 있게 되어 개발자는 코드상에서 디자이너는 CSS쪽에서 접근이 가능해져 협업이 되는 것이다. 디자이너의 경우 CSS를 직접 건드리지 않고 Flash Catalyst를 통해서 작업하기 때문에 CSS에 대해서는 몰라도 되지만 그 결과물은 CSS에 속성이 정의 되어 있음을 확인할 수 있을 것이다. 이러한 점이 개발자와 디자이너간에 협업을 가능하게 한 것이라고 생각한다.

 

 

고급 CSS (Advanced CSS)

 

Flex 3의 CSS에서 Selector는 단지 2개 였다. Class SelectorsType Selectors가 그것이다. 하지만 Flex 4부터는 이 Selector 종류가 크게 늘어났다. 이는 더욱 세밀하게 CSS 작업을 할 수 있다는 것을 의미한다. 이제 거의 HTML CSS와 맞먹을 정도인 것 같다. ^^ (CSS는 개인적으로 Flex의 강점중에 하나라고 생각한다. 기존 Flex 3 만으로도 강력한 CSS기능은 나로 하여금 Flex 매력에 푹 빠지게 했다.)

 

참고로 Class Selector와 Type Selector는 다음과 같다.

 

Type Selector

 

같은 종류의 컴포넌트에 적용된다. 다음과 같은 경우 spark.components.Button 및 이를 확장한 컴포넌트에 적용된다.

 

  s|Button { color: #CC0000; }

 

Class Selector

 

컴포넌트의 styleName을 지정한 컴포넌트라면 종류에 상관없이 적용이 된다.

 

  .header { background-color: #CCCCCC; }

 

 

아래부터는 Flex 4에서 추가된 CSS selector에 대해 예제와 함께 살펴본다.

 

1. ID Selectors

예전부터 컴포넌트 ID별로 CSS를 줄 수 있으면 좋겠다는 생각을 가졌다. Flex 4에 부터 적용되니 기쁘다. ID별로 CSS를 적용할 수 있기 때문에 같은 계열의 컴포넌트라도 하나의 객체로 생성된 유일한 1개의 컴포넌트에만 CSS를 적용할 수 있게 된다.

<?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="1024" minHeight="768">
    <s:Button id="btnTest" x="41" y="24" label="ID 적용됨"/>
    <s:Button x="128" y="24" label="ID 적용 안됨" />
	<fx:Style>
		#btnTest{
			color: #0000ff;
		} 
	</fx:Style>    
</s:Application>

 

ID가 btnTest로 지정된 것만 Label색이 파란색으로 지정된다. ID를 이용하기 때문에 네임스페이스 구분이 필요없다. 이제 컴포넌트 끼리 CSS 사생활 침해를 금지 시킬 수 있겠구나~ (농담)

 

 

2. Multiple class selectors

 

이는 Class selector가 확장된 개념이다. Class Selector는 중첩해서 사용할 수 있다. 아래 예제를 보면 쉽게 이해할 수 있을 것이다.

<?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="1024" minHeight="768">
	<mx:Label styleName="class1 class2" text="중첩 Class Selector 사용" x="8" y="40"/>
	<fx:Style>
		.class1 
		{
			color : #ff0000;
		}
		
		.class2
		{
			fontFamily : "궁서";
		}
	</fx:Style>    
</s:Application>

위 예제에서 Label에 styleName을 설정할 때 class selector를 2개 썼다. class1, class2는 각각 폰트색과 종류를 설정했는데 이 2개의 selector를 사용한 Label은 두개의 class selector에 정의가 적용된다. 결국 Label의 글자색은 빨강, 폰트는 궁서로 보이게 된다.

 

이렇게 class selector를 중첩이 가능하게 됨에 따라 중복되는 selector가 없도록 깔끔하게 CSS구성을 할 수 있게 되었다.

 

 

3. Descendant Selectors

이 selector는 자식/부모 관계에 있는 컴포넌트에 해당한다. Panel위에 Button을 놓았다고 하자. 이때 관계에서 Panel이 부모, Button은 자식이 된다. 특정 부모는 자식에게 어떤 것을 바랄 수 있게 된다. 그래서 사과를 줄수도 있고 바나나도 줄 수 있다. 여기에 특정 부모라는 말이 중요하다. 아무 부모나 자식에게 그렇게 주는 것을 금지한다. 이 관계를 이용한 것이 바로 Descendant selector이다.

 

<?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="1024" minHeight="768">
    <s:Panel x="14" y="42" width="250" height="200">
        <s:Button x="12" y="12" label="Button"/>
    </s:Panel>
    <s:Group x="33" y="393" width="200" height="200">
        <s:Button x="33" y="29" label="Button"/>
    </s:Group>
	<fx:Style>
	    @namespace s "library://ns.adobe.com/flex/spark";
	    @namespace mx "library://ns.adobe.com/flex/halo";
		s|Panel s|Button{
			color: #304F6B;
			baseColor: #5387B7;
		} 
	</fx:Style>    
</s:Application>

 

위 코드에서 볼 수 있듯이 Panel에 자식 Button만 CSS를 적용하도록 되어 있기 때문에 Group위에 올라간 Button에는 CSS적용이 안된다.

 

 

4. Pseudo selectors

 

Flex 4에서 상태변화에도 CSS를 적용할 수 있게 되었다. Flex 4에서는 Skin을 ProgrammaticSkin 기반이 아닌 SparkSkin기반으로 MXML형태로 제작할 수 있도록 되어  있다. flex4.swc에 spark.skins.*쪽을 훑어보기 바란다. Button의 경우 ButtonSkin.mxml이 존재하는데 아래와 같이 상태별로 다른 스킨을 줄 수 있도록 되어 있다.

<s:states>
	<s:State name="up" />
	<s:State name="over" />
	<s:State name="down" />
	<s:State name="disabled" />
</s:states>

 

이 상태에 따라서 CSS를 적용할 수 있도록 한것이 바로 Pseudo selector이다. 아래 예제에서 볼 수 있듯이 :를 이용해 컴포넌트의 상태에 따라 CSS를 주는 것을 확인할 수 있다.

<?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="1024" minHeight="768">
	<s:Button label="Test"/>
	<fx:Style>
	    @namespace s "library://ns.adobe.com/flex/spark";
	    @namespace mx "library://ns.adobe.com/flex/halo";
	 
		s|Button:up{
			color: #304F6B;
			baseColor: #5387B7;
		} 
		
		s|Button:down 
		{
			baseColor: #917541;
			color: #B8A553;
		}
		
		s|Button:over 
		{
			baseColor: #B8A553;
			color: #6B5630;
		}
	</fx:Style>    
</s:Application>

 

 

CSS Selector 사용하기

 

위에서 설명한 Selector 들은 독립적으로 사용하는 것외에도 다른 Selector와 함께 사용할 수 있다.

 

Flex 3에서는 아래와 같이 단순한 Type Selector와 범용적으로 사용되는 Class Selector만 사용할 수 있었다.

 

Button { color:#00FF00; }     /* Simple type selector */
.special { color:#FF0000; }     /* Universal class selector */

하지만 Flex 4부터는 새로운 Selector와 함께 섞어서 사용할 수 있게 되었다. 아래 CSS 적용 예시를 보자.

 

Button { color:#00FF00; }     /* 단순 type selector */
.special { color:#FF0000; }     /* 범용 class selector */
Button.special { color:#FF0000; }     /* class selector와 함께 사용된 Type selector  */
Button#b13 { color:#0000FF; }     /* ID selector와 함께 사용된 Type selector */
#b13 { color:#FF9933; }     /* 범용 id selector */
Panel VBox Button { color:#990000; }     /* Descendant selector */
Button:up { color:#FF9900; }     /* pseudo selector와 함께 사용된 Type selector  */
:up { color:#FF9933; }     /* 범용 pseudo selector */

 

위 내용을 좀더 새부적으로 이해하기 위해 예를 하나 들어보겠다.

 

Panel Button { color:#DD0000; }

 

위처럼 CSS Selector를 중첩한 경우 경우 다음과 같은 상황에 있는 컴포넌트에 color가 설정된다.

 

- Panel 위에 Button

- Panel을 확장한 컴포넌트 위에 Button

- Panel또는 Panel을 확장한 컴포넌트 위에 Button을 확장한 CheckBox와 같은 커스텀 Button

 

 

정리하며

 

Flex 4 CSS의 Selector를 외우기 쉽게 하기 위해 아래를 꼭 기억하자.

  • local name : type selector - 컴포넌트 종류
  • styleName : class selector - 컴포넌트 스타일명
  • identity : id selector - 컴포넌트 ID
  • state : pseudo selector - 컴포넌트 상태
  • display list : descendant selector - 컴포넌트 디스플레이 리스트 부모/자식 관계

 

참고글

 

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

Flex 3에서 DataGrid의 Header부분의 Separator는 headerSeparatorSkin 스타일로 정의되어 있다. 화면은 Flex DataGrid에 Separator가 붙은 보통의 모습이다.

 

 

위 프로그램은 아래와 같이 프로그래밍 한다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

만약 Separator를 지우고 싶다면 단순히 headerSeparatorSkin 에 mx.skins.ProgrammaticSkin으로 정의하면 된다. 아래는 실행화면과 소스코드이다. mx.skins.ProgrammaticSkin는 프로그램적으로 스킨을 만들 필요가 있을때 사용하는 클래스로 이 클래스 내부에서는 어떤 렌더링도 하지 않기 때문에 headerSeparatorSkin 을 이 클래스로 대체하는 것만으로도 Header의 separator를 삭제할 수 있는 것이다.

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid" 
		headerSeparatorSkin="mx.skins.ProgrammaticSkin">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

위 코드 대신 아래 코드처럼 <Style/> 블록이나 외부 .CSS 파일을 이용해서 headerSeparatorSkin의 스타일을 바꿀 수 있다.

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
    <mx:Style>
        DataGrid {
            headerSeparatorSkin: ClassReference("mx.skins.ProgrammaticSkin");
        }
    </mx:Style>	
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

또는 ActionScript 를 사용해 버튼을 누를때 동적으로 headerSeparatorSkin 스타일을 변경할 수 있도록 다음 코드처럼 만들 수 있다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.skins.ProgrammaticSkin;

            private function btn_click(evt:MouseEvent):void {
                dataGrid.setStyle("headerSeparatorSkin", ProgrammaticSkin);
            }
        ]]>
    </mx:Script>

    <mx:Button id="btn"
            label="Set header separator skin"
            click="btn_click(event);" />
            
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

좀더 파헤쳐보자.

 

DataGrid에는 drawSeparators() 메소드가 protected로 지정되어 있다. 만약 DataGrid를 커스터마이징해서 Separator 그리는 방식을 바꾸고 싶다면 drawSeparators() 부터 찾아가면 된다. 아래 코드는 DataGrid의 drawSeparators() 메소드 정의이다

/**
 *  Creates and displays the column header separators that the user 
 *  normally uses to resize columns.  This implementation uses
 *  the same Sprite as the lines and column backgrounds and adds
 *  instances of the headerSeparatorSkin and attaches mouse
 *  listeners to them in order to know when the user wants
 *  to resize a column.
 */
protected function drawSeparators():void
{
    DataGridHeader(header)._drawSeparators();
    if (lockedColumnHeader)
        DataGridHeader(lockedColumnHeader)._drawSeparators();
}

 

위 코드에서 볼 수 있듯이 결국 DataGridHeader에 정의된 _drawSeparators()쪽을 살펴봐야한다. 아래는 DataGridHeader의 _drawSeparators()와 drawSeparators() 부분이다.

mx_internal function _drawSeparators():void
{
    drawSeparators();
}

/**
 *  Creates and displays the column header separators that the user 
 *  normally uses to resize columns.  This implementation uses
 *  the same Sprite as the lines and column backgrounds and adds
 *  instances of the headerSeparatorSkin and attaches mouse
 *  listeners to them in order to know when the user wants
 *  to resize a column.
 */
protected function drawSeparators():void
{
    if (!separators)
        separators = [];

    var lines:UIComponent = UIComponent(getChildByName("lines"));
    
    if (!lines)
    {
        lines = new UIComponent();
        lines.name = "lines";
        addChild(lines); 
    }
    else
        setChildIndex(lines, numChildren - 1);

    // required to deal with some 2.x clipping behavior
    lines.scrollRect = new Rectangle(0, 0, unscaledWidth, unscaledHeight + 1);

    if (headerSepSkinChanged)
    {
        headerSepSkinChanged = false;
        clearSeparators();
    }

    var n:int = visibleColumns ? visibleColumns.length : 0;
    
    if (!needRightSeparator && n > 0)
    	n--;
    
    for (var i:int = 0; i < n; i++)
    {
        var sep:UIComponent;
        var sepSkin:IFlexDisplayObject;
        
        if (i < lines.numChildren)
        {
            sep = UIComponent(lines.getChildAt(i));
            sepSkin = IFlexDisplayObject(sep.getChildAt(0));
        }
        else
        {
            var headerSeparatorClass:Class =
                getStyle("headerSeparatorSkin");
            sepSkin = new headerSeparatorClass();
            if (sepSkin is ISimpleStyleClient)
                ISimpleStyleClient(sepSkin).styleName = this;
            sep = new UIComponent();
            sep.addChild(DisplayObject(sepSkin));
            lines.addChild(sep);
            
            separators.push(sep);
        }
        // if not separator
        if ( !(i == visibleColumns.length-1 && !needRightSeparatorEvents) )
        {
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
        }
		else
		{
            // if not separator
            if ( (i == visibleColumns.length-1 && !needRightSeparatorEvents) )
            {
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
            }
		}

        var cols:Array = visibleColumns;
        if (!(cols && cols.length > 0 || dataGrid.headerVisible))
        {
            sep.visible = false;
            continue;
        }

        sep.visible = true;
        sep.x = headerItems[i].x +
                visibleColumns[i].width - Math.round(sepSkin.measuredWidth / 2);
        if (i > 0)
        {
            sep.x = Math.max(sep.x,
                             separators[i - 1].x + Math.round(sepSkin.measuredWidth / 2));
        }
        sep.y = 0;
        sepSkin.setActualSize(sepSkin.measuredWidth, Math.ceil(cachedHeaderHeight));
        
        // Draw invisible background for separator affordance
        sep.graphics.clear();
        sep.graphics.beginFill(0xFFFFFF, 0);
        sep.graphics.drawRect(-separatorAffordance, 0,
							  sepSkin.measuredWidth + separatorAffordance,
							  cachedHeaderHeight);
        sep.graphics.endFill();
		sep.mouseEnabled = true;
    }

    while (lines.numChildren > n)
    {
        lines.removeChildAt(lines.numChildren - 1);
        separators.pop();
    }
}

 

DataGridHeader의 drawSeparators()에서 headerSeparatorSkin 스타일을 사용하는 것을 찾아볼 수 있다. 개발자는 이 부분을 커스터마이징할 수 있는 것이다.

 

DataGrid나 DataGridHeader를 커스터마이징할 필요없이 스킨만 변경하고 싶다면 headerBackgroundSkin의 기본 스킨인 mx.skins.halo.DataGridHeaderSeparator를 커스터 마이징하거나 mx.skins.Programmatics를 확장해서 사용하면 되겠다. 아래코드는 DataGrid에서 headerBackgroundSkin이 Style로 정의된 것을 보여주고 있다.

/**
 *  The class to use as the skin that defines the appearance of the  
 *  background of the column headers in a DataGrid control.
 *  @default mx.skins.halo.DataGridHeaderSeparator
 */
[Style(name="headerBackgroundSkin", type="Class", inherit="no")]

 

 

아래 코드는 mx.skins.halo.DataGridHeaderSeparator를 보여준다.

////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2005-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.skins.halo
{

import flash.display.Graphics;
import mx.skins.ProgrammaticSkin;

/**
 *  The skin for the separator between column headers in a DataGrid.
 */
public class DataGridHeaderSeparator extends ProgrammaticSkin
{
	include "../../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function DataGridHeaderSeparator()
	{
		super();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden properties
	//
	//--------------------------------------------------------------------------

	//----------------------------------
	//  measuredWidth
	//----------------------------------
	
	/**
	 *  @private
	 */
	override public function get measuredWidth():Number
	{
		return 2;
	}
	
	//----------------------------------
	//  measuredHeight
	//----------------------------------

	/**
	 *  @private
	 */
	override public function get measuredHeight():Number
	{
		return 10;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	override protected function updateDisplayList(w:Number, h:Number):void
	{
		super.updateDisplayList(w, h);
		var g:Graphics = graphics;
		
		g.clear();
		
		// Highlight
		g.lineStyle(1, 0xFFFFFF, 0.5);
		g.moveTo(0, 0);
		g.lineTo(0, h);
		g.lineStyle(1, getStyle("borderColor")); 
		g.moveTo(1, 0);
		g.lineTo(1, h);
	}

}

}

 

이외로 단순해서 놀랬는가? 결국 이 스킨은 borderColor 스타일로 지정된 색으로 구분자 선만 그어준다. 나는 이것을 바꿔서 실선을 점선을 그리도록 해보겠다. 아래 코드는 이를 구현한 DataGridHeaderDotSeparator 클래스이다.

 

package
{
import flash.display.Graphics;

import mx.skins.halo.DataGridHeaderSeparator;

/**
 * DataGrid Header에 점선을 그린다.
 */ 
public class DataGridHeaderDotSeparator extends DataGridHeaderSeparator
{
	/**
	 *  Constructor.
	 */		
	public function DataGridHeaderDotSeparator()
	{
		super();
	}
	
	/**
	 *  @private
	 */		
	override protected function updateDisplayList(w:Number, h:Number):void
	{
		var g:Graphics = graphics;
		
		g.clear();
		
		
		g.lineStyle(1, getStyle("borderColor"), 1); 
		var i:int;
		for( i = 0; i < h; i+=4 )
		{
			g.drawCircle( 1.5, i, 0.5 );
		}
	}		
}
}

 

 

아래 코드는 DataGridHeaderDotSeparator 스킨을 사용하는 예제이다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid"
		headerSeparatorSkin="DataGridHeaderDotSeparator">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

아래는 위 코드를 실행한 화면이다. Header부분에 구분자(Separator)가 점선으로 되었다. 좀 뭉뚝하지만 그냥 예제일 뿐이니깐 넘어가자. ^^;

 

 

Flex는 이처럼 프로그램적으로 스킨을 변경할 수 있다. 물론 이미지를 이용하는 방법도 있다. 이와 같은 방법으로 스킨을 변경하는 것은 Flex의 모든 컴포넌트에서 지원하므로 이런 스킬에 익숙해질 필요가 있겠다.

 

원본 예제 : Removing the header separator on the DataGrid control in Flex

 

주절주절 : 그냥 이렇게 가볍게 포스팅하는 것이 좋을 것 같다는 생각이 든다.

 

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

 

Flash Player 10부터 3D를 구현할 수 있는 일련의 Native API 코드가 추가되었다. Papervision3D나 Away3D 처럼 멋진 3D 효과를 만들기에는 부족하긴 하지만 여러가지 시도는 해볼 수 있겠다.

 

Adobe에서 제공하는 Flash용 ActionScript 3.0 프로그래밍 가이드를 살펴보면 3차원(3D)에서 작업에 대한 내용이 있다. 이 문서만 보아도 3D 표현을 하는데 큰 도움을 받을 수 있다. 문서에는 몇가지 예제가 있는데 예제중에 UV맵핑에 올라온 예제를 가지고 몇가지 테스트 코드를 만들었다.

 

 

테스트에 참가할 나의 딸, 예진이의 사진이다. ^^

 

 

T값을 이용한 원근표현

 

UV 맵핑은 텍스쳐(Texture) 맵핑이다. 텍스쳐는 일반적인 2D 사진을 의미하며 이 사진을 삼각형의 형태로 쪼개서 여러가지 3D 표현을 할 수 있다. 사진가지고 깃발처럼 펄럭거리게 할 수 있고, 구의 형태로 만들수도 있는 것이다.

 

UV맵핑은 사진의 어느부분을 버텍스(Vertex, 꼭지점)으로 삼을것인가 지정한다. UV맵핑에서 U는 사진의 가로축을 말하며 V는 사진의 세로축을 의미한다. 가로축/세로축 범위는 0~1까지다. 그러므로 U값이 0이면 사진의 좌측맨끝부분을 의미하며 1이면 사진의 우측맨끝을 말한다. V값도 마찬가지로 0이면 사진의 위끝부분, 1이면 맨아래 부분을 의미한다. UV값은 3D 버텍스와 1:1 대응되도록 한다. 그래서 지정된 1개의 버텍스는 UV좌표에 지정된 사진의 좌표에 대응되어 맵핑이 이뤄지도록 하는 것이다. 이러한 맵핑을 할 수 있도록 하는 함수가 Graphics의 drawTriangle()이다. 이 함수의 1번째 인자가 버텍스값이고 3번째 인자가 UV데이타 값이다.

 

사진의 경우 이미지 그대로 화면에 표현하기 위해 삼각형이 최소 2개가 필요하다. 사진에서 표현되는 실제 버텍스는 4개이지만 각각 삼각형의 버텍스는 6개가 된다. 이중에 2개의 버텍스는 중복이 된다. 이 중복을 제거하기 위해 사용하는 것이 인덱스이다. 인덱스를 이용하면 삼각형을 그리기 위한 버텍스의 순서를 정의함으로서 중복된 버텍스를 없앤다. 이 값은 drawTriangle()의 2번째 인자값으로 지정할 수 있다.

 

여기서 사용한 T값은 버텍스의 위치가 3D 형태로 변하면서 사진도 이에 맞게 크기를 조절할 필요가 있기 때문에 사용하는 값이다. 이를 이용해 원근표현을 할 수 있게 된 것이다.

 

아래 코드를 보면서 UV데이타와 T값을 이용한 3D 표현법에 대한 기초를 공부할 수 있다.

 

(소스를 복사할때는 이 소스를 직접복사하지 말고 소스 위에 마우스를 올려 소스보기 아이콘이 뜰때 그것을 눌러 새창이 뜨면 소스를 긁어다가 사용하면 되겠다.)

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	import flash.utils.getTimer;
	
	/**
	 * 3D 회전. T값을 이용해 원근 적용 
	 * @author Yongho, Ji
	 * @since 2009.06.30
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WS509D19BB-239B-4489-B965-844DDA611AE7.html
	 */ 
	public class Baby3D extends Sprite
	{
		[Embed(source="mybaby.png")]
		private var ImageClass:Class;
		
		//평면 버택스 좌표와 t값 
		private var x1:Number = -100, y1:Number = -100, z1:Number =0, t1:Number = 0;
		private var x2:Number = 100, y2:Number = -100, z2:Number =0, t2:Number = 0;
		private var x3:Number = 100, y3:Number = 100, z3:Number =0, t3:Number = 0;
		private var x4:Number = -100, y4:Number = 100, z4:Number =0, t4:Number = 0;
		
		//초점거리 
		private var focalLength:Number = 200;
		
		//버텍스에 대한 인덱스 
		private var indices:Vector.<int>;
		
		private var container:Sprite;
		
		private var bitmapData:BitmapData;

		public function Baby3D()
		{
			super();
			
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			//1평면당 2개의 삼각형을 가진다. 
			indices = new Vector.<int>;
			indices.push( 0,1,3, 2,3,1 );
			
			container = new Sprite();
			container.x = 200;
			container.y = 200;
			addChild( container );
			
			var bitmap:Bitmap = new ImageClass();
			bitmapData = bitmap.bitmapData;
			this.addEventListener( Event.ENTER_FRAME, rotatePlane );	
		}
		
		private function rotatePlane( event:Event = null ):void
		{
			//버텍스 회전 
			var ticker:Number = getTimer() / 400;
			z2 = z3 = -(z1 = z4 = 100 * Math.sin( ticker ) );
			x2 = x3 = -(x1 = x4 = 100 * Math.cos( ticker ) );
			
			//t값 계산 
			t1 = focalLength / ( focalLength + z1 );
			t2 = focalLength / ( focalLength + z2 );
			t3 = focalLength / ( focalLength + z3 );
			t4 = focalLength / ( focalLength + z4 );
			
			//삼각형 버택스
			//t값을 이용해서 버텍스 좌표에 원근값을 주도록 한다.  
			var vertices:Vector.<Number> = new Vector.<Number>;
			vertices.push( 
					x1*t1,y1*t1, 
					x2*t2,y2*t2, 
					x3*t3,y3*t3, 
					x4*t4,y4*t4 
			);
			
			// UV 맵핑 
			// T값을 이용해  원근값이 적용된 맵핑이미지를 만들기 위해  
			var uvtData:Vector.<Number> = new Vector.<Number>;
			uvtData.push( 
					0,0,t1,
					1,0,t2,
					1,1,t3,
					0,1,t4
			);	
			
			//그리기 
			container.graphics.clear();
			container.graphics.lineStyle( 1, 0xff0000, 1.0 );
			container.graphics.beginBitmapFill( bitmapData );
			container.graphics.drawTriangles( vertices, indices, uvtData, TriangleCulling.NONE );
			container.graphics.endFill();							
		}
	}
}

 

 

위 코드는 Adobe에서 제공하는 예제를 약간 변형한 것이다. 실행결과는 다음과 같다.

 

소개한 코드는 그냥 실행하는데 그치는 것은 좋지 못하다. 값을 적절하게 변경해가면서 어떻게 변경되는가 알아보는 것도 좋은 학습법이라 생각한다.

 

 

DisplayObject의 Y축을 이용한 회전. 원근투영 이용

 

위 예제와 UV맵핑은 동일하지만 T값은 1로 고정한다. 그리고 Y축만 회전하고 원근표현은 T값 대신에 perspectiveProjection 속성을 이용한다.

 

T값을 1로 한다는 것은 T값을 가지고 원근표현을 하지 않겠다는 의미이다. 아래 예제를 보면 알겠지만 T값이 변하지 않기 때문에 EnterFrame 이벤트 발생시마다 그려주는 대신 생성자에서 한번만 그렸다.

 

원근투영(Perspective Projection)은 Flash Player 10에서 기본적으로 지원해준다. 가장 단순한 투영방식으로 하나의 소실점을 가지도록 하는 Linear한 투영방식이다. 만약 이 투영방식을 사용하지 않는다면 Matrix3D를 이용해 다른 방식의 원근투영방식을 적용해야 한다.

 

아래 코드에서 root.transform.perspectiveProjection.fieldOfView는 100으로 설정되어 있다. 이 값은 T값으로 원근표현할때와 비슷한 느낌을 주기 위한 값이다. fieldOfView 0~180까지 적용할 수 있다. 0은 말그대로 매우 먼곳에서 대상을 바라보는 것과 같다. 즉, 왜곡이 없는 원통투영이 된다. 하지만 180에 가까워 질 수록 대상과 가까이서 보는 느낌을 주게 된다. 한개의 소실점을 가지는 원근투영이 된다. 이 값을 적절하게 조절하면서 실행해보자 무슨 의미인지 알 수 있을 것이다.

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	
	/**
	 * 3D 회전. 원근 투영을 이용
	 * @author Yongho, Ji
	 * @since 2009.06.30
	 * @see http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WS36223081-8938-4b45-BB89-F1F8B1A52E4E.html
	 */ 
	public class Baby3D_2 extends Sprite
	{
		[Embed(source="mybaby.png")]
		private var ImageClass:Class;		
		
		private var container:Sprite;
				
		public function Baby3D_2()
		{
			super();
			
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;
			
			container = new Sprite();
			container.x = 200;
			container.y = 200;
			addChild( container );
			
			//원근투영의 FiendOfView 설정. 기본값은 55임 
			root.transform.perspectiveProjection.fieldOfView = 100;
			
			//비트맵 데이타 생성 얻어오기 
			var bitmap:Bitmap = new ImageClass();
			var bitmapData:BitmapData = bitmap.bitmapData;

			//버텍스 
			var vertices:Vector.<Number> = new Vector.<Number>;
			vertices.push( 
					-100,-100, 
					100,-100, 
					100,100, 
					-100,100 
			);
			
			//위 베텍스에 대한 인덱스 , 1평면당 2개의 삼각형을 가지고 있다. 
			var indices:Vector.<int> = new Vector.<int>;
			indices.push( 0,1,3, 2,3,1 );

			//UV 맵핑 데이타  
			var uvtData:Vector.<Number> = new Vector.<Number>;
			uvtData.push( 
					0,0,1,
					1,0,1,
					1,1,1,
					0,1,1
			);	
			
			//그리기 
			container.graphics.clear();
			container.graphics.lineStyle( 1, 0xff0000, 1.0 );
			container.graphics.beginBitmapFill( bitmapData );
			container.graphics.drawTriangles( vertices, indices, uvtData, TriangleCulling.NONE );
			container.graphics.endFill();				
				
			this.addEventListener( Event.ENTER_FRAME, rotatePlane );
		}

		private function rotatePlane( event:Event = null ):void
		{
			//컨테이너 회전 
			container.rotationY -= 5;
		}
	}
}

 

 

 

실행된 이미지가 조금 다르게 보일 것이다. T값을 이용한 원근표현 방식의 경우 렌더링을 계속하기 때문에 삼각형 선(빨간색)이 왜곡되지 않는다. 하지만 원근투영방식을 이용하면 한번 렌더링을 해주고 그것 자체를 회전시키는 방식이기 때문에 삼각형 선이 왜곡되어 보인다.

 

위 예제들은 쉬운 코드들이긴 하지만 UV맵핑, T값, 원근투영, 버텍스 등에 대한 이해가 없다면 어려울 수 밖에 없다. 3D 프로그래밍을 한번도 안한 분이라면 다른 서적이나 인터넷을 통해 학습자료를 찾아 공부하면 좋겠다는 생각이 든다.

 

Flash Player 10의 3D 기법은 아주 기초 수준이기 때문에 Papervision3D, Away3D처럼 화려한 3D를 만드는 것은 기대하기 어렵다. 하지만 Native수준에서 기능을 제공하므로 빠른 연산이 가능해서 간단한 3D 구현하겠다면 위 예제 처럼 만들자. 앞으로 Papervision3D, Away3D도 Flash Player 10 기반으로 제공한다고 하니깐 조금더 향상된 퍼포먼스를 가진 Flash 3D 애플리케이션을 기대해도 좋을 것 같다.

 

참고글

3차원(3D)에서 작업

 

ActionScript 3.0(이하 AS3)은 Java와 매우 유사한 구조를 가진다. 그러므로 Java를 공부한 사람이라면 AS3를 쉽게 접근할 수 있게 된다. C++을 했던 사람도 비슷한 구조때문에 쉽게 접근이 가능하다.

 

AS2를 공부했던 사람은 객체지향적으로 설계된 AS3를 공부하면 된다.

 

여기서는 ActionScript 3.0에 대해서 어떤 절차없이 생각나는대로 적어보았다. 매우 개인적인 생각이기 때문에 반박하고 싶으신 내용도 있을지 모르겠다. 그렇다면 얼마든지 댓글 환영이다. ^^

 

 

Java와 ActionScript 3.0 차이점

 

Java와 AS3간에 차이점을 보여주는 표이다. 쭉 훑어보자.

http://blog.naver.com/surfwon/30049390718

 

 

ActionScript 3.0의 아쉬운점

 

AS3는 객체지향기반의 언어이지만 몇가지 아쉬운 점이 존재한다.

 

1. 메소드(함수)의 오버로드(overload)를 할 수 없다.

 오버로드는 클래스안에 같은 이름의 함수를 중복해서 사용할 수 있는 기능이다. AS3에서는 Java나 C++에서 지원되는 이 오버로드 기능이 없기 때문에 개발시 아에 생각을 하지 않게 하는 장점도 있지만 정말 필요할때 다른 방법으로 대체해야하는 불편함이 생긴다. 아래글을 보자.

 http://blog.jidolstar.com/484

 

2. 생성자는 하나만 존재한다.

위 오버로드를 지원하지 않는 것과 일맥 상통하는 내용이다.

 

3. 추상클래스가 지원되지 않는다.

Java와 같은 abstract 키워드가 존재하지 않아 추상클래스를 만들 수 없다. 하지만 throw등을 이용해 컴파일시가 아닌 런타임에서 동작하는 추상클래스는 제작이 가능하다. 추상클래스는 AS3에도 클래스 설계시 꼭 필요하지만 아직 지원하지 않아 아쉬운 부분이다. 아래 글을 보자.

http://blog.jidolstar.com/452

 

 

4. private 생성자가 지원되지 않는다.

private 생성자는 외부에서 클래스 객체를 임의로 만드는 것을 금지시켜주는 역할을 한다. 하지만 AS3에서는 생성자에 public외에는 private, protected 를 쓸 수 없다. private 생성자가 필요한 단적인 예는 싱글턴 패턴을 구현할 때인데 AS3에서는 아래와 같이 다른 방법으로 싱글턴 패턴을 구현한다.

http://koko8829.tistory.com/304

 

아래 링크는 싱글턴 패턴을 응용한 형태이다.

http://blog.jidolstar.com/468

 

 

 

ActionScript 3.0을 왜 사용해야하는가?

 

AS2를 주로 했던 사람은 AS3의 강력함을 잘 느끼지 못해서 전향하지 않는 사람들이 많은 것 같다. 아래 글을 보고 AS3를 왜 해야하는가 알아보자.

 

AS3를 왜 사용해야하는가? : http://ddongkang.tistory.com/76 

 

 

ActionScript 3.0 의 재미

 

초보자가 Java, C++에서 느끼지 못하는 AS3의 재미는 처음 만드는 애플리케이션이 시각화된 결과물이라는 점일 것이다. 이 게시판에 앞어 간단한 AS3프로젝트를 통해 3D를 단 몇줄도 안되는 코드로 구현했던 것을 보면 그 의미를 알 것이다. 또한 AS3를 하다보면 매우쉽게 데이터통신, 데이터제어, 각종 미디어 기술지원, Flex, AIR로의 개발확장성등으로 인해 C++, Java와는 또 다른 재미를 느끼게 될 것이다.

 

 

ActionScript 3.0 공부하자.

 

AS3는 얼마든지 공부할 수 있다. 좋은 동영상 강좌도 있다.

AS3 강좌모음 : http://ddongkang.tistory.com/73

 

AS3 학습을 위해 아래 링크의 문서와 친해지자.(한글이라서 좋다!)

http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/

http://help.adobe.com/ko_KR/AS3LCR/Flash_10.0/

 

위 문서는 영문도 있다.

http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/

http://help.adobe.com/en_US/AS3LCR/Flash_10.0/

 

 

 

Flash 기술에 대한 오해

 

아래 글에 오해하시는 분들이 많으신 것 같아요. 아래 제 댓글을 보시고 제가 보는 관점에서 아래 글을 판단해주셨으면 합니다. ^^

 

C++,  Java하시던 분(기초수준 및 서버개발만 주로 한 분들)이 AS3나 Flex를 접할때 잘못된 태도가 있다. 내가 좀 언어좀 하는데 그냥 쉽게쉽게 할 수 있겠지라는 마인드이다. 또는 Flash인데 그것도 언어야?... 이런 마인드.. 금물이다.

 

AS3나 Flex도 그만의 독특한 구조와 기능이 있다. C++, Java만 했던 사람이 데이터 바인딩, 메타데이터태그, 이벤트 모델, 비디오제어, 음원제어, 마이크로폰 제어, 디스플레이객체 다루기, 응용애플리케이션구조,각종보안, XML 다루기등에 대해서 잘 알리가 없지 않은가? 그만큼 Flash 기술이 단순히 그래픽을 위한 기술이다라고 오해하는데서 비롯된다고 생각한다. Flash 기술은 이미 데스크탑에서 모바일, 각종운영체제, 각종 브라우져에 MS계열의 기술보다 더욱 빠르고 거대하게 확장되어 이미 우리 실생활에 적용되고 있다. 그러므로 AS3.0을 공부할때 너무 가벼운 마음으로만 공부한다는 생각은 버리자. 요즘 안드로이드 폰에 Flash 지원하고 앞으로 AIR로 각종 모바일 프로그램을 만들 수 있게 된다는 것을 생각하면 가벼운 기술은 아니구나라는 생각을 할 수 있을 것이다.(못믿겠으면 여기를 본다) Flash는 이미 클라이언트 기반을 넘어 서버기반의 기술도 지원해주어 진정한 RIA의 선봉장에 있다. 이러한 점을 잘 이해하고 Flex,AS3,AIR,Flash등을 공부하는 것이 좋겠다.

 

+ Recent posts