Flex 3框架中容器的边框,背景色和背景图片是如何实现的?

以下分析都从flex 3源代码推出,不一定正确!希望能和有兴趣的朋友一起探讨。

Flex 3 框架中,容器的边框,背景色和背景图片是由容器的borderSkin 类(为了叙述方便,假设我们为容器定义了一个样式borderSkin: ClassReference("mx.skins.halo.HaloBorder"))来负责的,容器类如果检测到需要边框,背景色或者背景图片,容器会在自己内部建立一个HaloBorder类型的对象(
程序代码 程序代码
border = new mx.skins.halo.HaloBorder();
// Add the border behind all the children.
rawChildren.addChildAt(DisplayObject(border), 0);
)border,在渲染过程中border负责建立边框,背景色或者背景图片。

原理如下:
每个容器的createChildren()方法中会调用createBorder()方法来判断是否需要建立border,只有符合下面两个条件我们的容器才会建立边框:
[1]容器没有边框(border == null);
[2]我们定义了容器的borderSkin样式 ,并且该样式类不是 mx.skins.halo::HaloBorder 或者 我们定义了容器的borderSkin样式 ,并且该样式类是 mx.skins.halo::HaloBorder那么
[2.1]borderStyle 样式不是 none 或者 borderStyle 样式是 none 并且定义了 mouseShield 样式 或者 borderStyle 样式是 none 并且没有定义 mouseShield 样式 那么
[2.1.1]如果定义了 backgroundColor 并且 backgroundColor != "" 或者 定义了backgroundImage 并且 backgroundImage != ""

flex3 框架中 mx.skin::RectangularBorder 类支持backgroundImage, backgroundSize,backgroundAttachment 样式。mx.skin::HaloBorder 类实现了 backgroundColor 和 边框的绘制。
类的继承链如下:HaloBorder - RectangularBorder - Border - ProgrammaticSkin - FlexShape - Shape - DisplayObject - EventDispatcher - Object

设置了 borderSkin: ClassReference("mx.skins.halo.HaloBorder")的容器,最底层是背景色和边框(因为背景色和边框用的是graphics绘制的),背景色上面是背景图片,背景图片上面是容器的子孙。

此文禁止转载,但是您可以以超级链接的方式链接到这篇文章。


附录 方便我自己查阅而放在这里

默认主题(framework.swc 中包含的 default.css文件)中有关边框,背景色和背景图片的定义
程序代码 程序代码

global
{
    backgroundAlpha: 1.0; /* this runs the opacity of nearly every square piece of the components */
    /* backgroundDisabledColor: #DDDDDD; */
    backgroundSize: "auto";
    bevel: true;
    borderAlpha: 1.0;
      borderCapColor: #919999;
    borderColor: #B7BABC;
    borderSides: "left top right bottom";
    borderSkin: ClassReference("mx.skins.halo.HaloBorder");
    borderStyle: "inset";
    borderThickness: 1;
        ...........
}

Container
{
    borderStyle: "none";
}

mx.core::Container 类中的和本文有关的一些定义:
程序代码 程序代码

/**
     *  @private
     *  Create components that are children of this Container.
     */
    override protected function createChildren():void
    {
        super.createChildren();

        // Create the border/background object.
        createBorder();

        // To save ourselves an extra layout pass, check to see
        // if the scrollbars will definitely be needed.
        // If so, create them now.
        createOrDestroyScrollbars(
            horizontalScrollPolicy == ScrollPolicy.ON,
            verticalScrollPolicy == ScrollPolicy.ON,
            horizontalScrollPolicy == ScrollPolicy.ON ||
            verticalScrollPolicy == ScrollPolicy.ON);

        // Determine the child-creation policy (ContainerCreationPolicy.AUTO,
        // ContainerCreationPolicy.ALL, or ContainerCreationPolicy.NONE).
        // If the author has specified a policy, use it.
        // Otherwise, use the parent's policy.
        // This must be set before createChildren() gets called.
        if (creationPolicy != null)
        {
            actualCreationPolicy = creationPolicy;
        }
        else if (parent is Container)
        {
            if (Container(parent).actualCreationPolicy ==
                ContainerCreationPolicy.QUEUED)
            {
                actualCreationPolicy = ContainerCreationPolicy.AUTO;
            }
            else
            {
                actualCreationPolicy = Container(parent).actualCreationPolicy;
            }
        }

        // It is ok for actualCreationPolicy to be null. Popups require it.

        if (actualCreationPolicy == ContainerCreationPolicy.NONE)
        {
            actualCreationPolicy = ContainerCreationPolicy.AUTO;
        }
        else if (actualCreationPolicy == ContainerCreationPolicy.QUEUED)
        {
            var mainApp:Application = parentApplication ?
                                      Application(parentApplication) :
                                      Application(Application.application);
            
            mainApp.addToCreationQueue(this, creationIndex, null, this);
        }
        else if (recursionFlag)
        {
            // Create whatever children are appropriate. If any were
            // previously created, they don't get re-created.
            createComponentsFromDescriptors();
        }

        // If autoLayout is initially false, we still want to do
        // measurement once (even if we don't have any children)
        if (autoLayout == false)
            forceLayout = true;

        // weak references
        UIComponentGlobals.layoutManager.addEventListener(
            FlexEvent.Update_COMPLETE, layoutCompleteHandler, false, 0, true);
    }

