组件

组件是FairyGUI中的一个基础容器。组件可以包含一个或多个基础显示对象,也可以包含组件。

组件属性

在舞台上点击空白处,右边属性栏显示的是容器组件的属性:

其他属性

设计图功能

可以设定一个组件的设计图。设计图将显示在舞台上,可以设置显示在组件内容的底层或者上层。使用设计图可以使拼接UI更加快速和精准。

设计图不会发布到最终的资源中。

点击穿透

组件内,显示在前面的元件将优先收到点击事件。如果该元件是可触摸的,则点击事件结束,不会继续向后传递。

在组件宽度x高度的范围内点击测试都是有效的(无法穿透),无论这个范围内是否有子元件。举个例子说明。

这是组件A,大小为400x400,内容为4个白色的矩形:

这是组件B,大小为400x400,内容为一个红色的矩形:

将B先添加进舞台,然后再添加A到舞台,也就是说,A显示在B的前面,效果如下图:

可以看到,虽然A在B的上面,但红色方块是可见的,因为A在此区域并没有内容。当点击图中绿色点的位置时,点击事件将在A上面触发,而B是点击不了的。这是因为在A的范围内,点击是不能穿透的。

如果希望A能被穿透应该怎样?组件属性里提供设置:,勾选即可。代码里也可以设置:

//true表示不可穿透,false表示可穿透。
aComponent.opaque = false;

设置穿透后,只有点击4个白块时,A才接收到点击事件,如果点击绿色点位置,B将接收到点击事件。这个特性在设计一些全屏界面时尤其要注意。例如一个主界面添加到舞台,并设置为全屏,如果不穿透的话,那么Stage.isTouchOnUI将一直返回true。

注意:图片和普通文字是不接受点击的,如果一个只含有图片或普通文字的组件,设置了点击穿透,那么整个组件就是完全穿透了,不会拦截到任何点击。

点击测试

有些特殊需求,需要用到不规则区域的点击测试。FairyGUI提供了两种方案解决这种需求:

  1. 组件内拖入一个图形,形状选多边形,然后用多边形勾画出不规则区域的形状。最后在组件的“点击测试”属性里,选择这个图形元件。

  2. 如果不规则形状为圆环等其它带洞的形状,那么用图形无法画出来。这时,可以用像素检测。

    首先你要准备一张包含不规则区域的图片,图片里不透明的像素代表接受点击的区域,透明的像素代表点击穿透的区域,组件里超出图片范围的也是可穿透的区域。

    把这张图片拖入舞台,然后在组件的“点击测试”属性里,选择这张图片。

    注意:用于像素检测的这张图片只能放置在和组件同一个包内,也不能用装载器,只能是图片。

遮罩

FairyGUI的遮罩有两种:矩形遮罩和自定义遮罩。

矩形遮罩

将组件的“溢出处理”设置为“隐藏”或者“滚动”,那么组件就带了矩形遮罩。超出组件(矩形区域-边缘留空)的区域都不可见。无论在什么平台,这种遮罩的效率是最高的。

自定义遮罩

可以设置组件内一个图片或者图形作为组件的遮罩。这种遮罩一般都是使用模板测试(Stencil Op)技术。

当使用图形(Graph)作为遮罩时,有图形的区域内容可见,例如,一个圆形,则圆形区域内可见,其他区域不可见。

当使用图片作为遮罩时,图片内透明度为0的像素对应区域的内容不可见,反之可见。超出图片区域的内容不可见

备注:

  1. Starling平台要使用自定义遮罩必须在应用程序描述文件里加上:

    <initialWindow>
    <depthAndStencil>true</depthAndStencil>
    </initialWindow>
  2. AS3平台不支持用图片做遮罩。

  3. Unity平台:如果你要对使用了自定义遮罩的组件进行设置倾斜、设置BlendMode,设置滤镜,又或者曲面UI中含有自定义遮罩的组件时,需要额外的设置才能显示正常。请参考PaintMode

反向遮罩(挖洞)

