关于Flex中的表单布局的问题

时间:2010-09-21 17:55:00

标签: flex forms

今天看了一下表单布局

<mx:Form id="form">
        <mx:FormItem label="horizontal:">
            <mx:Text text="test"/>
        </mx:FormItem>
</mx:Form>

以'label'格式输出 - 'textbox'。但我想改变方向而不改变代码。像

标签

复选框

我该怎么做?请详细解释。

谢谢和问候,

Karthik Jayaraman

3 个答案:

答案 0 :(得分:0)

我认为你必须覆盖formItem代码并改变它定位它的孩子的方式。

我怀疑这是微不足道的,但我不认为它会很难。打开表单代码并查看updateDisplayList()。

答案 1 :(得分:0)

您的示例不是很清楚,但使用下面的示例可能会澄清您的要求

    <mx:Form id="form">
            <mx:FormItem label="Layout Demo:">
                <mx:TextInput text="Input 1"/>
                <mx:TextInput text="Input 2"/>
            </mx:FormItem>
    </mx:Form>

将呈现为:

"Layout Demo: | Input 1 |"      
              | Input 2 |

如果要将其更改为水平渲染:

"Layout Demo: | Input 1 |  | Input 2 |" 

将属性direction="horizontal"添加到<mx:FormItem>

答案 2 :(得分:0)

我已经完成了这项工作,标签垂直位于Flex 3中的表单项上方,并且它不漂亮。它起作用,但它不是最稳定的,并且需要对组件生命周期方法进行大量的黑客攻击。

