2012-06-15 3 views
0

MXML에서 이동하고 ActionScript 내에 사용자 지정 구성 요소 컨트롤을 빌드했습니다.사용자 지정 복합 컨트롤이 VGROUP에 다시 추가 된 후 0.5-1 초 동안 올바르게 렌더링되지 않습니다.

컨트롤이 올바르게 표시됩니다. 문제는 표시 목록에서 제거하고 .addElement (control) 메서드를 사용하여 다시 추가 한 후에 발생합니다.

다음은 다시 다시 추가하는 코드입니다.

private function displayParameters(parameters:ArrayCollection):void{ 

    for(var index:int = 0; index<parameters.length; index++){ 

     if(parameters[index] is ReportControl){ 

      var control:ReportControl = parameters[index] as ReportControl; 
      control.percentWidth = 100; 
      vgParameters.addElement(control); 
     } 
    } 
} 

ReportControl

는 아래와 같다 comboBoxMultiSelect 기본 클래스이다. ReportControl에 대한 그래픽 특수 문자는 없으며 구체적인 구현 (다형성)을위한 프로그래밍 인터페이스 역할을합니다.
public class comboBoxMultiSelect extends ReportControl{ 

    [Embed("../Assets/Icons/plus-16.png")] 
    private var plusIcon:Class; 
    [Embed("../Assets/Icons/minus-16.png")] 
    private var minusIcon:Class; 

    private var expanded:Boolean = false; 
    private var buttonIconChanged:Boolean = false; 

    private var _drp:ComboBox; 
    private var _btnMultiple:Button; 
    private var _horizontalGroup:HGroup; 
    private var _multiSelector:ReportGridSelector; 

    private var _multiSelection:Boolean = true; 
    private var bMultiSelectionChanged:Boolean = false;   

    public function ToggleExpanded():void{ 
     expanded = !_expanded; 
     buttonIconChanged = true; 

     invalidateSize(); 
     invalidateProperties(); 
     invalidateDisplayList(); 
    } 

    public function comboBoxMultiSelect(){ 
     super(); 
    } 

    override protected function createChildren():void{ 

     super.createChildren();    

     if(!_horizontalGroup){ 
      _horizontalGroup = new HGroup(); 
      _horizontalGroup.gap = 0; 
      _horizontalGroup.percentWidth = 100; 
      _horizontalGroup.height = ReportControl.SIZE_DEFAULT_HEIGHT; 
      addChild(_horizontalGroup); 
     } 

     if(!_drp){ 
      _drp = new ComboBox(); 
      _drp.text = GuiText; 
      _drp.percentWidth = 100; 
      _drp.height = ReportControl.SIZE_DEFAULT_HEIGHT; 
      _horizontalGroup.addElement(_drp); 
     } 

     if(!_btnMultiple && _multiSelection){ 
      _btnMultiple = new Button; 
      _btnMultiple.setStyle("icon", plusIcon); 
      _btnMultiple.width = 20; 
      _btnMultiple.height = ReportControl.SIZE_DEFAULT_HEIGHT; 
      _btnMultiple.visible = true; 
      _btnMultiple.addEventListener(MouseEvent.CLICK, 
         function(event:MouseEvent):void{ 
           ToggleExpanded(); 
         }); 
      _horizontalGroup.addElement(_btnMultiple); 
     } 
    } 

    override protected function commitProperties():void{ 
     super.commitProperties(); 

     if(buttonIconChanged){ 

      if(_expanded==true){ 
       _btnMultiple.setStyle("icon", minusIcon); 
      } 
      else{ 
       _btnMultiple.setStyle("icon", plusIcon); 
      } 
      buttonIconChanged = false; 
     } 

    } 

    override protected function updateDisplayList(unscaledWidth:Number, 
             unscaledHeight:Number):void{ 

     super.updateDisplayList(unscaledWidth, unscaledHeight); 

     _horizontalGroup.width = unscaledWidth; 
     _horizontalGroup.height = unscaledHeight; 
    } 