效果和正常遮罩相反,也就是可见的区域变不可见,不可见的区域变可见。例如:

使用图形(Graph)作为遮罩时,有图形的区域内容不可见,例如,一个圆形,则圆形区域内不可见,其他区域可见。

使用图片(Image)作为遮罩时,图片内透明度为0的像素对应区域的内容可见,反之不可见。超出图片区域的内容可见

备注:

  1. 当遮罩发生时,点击测试也同样会发生变化,只有显示出来的内容才接受点击检测,被遮住的内容不接受点击检测。
  2. 对于正在编辑的组件,遮罩只有在预览时才能看到效果。
  3. 定义了遮罩的组件,其内部的元件永远无法和外部的元件合并Draw Call,因为他们有不同的材质属性。
  4. 只有部分平台支持使用图片做反向遮罩,请以测试结果为准。

扩展

可以看到有六种“扩展”选择。组件可以随意在这些“扩展”中切换。选择哪种“扩展”,组件就有了那种扩展的属性和行为特性。

下面以按钮为例,介绍一下“扩展”是怎样工作的。选择“扩展”为按钮后,可以看到属性面板下方出现了按钮相关的提示和设置。

这里先忽略按钮组件的设置,后续教程会详细说明。从提示可以看到,FairyGUI中“扩展”的定义方式是以“名称约定”为基础的。一个按钮,可以带有标题和图标,这个标题(一般是一个文本)和图标(一般是一个装载器)需要你自己放置到组件中,并把他们名字设置为title和icon,就像这样:

然后我们测试一下这个刚制作好的组件。把按钮组件拖到另一个组件中,并设置一下“标题”和“图标”,如下图

效果出来了。这说明标题文本被自动设置到了名称为“title”的文本元件上,图标被自动设置到了名称为“icon”的装载器元件上。

如果按钮组件里没有放置名称为icon的装载器控件呢?那么设置图标就没有效果,仅此而已。其他约定的处理方式也相同。不会有任何报错。

从按钮的设计就可以看出FairyGUI“扩展”功能的优势所在。如果一个编辑器提供了现成的按钮组件,无论设计者考虑多么周到,都无法覆盖所有需求,就一个按钮,随便想到的变化就可能有:是否带图标/图标在左边还是右边/图标与文字的距离/是否带文字/文字的颜色/文字的大小等等。而在FairyGUI里,按钮组件内所有东西都是任由你布置的。

“扩展”还赋予了组件行为,具体到按钮上,就是处理各种鼠标或触摸事件,按下时改变状态(单选/多选),单击时播放声音等等。这些都是由“扩展”的底层去处理的。这部分同样是通过“名称约定”来工作的。例如,按钮内只要提供了名称为“button”的控制器,当鼠标悬浮在按钮上方时,就会自动切换该控制器到“over”页面;当鼠标按下时,就会自动切换该控制器到“down”页面,等等。如果按钮没有提供名称为“button”的控制器呢?上述行为就不会发生。按钮控制器并不是必须的,如果你不需要以上行为,就不用提供。

其他类型的“扩展”的工作方式与按钮类似。后续文档会详细介绍每种“扩展”的属性和行为。

GComponent

组件支持动态创建,例如:

GComponent gcom = new GComponent();
gcom.SetSize(100,100);
GRoot.inst.AddChild(gcom);

动态创建的组件是空组件,可以作为其他组件的容器。一个常见的用途,如果你要建立一个多层的UI管理系统,那么空组件就是一个合适的层级容器选择。动态创建的组件默认是点击穿透的,也就是说如果直接new一个空组件作为接收点击用途,你还得这样设置:

//设置组件点击不穿透。
gcom.opaque = true;

如果要创建UI库里的组件,应该使用这样的方式:

GComponent gcom = UIPackage.CreateObject("包名","组件名").asCom;
GRoot.inst.AddChild(gcom);

FairyGUI和Flash/Cocos类似,采用树状的结构组织显示对象。容器可以包含一个或多个基础显示对象,也可以包含容器。这个树状结构称为显示列表。FairyGUI提供了API管理显示列表。