在Flex 4中,新的皮肤模型修复了这个问题,如果可能的话,请使用Hero版本(http://blog.flexexamples.com/2010/08/28/creating-a-simple-spark-form-in-flex-hero/)。

以下是我在Flex 3中用来覆盖的代码的相关位,是YMMV的一种方法。

NB这个组件有更多内容,我把东西拿出去,因此作为我接近它的粗略指南,它可能不会按原样运行,但通过一些调整它应该没问题。

package my.controls
{
import flash.display.DisplayObject;

import mx.containers.FormItem;
import mx.containers.FormItemDirection;
import mx.core.EdgeMetrics;
import mx.core.IDeferredInstance;
import mx.core.UIComponent;
import mx.styles.CSSStyleDeclaration;
import mx.styles.StyleManager;

/**
 * Allows positioning of the FormItem labels to be as inherited from FormItem, or
 * allows for top alignment of labels whilst resizing form elements to fill width
 * of parent container.
 *
 * <p>Possible values are: <code>left</code>; and <code>top</code>.</p>
 * <p>Default value is: left.</p>
 */
[Style(name="labelAlign", type="String", enumeration="left,top", inherit="no")]

public class MyFormItem extends FormItem
{
    private static const labelAlignStyleName:String = "labelAlign";

    private static var classConstructed:Boolean = constructCustomStyle();

    private var _isFormItemLabelTopAligned:Boolean;

    private var _hasFormItemLabel:Boolean;

    /**
     * This calls the super.commitProperties() method and then sets instance variables
     * indicating on whether the labels should be top aligned, and also if there is
     * a non zero length string as the label value.
     */
    override protected function commitProperties():void
    {
        super.commitProperties();

        if (label != null && label.length > 0)
        {
            _hasFormItemLabel = true;
        }

        if (getStyle(labelAlignStyleName) == "top")
        {
            _isFormItemLabelTopAligned = true;
        }
    }

    /**
     * After calling the super.measure() method, a check is made to see if there is a label
     * value, and if the label is top aligned.  If so, it modifies the measuredHeight and
     * measuredMinHeight to include the height of the label, plus the indicatorGap style value
     */
    override protected function measure():void
    {
        super.measure();

        //If this is top aligned, then change the height of this component
        if (_isFormItemLabelTopAligned)
        {
            if (_hasFormItemLabel)
            {
                var addedHeight:Number = itemLabel.getExplicitOrMeasuredHeight() + getStyle(indicatorGapStyleName);

                //Add the height of the label
                measuredHeight = measuredHeight + addedHeight;
                measuredMinHeight = measuredMinHeight + addedHeight;
            }

            //Ignore any measured width we may get, caclulate the width as being the greater of the label or child
            var children:Array = getChildren();
            var maxChildWidth:Number = 0;

            //Usually there is only 1 child, but code for in case there are more
            for each (var child:DisplayObject in children)
            {
                var uiComp:UIComponent = child as UIComponent;
                maxChildWidth = Math.max(maxChildWidth, uiComp.getExplicitOrMeasuredWidth());
            }

            //Set the measurement
            measuredWidth = measuredMinWidth = Math.max(itemLabel.getExplicitOrMeasuredWidth(),
                                                        maxChildWidth);
        }
    }

    /**
     * <p>Checks for label top alignment and if so, changes the location of label to be
     * moved to the top left of the container.  It then proceeds to layout the children depending on
     * whether the <code>MyFormItem.direction</code> property is horizontal or vertical.</p>
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if (_isFormItemLabelTopAligned)
        {
            //Anchor the label to the top left of the container
            if (_hasFormItemLabel)
            {
                var vm:EdgeMetrics = viewMetricsAndPadding;
                itemLabel.move(vm.left, vm.top);
            }

            //Depending on the layout of the MyFormItem items, take appropriate action
            if (numChildren > 0)
            {
                if (direction == FormItemDirection.VERTICAL)
                {
                    moveVerticalLayoutWithTopLabel();
                }
                else
                {
                    moveHorizontalLayoutWithTopLabel();
                }
            }

            findAndRemoveIndicator();
        }

    }

    /**
     * <p>Given that labelAlign is set to be top aligned, this method works out the  new
     * positioning and size of the vertically aligned children.  It uses the <code>verticalGap</code>
     * style property to set a width between the children.</p>
     */
    protected function moveVerticalLayoutWithTopLabel():void
    {
        var vm:EdgeMetrics = viewMetricsAndPadding;
        var commonX:Number = vm.left;

        var child:UIComponent;
        var previousChild:UIComponent = null;
        var newY:Number = vm.top + (_hasFormItemLabel ? (itemLabel.getExplicitOrMeasuredHeight() + getStyle(indicatorGapStyleName)) : 0);
        var availableWidth:Number = this.width - getStyle("paddingLeft") - getStyle("paddingRight");

        for (var i:Number = 0; i < numChildren; i++)
        {
            //Calculate the new Y value of the child including the vertical gap
            if (previousChild != null)
            {
                newY = newY + previousChild.getExplicitOrMeasuredHeight() + getStyle("verticalGap");
            }

            child = UIComponent(getChildAt(i));
            child.move(commonX, newY);

            //If these children are percentWidths, then set the size of them as we have more width now
            if (!isNaN(child.percentWidth))
            {
                var childSize:Number = availableWidth * (child.percentWidth / 100);
                child.setActualSize(childSize, child.getExplicitOrMeasuredHeight());
            }

            previousChild = child;
        }
    }


    /**
     * <p>Given that labelAlign is set to be top aligned, this method works out the  new
     * positioning and size of the horizontal aligned children.  It uses the <code>horizontalGap</code>
     * style property to set a width between the children.</p>
     */
    protected function moveHorizontalLayoutWithTopLabel():void
    {
        var child:UIComponent;
        var previousChild:UIComponent = null;
        var vm:EdgeMetrics = viewMetricsAndPadding;
        var commonY:Number = vm.top + (_hasFormItemLabel ? (itemLabel.getExplicitOrMeasuredHeight() + getStyle(indicatorGapStyleName)) : 0);
        var newX:Number = vm.left;
        var availableWidth:Number = this.width - getStyle("paddingLeft") - getStyle("paddingRight");

        for (var i:int = 0; i < numChildren; i++)
        {
            //Calculate the new X postion of the child taking into account of the label move
            if (previousChild != null)
            {
                newX = newX + previousChild.width + getStyle("horizontalGap");
            }

            child = UIComponent(getChildAt(i));
            child.move(newX, commonY);

            //If these children are percentWidths, then set the size of them as we have more width now
            if (!isNaN(child.percentWidth))
            {
                var childSize:Number = availableWidth * (child.percentWidth / 100);
                child.setActualSize(childSize, child.getExplicitOrMeasuredHeight());
            }

            previousChild = child;
        }
    }

    private function findAndRemoveIndicator():void
    {
        if (!required)
        {
            return;
        }

        var indicatorClass:Class = getStyle("indicatorSkin") as Class;
        var rawChildrenLength:int = rawChildren.numChildren;

        for (var i:int = 0; i < rawChildrenLength; i++)
        {
            var displayObject:DisplayObject = rawChildren.getChildAt(i);
            if (displayObject is indicatorClass)
            {
                rawChildren.removeChild(displayObject);
                return;
            }
        }
    }

    /**
     * A convenience method to register a style with the StyleManager and create the necessary
     * defaults.
     */
    private static function constructCustomStyle():Boolean
    {
        const className:String = "MyFormItem";
        const defaultLabelAlignment:String = "left";
        const defaultIndicatorGap:Number = 2;

        if (!classConstructed)
        {
            var style:CSSStyleDeclaration = StyleManager.getStyleDeclaration(className);

            if (style)
            {
                if (style.getStyle(labelAlignStyleName) == undefined)
                {
                    style.setStyle(labelAlignStyleName, defaultLabelAlignment);
                }
            }
            else
            {
                style = new CSSStyleDeclaration();
                style.defaultFactory = function():void
                {
                    this[labelAlignStyleName] = defaultLabelAlignment;
                }

                StyleManager.setStyleDeclaration(className, style, true);
            }
        }
        return true;
    }

    /**
     * Calls super.styleChanged() method.
     *
     * If the labelAlign style changes, then the component is invalidated as
     * its properties and size recalculated as well as being redrawn.
     */
    override public function styleChanged(styleProp:String):void
    {
        super.styleChanged(styleProp);

        // Check to see if style changed. 
        if (styleProp == labelAlignStyleName)
        {
            invalidateProperties();
            invalidateSize();
            invalidateDisplayList();
        }
    }
}

}