    override protected function measure():void{ 

     super.measure(); 
     measuredMinWidth = measuredWidth = ReportControl.SIZE_DEFAULT_WIDTH; 

     //minimum size  //default size 
     if(_expanded==true) 
      measuredMinHeight= measuredHeight = 200;    
     else 
      measuredMinHeight= measuredHeight = 
           ReportControl.SIZE_DEFAULT_HEIGHT; 
    } 
} 

내가 vgParameters.addElement(control)을 사용하여 다시 컨트롤을 추가

comboBoxMultiSelect가 제대로 렌더링되지 않습니다. 버튼 _btnMultiple 안에있는 plusIcon은 처음에는 올바르게 포지셔닝되지 않았지만 나중에 0.5-1 초 후에 자동으로 바로 정정됩니다.

나는 comboBoxMultiSelect 내에 문제가 있음을 확신하지만 아이콘을 강제로 같은 위치에 두는 방법을 확신하지 못합니다.

내 열심히 노력한 결과 매우 귀찮았습니다. 내가 뭘 잘못하고 있는지에 대한 아이디어가있는 사람이 있습니까?

감사합니다 :)

UPDATE -----> 여기에 띄는

[Event (name= "controlChanged", type="Reporting.ReportControls.ReportControlEvent")] 
[Event (name= "controlIsNowValid", type="Reporting.ReportControls.ReportControlEvent")] 
public class ReportControl extends UIComponent 
{ 
    private var _guiText:String; 
    private var _amfPHPArgumentName:String; 
    private var _reportResult:ReportResult; 
    private var _sequence:int; 
    private var _reportId:int; 
    private var _controlConfiguration:ReportParameterVO; 
    private var _isValid:Boolean = false; 
    internal var _selection:Object; 

    /** 
    * SIZE_DEFAULT_HEIGHT = 22 
    */ 
    internal static const SIZE_DEFAULT_HEIGHT:int = 22; 

    /** 
    * SIZE_DEFAULT_WIDTH = 150 
    */ 
    internal static const SIZE_DEFAULT_WIDTH:int = 150; 

    public function get ControlConfiguration():ReportParameterVO{ 
     return _controlConfiguration; 
    } 

    public function set ControlConfiguration(value:ReportParameterVO):void{ 

     _controlConfiguration = value;    
     _guiText = (value ? value.GuiText:""); 
     _amfPHPArgumentName = (value ? value.AMFPHP_ArgumentName: ""); 
     _sequence = (value ? value.Sequence : null); 
     _reportId = (value ? value.ReportId : null);    
    } 

    public function get IsValid():Boolean{ 
     return _isValid; 
    } 

    public function get ReportID():int{ 
     return _reportId; 
    } 

    public function get Sequence():int{ 
     return _sequence; 
    } 

    public function get ControlRepResult():ReportResult{ 
     return _reportResult; 
    } 
    public function set ControlRepResult(value:ReportResult):void{ 
     _reportResult = value; 
    } 

    internal function set Selection(value:Object):void{ 
     _selection = value; 
    } 

    internal function get Selection():Object{ 
     return _selection; 
    } 

    public function get ParameterSelection():Object{ 
     return _selection; 
    } 

    public function get GuiText():String{ 
     return _guiText; 
    } 

    public function get AmfPHPArgumentName():String{ 
     return _amfPHPArgumentName; 
    } 

    public function ReportControl(){ 
     //TODO: implement function 
     super(); 
    } 

    public function dispatchControlChanged():void{ 
     this.dispatchEvent(new ReportControlEvent(ReportControlEvent.CONTROL_CHANGED, this, true)); 
    } 
    public function dispatchControlIsNowValid():void{ 
     this.dispatchEvent(new ReportControlEvent(ReportControlEvent.CONTROL_IS_NOW_VALID, this, true)); 
    } 

