[转] 完美解决Panel设置了borderSkin后内容体与标题栏重叠的问题
[url] http://blog.csdn.net/zlxluofeng/archive/2010/12/17/6081233.aspx[/url]
如果您做过Flex皮肤方面的工作,并且想自定义Panel及其子控件(TitleWindow、Alert)的皮肤,应该会碰到过在设置了Panel的borderSkin样式后,Panel中的内容体会与Panel的Header部分重叠的问题。初次遇到此问题的时候,我一时束手无策,只能找一种替代方式来暂时解决给Panel设置皮肤。最近再一次接触到Flex的皮肤,因为替代方案在更换皮肤时会对系统改动比较大,所以我觉得有必要更深入了解Flex皮肤与样式,看有没有更好的方式来解决我们遇到的问题,我研究了一下Flex SDK中定义皮肤的类以及Container组件,事实证明我是幸运的。
borderSkin样式用来给Container组件设置四周的边框及背景,Panel的默认borderSkin是mx.skins.halo.PanelSkin,在PanelSkin中有一个对容器边缘有重要影响的属性borderMetrics,该属性用来指定容器四个边框的大小,该属性是实现了IRectangularBorder接口而来的(确切的说应该是IBorder,由于Container中使用的是IRectangularBorder接口,所以我们需要IRectangularBorder接口)。对于自定义皮肤,一般都是使用Flash工具制作的,其链接类是MovieClip的子类,并没有实现Flex中的IRectangularBorder接口,所以对于Container来说,获取不到borderMetrics属性,也就只能使用默认值,也就是四个边框的大小都是0。对于大多数Container的子类来说,边框为0基本没什么影响,但由于Panel的特殊性——Panel多了标题栏,如果Panel的顶部边框为0的话,其内容体部分就会撑到最上面去,与Panel中定义的Header部分重叠起来。其实我们要解决的就是如何给自定义皮肤的Panel容器顶部边框指定一个大于0的值。
对于皮肤的链接类从mx.skins.halo.PanelSkin继承,经测试是没办法编译通过的,因为Flash中链接类必须是MovieClip的子类;所以我尝试了另外一种方法,就是实现IRectangularBorder接口。在Flash中,对皮肤的链接类实现IRectangularBorder接口,但这种方式在项目中无法把皮肤的链接类对象转换为IRectangularBorder接口,可能是因为皮肤是在单独的swf中的缘故,没有与我们的Flex项目在同一个域中;我尝试的第三种方式也是实现IRectangularBorder接口,只是在Flex项目新建了一个容器类,然后把Flash中的Panel皮肤实例化并放到新建的容器类中,把该容器作为Panel的新皮肤类,终于可以解决Panel的内容与标题栏重叠的情况了,下面是效果图和一些代码:
下面是在Flex项目中实现的皮肤类,该类实现了IRectangularBorder接口的borderMetrics属性,并指定了该属性的top值。
package
{
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.geom.Rectangle;
import mx.containers.Panel;
import mx.core.EdgeMetrics;
import mx.core.IRectangularBorder;
public class Panel_XSMSkin extends Sprite implements IRectangularBorder
{
/**
* Flash设计的Panel皮肤链接类
*/
[Embed(source="yflexskin.swf", symbol="Panel_borderSkin")]
public var PanelSkinClass:Class;
protected var skin:DisplayObject;
/**
* 容器的边框大小属性
*/
private var _bm:EdgeMetrics;
public function Panel_XSMSkin()
{
super();
if(PanelSkinClass)
{
// 创建皮肤,并加入到此实现类中
skin = new PanelSkinClass();
this.addChild(skin);
}
}
// IRectangularBorder 接口的方法,可不实现
public function get backgroundImageBounds():Rectangle
{
return null;
}
public function set backgroundImageBounds(value:Rectangle):void
{
}
public function get hasBackgroundImage():Boolean
{
return false;
}
public function layoutBackgroundImage():void
{
}
/**
* 此属性的值用来设置容器的边框大小
* @return
*
*/
public function get borderMetrics():EdgeMetrics
{
if(_bm == null)
{
// 这里的关键是实现此属性,并指定边框的大小,其中第二个属性是top值
_bm = new EdgeMetrics(10, 40, 10, 10);
}
return _bm;
}
}
}
下面是CSS中Panel的设置
Panel
{
closeButtonDisabledSkin: Embed(source="yflexskin.swf",symbol="Panel_closeButtonDisabledSkin");
closeButtonDownSkin: Embed(source="yflexskin.swf",symbol="Panel_closeButtonDownSkin");
closeButtonOverSkin: Embed(source="yflexskin.swf",symbol="Panel_closeButtonOverSkin");
closeButtonUpSkin: Embed(source="yflexskin.swf",symbol="Panel_closeButtonUpSkin");
/*
Removed due to incompatibilities with Flex 3
borderSkin: Embed(source="yflexskin.swf", symbol="Panel_borderSkin");
*/
borderSkin: Embed(skinClass="Panel_XSMSkin");
drop-shadow-enabled: false;
}
CSS中注释的部分是没有封装皮肤链接类时设置的样式,在该样式下,内容体会与标题栏重叠,而未注释的borderSkin用来解决这个问题。
该测试项目可以通过下面的链接地址下载
对于上面这种方式也并不是没有缺陷,因为没有从Flex皮肤的基类Border中继承,无法通过CSS来设置Panel的部分样式,当然对于自定义皮肤,基本上也不需要设置这些样式了。
这个Panel皮肤的问题同样适用于TitleWindow和Alert,在研究这块的过程中,我尝试了多种方式,目前只找到上述这一种方式能行的通,如果您有更好的方式,还请指定一下哈,也希望与您探讨这个问题。