Apache flex 自定义复合控件在添加回VGROUP后仅0.5-1秒内未正确呈现

Apache flex 自定义复合控件在添加回VGROUP后仅0.5-1秒内未正确呈现,apache-flex,actionscript,adobe,custom-controls,Apache Flex,Actionscript,Adobe,Custom Controls,我将离开MXML,并在ActionScript中构建了一个自定义组件控件 我已正确显示控件。问题是在我将其从显示列表中删除并使用.addElement(control)方法重新添加后出现的 下面是再次添加它的代码 private function displayParameters(parameters:ArrayCollection):void{ for(var index:int = 0; index<parameters.length; index++){ if

我将离开MXML,并在ActionScript中构建了一个自定义组件控件

我已正确显示控件。问题是在我将其从显示列表中删除并使用.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);
      }
   }
}
当我使用
vgParameters.addElement(control)
重新添加控件时,
comboBoxMultiSelect
未正确呈现。按钮
\u btn multiple
内的
plusIcon
起初未正确定位,但大约0.5-1秒后会迅速自我纠正

我很确定问题在于comboBoxMultiSelect,只是不确定如何强制图标保持在同一位置

这是非常恼人的,在我努力工作之后,有人知道我做错了什么吗

谢谢:)

更新------>这是报告控制代码

[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;
    }

}

突出的一点是在
updateDisplayList()
的实现中。正如您所知,这是您的组件应该调整其子对象大小和位置的地方(和/或进行任何编程绘图)

但与其直接设置子对象的宽度/高度,不如使用Flex生命周期方法之一:
setActualSize()
setLayoutBoundsSize()
。对spark组件使用
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
被指定了一个百分比宽度,但在
updateDisplayList()
中,它被指定了一个固定宽度(这可能是一个误导,但我不会设置百分比宽度)

2) 您可以在删除组件后或重新添加组件之前欺骗组件进行自我验证。一种可能是浪费时间的恶作剧的预感

3) 从组件中删除“HGroup”。这有点不必要:布局要求非常简单,只需几行Actionscript即可完成。随着布局要求变得越来越复杂,您的里程数也会有所不同

createChildren()
中,将组合框和按钮直接添加到
ui组件中。然后在
updateDisplayList()
中调整它们的大小和位置,如下所示:

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);
}

我将试着给你一个例子,说明我们在上面的评论中讨论过的Spark蒙皮体系结构的含义。这不是对你问题的直接回答,但我想你可能会觉得有趣。为了简洁起见,我必须使它比您的组件简单一些,因为您似乎已经为您的问题剥离了一些代码,所以我无法确切地知道它应该做什么

这将是一个组件,您可以通过单击按钮在正常和扩展状态之间切换。首先,我们创建皮肤类。通常,您会先创建主机组件,但这样解释会更容易

<!-- 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>
好吧,这里还有很多事情要做

  • 首先看一下
    SkinState
    元数据声明:当一个皮肤类被分配给组件时,编译器将检查该皮肤是否实现了所需的状态
  • 然后,
    SkinPart
    声明:主机组件上的属性名称必须与skin类中标记的id完全匹配。当
    required
    设置为
    true
    时,编译器将检查这些组件是否确实存在于皮肤中。如果需要可选蒙皮零件,请将其设置为
    false
  • 请注意,
    toggleButton
    的类型是
    IEventDispatcher
    :从主机组件的角度来看,所有
    toggleButton
    必须做的事情都是分派单击事件。这意味着我们现在可以用
    创建一个皮肤,整个过程将以同样的方式工作。看看这有多强大
  • 由于skinpart属性不会立即分配,因此我们会覆盖
    partAdded()
    方法,该方法将在组件可用时执行。在大多数情况下,这是连接事件侦听器的地方
  • toggleExpanded()
    方法中,我们切换布尔值,就像您问题中的组件一样,但是我们只会使皮肤状态无效。这将导致皮肤调用
    getCurrentSkinState()
    方法,并将其状态更新为返回的任何值
瞧!您有一个工作组件,其行为被很好地分割成一个actionscript类,您不必担心布局的复杂性。
<!-- 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>
[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";
    }
}
<c:ComboBoxMultiSelect skinClass="my.skins.ComboBoxMultiSelectSkin" />
@namespace c "my.components.*";

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