    public function addSelfToValueObject(valueObject:Object):Object{ 
     valueObject[AmfPHPArgumentName] = _selection; 
     return valueObject; 
    } 

} 
+0

"왜 천천히 MXML에서 벗어나고 있습니까?" 이 간단한 복합 클래스를 만드는 데 몇 시간이 걸렸을 것입니다. MXML로 작성한 경우 몇 분이 걸렸을 것이며 프레임 워크가 어려움을 처리하게 할 것입니다. 그것의 이득은 어디에 있습니까? 레이아웃 (mxml)을 비헤이비어 (as)와 분리하려는 경우 원한다면이를 수행하는 방법을 보여줄 수 있지만 질문에 대한 답변이되지는 않습니다. – RIAstar

+0

우리는 미래에 필요할 수있는 'X'개의 제어 유형에 대한 추상화 형식이 필요했습니다. MXML을 사용하여 (분명히 더 빨랐을지라도) 어느 정도 제어 할 수 있었을 것이라고 생각했습니다. 또한 플렉스를 학습하고 있으므로 구성 요소 라이프 사이클을 뛰어 넘는 것보다 더 좋은 방법은 무엇입니까? – DynaWeb

+0

구성 요소 수명주기를 배우는 것이 하나의 추상입니다. 이 컨텍스트에서 추상화가 의미하는 바를 정확히 이해하지는 않지만 다양한 형태 (스킨)를 취할 수있는 하나의 호스트 구성 요소를 작성할 수있는 Spark 스키닝 아키텍처를 살펴보아야합니다. 다음은이 개념에 대한 아주 간단한 예제 인 내 대답입니다. http://stackoverflow.com/questions/9930740/what-is-the-hostcomponent/9931286#9931286. (불행하게도 그것은 매우 간단하여 아무런 행동도하지 않습니다.) 대부분의 Spark 구성 요소는이 개념으로 구성됩니다. – RIAstar

답변

1

: 그런 다음 크기와는 updateDisplayList()이 같은 그들을 놓습니다. 그것은 당신의 질문에 대한 답이 아니지만, 당신이 흥미로울 것이라고 생각했습니다. 간결함을 위해 구성 요소보다 다소 간단하게 만들어야 할 것입니다. 그리고 질문에 대한 코드 일부를 제거한 것으로 보이기 때문에 정확히 무엇을해야하는지 알 수 없습니다.

버튼을 클릭하면 일반 상태와 확장 상태를 전환 할 수있는 구성 요소가됩니다. 먼저 스킨 클래스를 만듭니다. 일반적으로 먼저 호스트 구성 요소를 작성하지만이 방법을 설명하는 것이 더 쉬울 것입니다.

<!-- my.skins.ComboBoxMultiSelectSkin --> 
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" 
     xmlns:s="library://ns.adobe.com/flex/spark" 
     height.normal="25" height.expanded="200"> 

    <fx:Metadata> 
     [HostComponent("my.components.ComboBoxMultiSelect")] 
    </fx:Metadata> 

    <s:states> 
     <s:State name="normal" /> 
     <s:State name="expanded" /> 
    </s:states> 

    <s:layout> 
     <s:HorizontalLayout gap="0" /> 
    </s:layout> 

    <s:ComboBox id="comboBox" width="100%" /> 
    <s:Button id="toggleButton" width="20" 
       icon.normal="@Embed('../Assets/Icons/plus-16.png')" 
       icon.expanded="@Embed('../Assets/Icons/minus-16.png')"/> 

</s:Skin> 

따라서 구성 요소의 모양과 배치 방법을 완전히 설정했습니다. 두통이 사라지는 것을 느끼십니까? 나는 이것을 아주 우아하게 생각한다. 두 상태가 있으며 버튼의 아이콘처럼 구성 요소의 높이가 현재 선택된 상태로 조정됩니다. 상태가 전환되는 방법과시기는 구성 요소 동작이며 호스트 구성 요소에 정의됩니다.