显示列表管理

渲染顺序

在FairyGUI中,显示列表是以树形组织的,下面说的渲染顺序均指在同一个父元件下安排的顺序,不同父元件的元件是不可能互相交错的,这是前提,请注意。

显示对象的渲染顺序取决于它的显示列表中的顺序,顺序大的后渲染,即显示在较前面。一般来说,我们都是使用AddChild或SetChildIndex调整渲染顺序。例如如果要一个元件显示在容器的最前面,那调用AddChild(元件)就可以了,AddChild是可以重复调用的。也可以调用SetChildIndex设置对象在显示列表中的具体位置,例如SetChildIndex(元件,0)就可以将元件置于最底层。

还有另外一个因子可以影响渲染循序,它就是GObject.sortingOrder。这个属性只用于特定的用途,不作常规的使用。它一般用于类似固定置顶的功能,另外,永远不要将sortingOrder用在列表中。sortingOrder越大,则渲染顺序越后,即显示到更前面的位置。一般情况下,sortingOrder为0,这时渲染顺序由显示对象在显示列表中的顺序决定。sortingOrder可以令你更灵活的控制渲染循序。例如,如果希望一个元件始终保持在其他元件上方,可以设置其sortingOrder一个较大的整数值,这样无论容器使用AddChild添加了多少元件,这个元件依然显示在最前面。(sortingOrder的效率较差,勿做频繁调用的用途)

上面提到的都是调整对象在显示列表中的顺序,如果不想调整这个顺序的同时,又要调整渲染顺序,组件还提供了另一种方式。

//升序,这是默认值,按照对象在显示列表中的顺序,从小到大依次渲染,效果就是序号大的显示在较前面。
aComponent.childrenRenderOrder = ChildrenRenderOrder.Ascent;

//降序,按照对象在显示列表中的顺序,从大到小依次渲染,效果就是序号小的显示在较前面。
aComponent.childrenRenderOrder = ChildrenRenderOrder.Descent;

//拱形,需要指定一个顶峰的索引,从两端向这个索引位置依次渲染,效果就是这个位置的对象显示在最前面,两边的对象依次显示在后面。
aComponent.childrenRenderOrder = ChildrenRenderOrder.Arch;
aComponent.apexIndex = 3; //索引为3的对象显示在最前面。

绑定扩展类

可以绑定一个类为组件的扩展类。首先,编写一个扩展类:

public class MyComponent : GComponent
{
GObject msgObj;

//如果你有需要访问容器内容的初始化工作,必须在这个方法里,而不是在构造函数里。各个SDK的函数原型的参数可能略有差别,请以代码提示为准。在Cocos2dx/CocosCreator里,方法的名字是onConstruct,且不带参数
override protected void ConstructFromXML(XML xml)
{
base.ConstructFromXML(xml);

//在这里继续你的初始化
msgObj = GetChild("msg");
}

public void ShowMessage(string msg)
{
msgObj.text = msg;
}
}

然后注册你的扩展类。注意,必须在组件构建前注册,如果你使用的是UIPanel,那么在Start里注册是不够早的,必须在Awake里,总之,如果注册不成功,90%可能都是注册晚于创建,10%可能是URL错误,这可以通过打印URL排查。

UIObjectFactory.SetPackageItemExtension("ui://包名/组件A”, typeof(MyComponent));

这样就为组件A绑定了一个实现类MyComponent 。以后所有组件A创建出来的对象(包括在编辑器里使用的组件A)都是MyComponent类型。然后我们就可以为MyComponent添加API,用更加面向对象的方式操作组件A。例如:

MyComponent gcom = (MyComponent)UIPackage.CreateObject(“包名“, ”组件A”);
gcom.ShowMessage("Hello world");

注意:如果组件A只是一个普通的组件,没有定义“扩展”,那么基类是GComponent,如上例所示;如果组件A的扩展是按钮,那么MyComponent的基类应该为GButton,如果扩展是进度条,那么基类应该为GProgressBar,等等。这个千万不能弄错,否则会出现报错。