/**
     *  Creates the container's border skin
     *  if it is needed and does not already exist.
     */
    protected function createBorder():void
    {
        if (!border && isBorderNeeded())
        {
            var borderClass:Class = getStyle("borderSkin");

            if (borderClass != null)
            {
                border = new borderClass();
                border.name = "border";

                if (border is IUIComponent)
                    IUIComponent(border).enabled = enabled;
                if (border is ISimpleStyleClient)
                    ISimpleStyleClient(border).styleName = this;

                // Add the border behind all the children.
                rawChildren.addChildAt(DisplayObject(border), 0);

                invalidateDisplayList();
            }
        }
    }

    /**
     *  @private
     */
    private function isBorderNeeded():Boolean
    {
        //trace("isBorderNeeded",this,"ms",getStyle("mouseShield"),"borderStyle",getStyle("borderStyle"));

        // If the borderSkin is a custom class, always assume the border is needed.
        var c:Class = getStyle("borderSkin");
        
        // Lookup the HaloBorder class by name to avoid a linkage dependency.
        // Note: this code assumes HaloBorder is the default border skin. If this is changed
        // in defaults.css, it must also be changed here.
        try
        {
            if (c != getDefinitionByName("mx.skins.halo::HaloBorder"))
                return true;
        }
        catch(e:Error)
        {
            return true;
        }
            
        var v:Object = getStyle("borderStyle");
        if (v)
        {
            // If borderStyle is "none", then only create a border if the mouseShield style is true
            // (meaning that there is a mouse event listener on this view). We don't create a border
            // if our parent's mouseShieldChildren style is true.
            if ((v != "none") || (v == "none" && getStyle("mouseShield")))
            {
                return true;
            }
        }

        v = getStyle("backgroundColor");
        if (v !== null && v !== "")
            return true;

        v = getStyle("backgroundImage");
        return v != null && v != "";
    }

override public function styleChanged(styleProp:String):void
    {
        var allStyles:Boolean = styleProp == null || styleProp == "styleName";

        // Check to see if this is one of the style properties that is known
        // to affect page layout.
        if (allStyles || StyleManager.isSizeInvalidatingStyle(styleProp))
        {
            // Some styles, such as horizontalAlign and verticalAlign,
            // affect the layout of this object's children without changing the
            // view's size.  This function forces the view to be remeasured
            // and layed out.
            invalidateDisplayList();
        }

        // Replace the borderSkin
        if (allStyles || styleProp == "borderSkin")
        {
            if (border)
            {
                rawChildren.removeChild(DisplayObject(border));
                border = null;
                createBorder();
            }
        }
        
        // Create a border object, if none previously existed and
        // one is needed now.
        if (allStyles ||
            styleProp == "borderStyle" ||
            styleProp == "backgroundColor" ||
            styleProp == "backgroundImage" ||
            styleProp == "mouseShield" ||
            styleProp == "mouseShieldChildren")
        {
            createBorder();
        }

        super.styleChanged(styleProp);

        // Check to see if this is one of the style properties that is known.
        // to affect page layout.
        if (allStyles ||
            StyleManager.isSizeInvalidatingStyle(styleProp))
        {
            invalidateViewMetricsAndPadding();
        }

        if (allStyles || styleProp == "horizontalScrollBarStyleName")
        {
            if (horizontalScrollBar && horizontalScrollBar is ISimpleStyleClient)
            {
                var horizontalScrollBarStyleName:String =
                    getStyle("horizontalScrollBarStyleName");
                ISimpleStyleClient(horizontalScrollBar).styleName =
                    horizontalScrollBarStyleName;
            }
        }

        if (allStyles || styleProp == "verticalScrollBarStyleName")
        {
            if (verticalScrollBar && verticalScrollBar is ISimpleStyleClient)
            {
                var verticalScrollBarStyleName:String =
                    getStyle("verticalScrollBarStyleName");
                ISimpleStyleClient(verticalScrollBar).styleName =
                    verticalScrollBarStyleName;
            }
        }
    }




[本日志由 Admin 于 2008-09-17 02:27 PM 编辑]
文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags:
评论: 2 | 引用: 0 | 查看次数: 7724
回复回复leftkey[2010-07-09 03:20 PM | del]
你好。被一个问题困扰。
panel里加了borderskin的属性之后,panel标题的高度就丢失了,这个问题该如何解决?求教!谢谢!QQ:309925294
回复回复小小菜鸟[2008-11-05 11:38 AM | del]
好文
flex最终输出的毕竟不是HTML,
所以负责的紧啊,
经常被控件的样式折磨
发表评论
昵 称:
密 码: 游客发言不需要密码.
内 容:
验证码: 验证码
选 项:
字数限制 1000 字 | UBB代码 开启 | [img]标签 关闭