이제 일반 ActionScript에서 호스트 구성 요소를 만듭니다. 이를 위해 SkinnableComponent를 확장합니다 (UIComponent 대신 SkinnableComponent를 확장하는 경우 ReportControl을 확장 할 수도 있음).

[SkinState("normal")] 
[SkinState("expanded")] 
public class ComboBoxMultiSelect extends SkinnableComponent { 

    [SkinPart(required="true")] 
    public var toggleButton:IEventDispatcher; 

    [SkinPart(required="true")] 
    public var comboBox:ComboBox; 

    private var expanded:Boolean; 

    override protected function partAdded(partName:String, instance:Object):void { 
     super.partAdded(partName, instance); 

     switch (instance) { 
      case toggleButton: 
       toggleButton.addEventListener(MouseEvent.CLICK, handleToggleButtonClick); 
       break; 
      case comboBox: 
       comboBox.addEventListener(IndexChangeEvent.CHANGE, handleComboSelection); 
       break; 
     } 
    } 

    private function handleToggleButtonClick(event:MouseEvent):void { 
     toggleExpanded(); 
    } 

    private function handleComboSelection(event:IndexChangeEvent):void { 
     //handle comboBox selection 
    } 

    protected function toggleExpanded():void { 
     expanded = !expanded; 
     invalidateSkinState(); 
    } 

    override protected function getCurrentSkinState():String { 
     return expanded ? "expanded" : "normal"; 
    } 
} 

좋아, 여기에는 더 많은 것이 있습니다. SkinState 메타 데이터 선언에서

  • 첫 번째보기는 : 피부 클래스가 구성 요소에 할당 될 때, 컴파일러는 피부가 구현 필요한 상태가 있는지 여부를 확인합니다.
  • 그런 다음 SkinPart 선언 : 호스트 구성 요소의 속성 이름이 스킨 클래스의 태그 ID와 정확히 일치해야합니다.requiredtrue으로 설정되어 있기 때문에 컴파일러는 이러한 구성 요소가 실제로 스킨에 존재하는지 여부를 확인합니다. 옵션 스킨 파트가 필요한 경우 false으로 설정합니다.
  • toggleButton의 유형은 IEventDispatcher입니다. 호스트 구성 요소의 관점에서 모두 toggleButton, CLICK 이벤트를 전달해야합니다. 이것은 우리가 지금 <s:Image id="toggleButton" source="..." />으로 피부를 만들 수 있고 모든 것이 똑같이 계속 작용할 수 있다는 것을 의미합니다. 이것이 얼마나 강력한 지 보아라.
  • skinpart 속성이 즉시 할당되지 않기 때문에 구성 요소를 사용할 수있게 될 때마다 실행되는 partAdded() 메서드를 재정의합니다. 대부분의 경우 이것은 이벤트 리스너를 연결하는 곳입니다.
  • toggleExpanded() 메서드에서는 부울을 질문의 구성 요소처럼 토글하지만 피부 상태는 무효화합니다. 그러면 스킨이 getCurrentSkinState() 메서드를 호출하고 반환되는 값으로 상태를 업데이트합니다.

Et voilà! 행동 스크립트가 actionscript 클래스로 잘 분리 된 작업 구성 요소가 있고 레이아웃 복잡함에 대해 걱정할 필요가 없습니다. 동일한 동작으로 구성 요소를 만들고 싶지만 세로 대신 가로로 확장해야하는 경우 : height 대신 width을 조정하고 동일한 호스트 구성 요소에 할당하는 새 스킨을 만듭니다.

아! 나는 스킨을 구성 요소에 할당하는 방법을 알려주는 것을 거의 잊어 버렸습니다. 당신은 그것을 하나의 인라인을 수행 할 수 있습니다

<c:ComboBoxMultiSelect skinClass="my.skins.ComboBoxMultiSelectSkin" /> 

또는 스타일을 통해를 :

@namespace c "my.components.*"; 

c|ComboBoxMultiSelect { 
    skinClass: ClassReference("my.skins.ComboBoxMultiSelectSkin") 
} 
1

한 가지 updateDisplayList()의 구현에 ReportControl 코드를입니다. 아시다시피 이것은 구성 요소가 자식 객체의 크기를 조정하고 배치해야하는 위치입니다 (그리고/또는 프로그래밍 방식의 도면을 사용).

그러나 자식 개체의 너비/높이를 직접 설정하는 대신 Flex 수명주기 메서드 중 하나 인 setActualSize() 또는 setLayoutBoundsSize()을 사용해야합니다. 스파크 구성 요소에는 setLayoutBoundsSize()을 사용하십시오.

Flex 구성 요소의 너비/높이를 설정하면 구성 요소 자체가 무효화되므로 다음 업데이트주기에서 다시 렌더링 할 수 있습니다. 그러나 구성 요소를 updateDisplayList()으로 렌더링하려고하므로이 메서드 내부에서 자식 객체를 무효화하지 않도록주의해야합니다.

setActualSize()setLayoutBoundsSize() 메서드는 Flex 구성 요소의 너비/높이를 설정하지만 구성 요소를 무효화하지는 않습니다.

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void 
{ 
    super.updateDisplayList(unscaledWidth, unscaledHeight); 
    _horizontalGroup.setLayoutBoundsSize(unscaledWidth, unscaledHeight); 
    // if you wanted to position objects, you would set their x/y coordinates 
    // here with the move() or setLayoutBoundsPosition() methods 
} 

주, 일부 자식 개체뿐만 아니라 createChildren()에 크기가되고있는 것 같습니다 ... 그리고 그것은 기본 Flex 구성 요소는 (ReportControl 어떤 클래스를 확장 하는가?

을이 경우에 무엇인지 정말 분명하지 않다 이 방법을 사용하면 일 수도 있고 그 렌더링 결함을 제거 할 수 있습니다. 너비/높이 속성을 직접 설정하는 것보다 코드를 적게 실행합니다.

그것은이 요소에서 불필요한 가지 인 HGroup와 상호 작용을 할 수있다

[편집]. 이 방법으로 구성 요소를 만드는 것이 재미 있다고 생각하지만, 더 지루할 수 있습니다 ... 이것이 @RIAStar가 다른 접근법을 현명하게 지적한 이유입니다. 이 경로 아래로 계속하려면

몇 가지 더 아이디어 :

1) 당신이 createChildren()에서 일을 크기를 살펴보세요 - 예를의 HGroup는 percentWidth를 부여하지만, updateDisplayList()에있다 고정 너비가 주어진다면 (이것은 붉은 청어가 될 수 있지만 percentWidth는 설정하지 않습니다).

2) 구성 요소를 제거한 후 또는 다시 추가하기 전에 구성 요소의 유효성을 검사하도록 속일 수 있습니다. 시간 낭비일지도 모른다.

3) 구성 요소에서 'HGroup'을 제거하십시오. 그것은 불필요한 일입니다. 레이아웃 요구 사항은 Actionscript의 몇 줄을 사용하기에 충분히 간단합니다. 귀하의 마일리지는 레이아웃 요구 사항이 더 복잡해질수록 다양합니다!

에서 콤보 상자와 버튼을 UIComponent에 직접 추가하십시오. 내가 당신에게 나는 우리가 위의 댓글에서 언급 한 불꽃 스키닝 아키텍처 무엇을 의미하는지의 예를 제공하기 위해 노력할 것이다

override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void 
{ 
    super.updateDisplayList(unscaledWidth, unscaledHeight); 
    var padding:Number = 10; 
    var gap:Number = 0; 

    // make the ComboBox consume all of the width execpt for 20px and gap + padding 
    var availableWidth:Number = unscaledWidth - 20 - gap - (2*padding); 
    _drp.setLayoutBoundsSize(availableWidth, unscaledHeight); // combo box 100% width 
    _btnMultiple.setLayoutBoundsSize(20, unscaledHeight); // button is 20px wide 

    // now position them ... 
    // probably should not use 0, rather calculate a Y coordinate that centers them 
    // in the unscaledHeight 
    _drp.setLayoutBoundsPosition(padding, 0); 
    _btnMultiple.setLayoutBoundsPosition(unscaledWidth - padding - 20, 0); 
} 
+0

감사합니다 Sunil D, 나는 그것을 시험해 보겠습니다. 'ReportControl' 기본 클래스는 UIComponent를 직접 확장합니다. 그리고 간단한 게터와 세터가 포함되어 있습니다. 나는 그것을위한 코드를 게시 할 것이다. – DynaWeb

+0

불행히도 그 트릭을하지 않았다. – DynaWeb

+0

나는 더 많은 아이디어가있는 나의 대답을 편집했다. 그러나, 당신은 @ RIAStar의 현명한 조언을 고려할 수도 있습니다 :) –

0

감사합니다 모두 당신의 답변을 너무 많이! 개념을 설명하는 데있어서 고려해야 할 세부 사항에 대한 배려와 관심은 대단합니다! Très bien!

@RIAstar 그러나 이미 코드의 양 때문에 (비주얼 엘리먼트를 비헤이비어와 분리하면) 아키텍처를 변경하면 코드의 재 팩터가 커지고, 그렇지 않은 피쳐에는 많은 비용이 듭니다. 명시 적으로 요청되었습니다. (런타임에 변경 될 수있는 컨트롤의 시각적 표현) 확실히 흥미 롭습니다. 향후 버전에 추가 할 예정입니다.

그렇다면 내 문제에 대한 해결책을 찾을 수 있었다고 생각합니다. 필자는 @SunilD.를 추가하기 전에 컨트롤의 유효성을 검증하라는 제안을하기로 결정했습니다. 내가 아는 해킹이지만, 인간은 완벽하지 않으므로 코드도 사용할 수 없습니다. ;-)

컨트롤을 보면 이미지 렌더링에 문제가있는 버튼 만있는 것으로 나타났습니다. 그래서 테스트를 위해 아이콘이있는 버튼 인스턴스 에 추가하고 제거했으며 동일한 동작을 보았습니다. (comboBoxMultiSelect가 구현 된 방법과 관계없이) I 또한은 버튼이 처음 생성되었을 때 버튼을 보지 못했음을 알았습니다. 디스플레이 목록에서 버튼이 제거되면 버튼을 재구성하지 않는 이유는 무엇입니까?

FlexEvent.REMOVE 이벤트에 comboBoxMultiSelect을 연결하고 단추 참조를 파괴하고 새 단추를 만든 다음 AddChild()를 사용하여 다시 추가합니다. 다음은 이벤트에 대한 설명입니다.

성분이로 removeChild() removeChildAt() removeElement() 또는 removeElementAt() 메소드를 사용하여 콘텐츠 아이로서 컨테이너로부터 제거 될 때. 가

"성분이 제거되는 경우 전달

rawChildren.removeChild() 또는 rawChildren.removeChildAt() Methods를 사용해 아닌 아이 등 용기는, 이벤트가 전달되지 않는다.

이 이벤트에만 발송 부착 된 하나 이상의 관련 청취자가 존재하는 경우 전달 목적."

확실히, 이것은 잘못 표시되는 것을 수정하고 어떤 일이 일어 났는지 설명합니다. 어떤 이유로 버튼이 다시 추가 될 때 스타일을 적용하기 위해 버튼 하나 이상의 렌더 이벤트를 사용하고 있습니다.이 동작을 복제 할 수있는 다른 사용자는 누구입니까?

실제 질문은입니다. "표시 목록에 단추를 추가하고 다시 추가하여 삽입 된 아이콘이 영향을받지 않도록하는 가장 좋은 방법은 무엇입니까?"

관련 문제