从Flex中owner和parent的区别来说Flex API设计思路(http://www.iteye.com/topic/849006)

英文原文: Flex 4 Gumbo DOM Tree API - Functional and Design Specification

翻译的原创链接:  http://www.smithfox.com/?e=36  转载请注明, 文中如果有什么错误的地方或是讲的不清楚的地方,欢迎大家留言.

这是一篇难得的Flex功能和架构技术SPEC, 耐心看完绝对有收获.

为了振作你看这个文章的兴趣, 假设你应聘Flex工作被问到了下面的几个问题:

1. Flex中owner和parent有什么区别?

2. addChild和addElement两套函数有什么不同,(不是指怎么使用不同, 而是指框架内部的设计有什么不同)?

3. <s:Rect>是GraphicElement吗, 他们为什么可以放在<s:Group>内?

4. SkinnableComponent, SkinnableContainer, Group, DataGroup以及SkinnableDataContainer有什么区别?

5. 最关键的是: 你知道smithfox吗?(哈哈)

 

目的

在Flex 4中有许多DOM(Document Object Model)树。他们到底是怎么组织和呈现的?

定义

图形元素(graphic element) - 就象是矩形, 路径, 或是图片. 这些元素不是DisplayObject的子类; 但是它们还是需要一个DisplayObject来渲染到屏幕. (smithfox注: "多个图形元素可以只用一个DisplayObject来渲染")

视觉元素(visual element) - (英文有时简称为 - "element"). 可以是一个halo组件, 或是一个gumbo组件, 或是一个图形元素. 视觉元素实现了接口 IVisualElement.

数据项 (英文有时简称为 - "item") - 本质上Flex中的任何事物都可以被看着数据项. 通常是指非可视化项,比如 String, Number, XMLNode, 等等. 一个视觉元素也能作为数据项 -- 这要看他是怎么被看待的.

组件树 - 组件树表现了MXML文档结构. 举个简单例子, 一个 Panel 包含了一个 Label. 这个例子中, Panel 和 Label 都在组件树中, 但是 Panel的皮肤却不是.

布局树 - 布局树呈现了运行时的布局. 在这个树中, 父亲负责呈现和布局对象, 孩子则是被布局的视觉元素.  举个简单例子, 一个 Panel 包含了一个 Label.  这个例子中, Panel 和 Label 都在布局树中, 同样Panel皮肤和皮肤中的contentGroup也是.

显示树 - Flash 底层 DisplayObject 树.

本文中的全部图的图例如下:

背景:

当你用MXML创建应用程序时, 幕后发生了许多的事情,会将MXML转换成Flash显示对象. 后台有三个主要因素: 皮肤,项渲染和显示对象sharing. 前两个对开发人员是非常重要的概念; 最后一个只需要框架开发人员关注, 但仍然比较重要.

皮肤:

当你初始化一个 Button, 其实创建了不止一个对象. 例如:

<s:Button />

 在布局树中的结果是:


(注: TextBox 已经更名为 Label)

一个皮肤文件被实例化了,并且加入到Button的显示列表中.Button的皮肤文件如下:

 

Xml代码   收藏代码
  1. <s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"  minWidth="23" minHeight="23">  
  2.   
  3.     <fx:Metadata>  
  4.         [HostComponent("mx.components.Button")]  
  5.     </fx:Metadata>  
  6.   
  7.     <s:states>  
  8.         <s:State name="up" />  
  9.         <s:State name="over" />  
  10.         <s:State name="down" />  
  11.         <s:State name="disabled" />  
  12.     </s:states>  
  13.   
  14.     <!-- background -->  
  15.     <s:Rect left="0" right="0" top="0" bottom="0"  
  16.           width="70" height="23"  
  17.           radiusX="2" radiusY="2">  
  18.         <s:stroke>  
  19.             <s:SolidColorStroke color="0x5380D0" color.disabled="0xA9C0E8" />  
  20.         </s:stroke>  
  21.         <s:fill>  
  22.             <s:SolidColor color="0xFFFFFF" color.over="0xEBF4FF" color.down="0xDEEBFF" />  
  23.         </s:fill>  
  24.     </s:Rect>  
  25.   
  26.     <!-- label -->  
  27.     <s:Label id="labelDisplay" />  
  28.   
  29. </s:Skin>  

 尽管Button看上去是一个叶子结点, 但因为皮肤的存在, 实际上他包含了孩子. 为访问这些元素,所有SkinnableComponent对象都定义了skin属性. 这样就可以通过Button.Skin实例来访问Rectangle 和Label. 如要访问Label, 你可以写成:myButton.skin.getElementAt(2)或是 myButton.skin.labelDisplay.由于labelDisplay是 Button 的 skin part, 所以你可可以直接写成 myButton.labelDisplay.

同样的原则也一样适用在SkinnableContainerSkinnableContainer是容器所以天然就有孩子, 但同时他们也是SkinnableComponent,所以也有一个皮肤以及来自皮肤的孩子.

(smithfox注: SkinnableContainer的确是继承自SkinableComponent, 见图)

还是以Panel为例:

 

Xml代码   收藏代码
  1. <s:Panel>  
  2.     <s:Button />  
  3.     <s:Label />  
  4.     <s:CheckBox />  
  5. </s:Panel>  

 panel有三个孩子:一个button,一个label,和一个checkbox.用定义在SkinnableContainer上的content APIs可以访问他们. 这些content APIs很像flash DisplayObjectContainer 的 APIs, 包括addElement(), addElementAt(), getElementAt(), getElementIndex(), 等等.... 所有方法的完整列表在稍后文档中列出.

因为 panel有3个孩子, 它的组件树象这样:


(注: TextBox 已经更名为 Label)

但是, 这只是组件树. 因为皮肤的原因, Panel真正布局树是这样的:


(注: TextBox 已经更名为 Label)

在上面这张图上有许多箭头. 需要注意的有:

  • Panel的组件孩子有: button, label, 和checkbox.
  • button, label, 和checkbox的组件父亲(owner 属性) 是 Panel.
  • button, label, and checkbox的布局父亲 (parent 属性) 是 Panel皮肤的contentGroup.

这意味着即使看上去Panel的孩子应该是一个button, 一个label, 和一个checkbox; 但实际上真正的孩子是一个panel皮肤实例. button, label, 和 checkbox 向下变成了皮肤中contentGroup的孩子. 有几种方法可以访问panel中的ButtonmyPanel.getElementAt(0) or myPanel.contentGroup.getElementAt(0) or myPanel.skin.contentGroup.getElementAt(0).

所有 SkinnableComponent 都有 skin 属性. 在 SkinnableContainer中组件的孩子实际上下推成为skin的 contentGroup的孩子组件树 指向编译自MXML的语义树.Panel 例子中, 只包括Panel 和他的孩子: 一个 button, 一个 label, 和一个checkbox. 由于皮肤, 布局树 是布局系统所实际看到的树.Panel 例子中,包括 这个panel, panel的皮肤, 以及这个皮肤的所有孩子(皮肤中的contentGroup的孩子).

布局树无需和所见的Flash显示列表有什么相关性. 这是因为 GraphicElement 不是天然的显示对象. 因为考虑效率的原因, 他们最小化了显示对象数目(smithfox注: 多个GraphicElement可以在一个DisplayObject上渲染, 这样DisplayObject的总数就可以大大减少).

(smithfox注: GraphicElement是spark的类, 确实是少有继承层次非常少的对象, 如图:)

IVisualElementContainer 定义了content APIs. 在Spark中, SkinGroup, 和 SkinnableContainer 实现了这个接口,持有着可视化元素. 为保持一致性, MX的 Container 也实现了这个接口, 不过只是对addChild(), numChildren, 等函数的封装....

 

As3代码   收藏代码
  1. package mx.core  
  2. {  
  3.   
  4. public interface IVisualElementContainer  
  5. {  
  6.   
  7. //----------------------------------  
  8. //  Visual Element iteration  
  9. //----------------------------------  
  10.   
  11. /**  
  12.  *  The number of elements in this group.  
  13.  *   
  14.  *  @return The number of visual elements in this group  
  15.  */  
  16. public function get numElements():int;  
  17.   
  18. /**  
  19.  *  Returns the visual element that exists at the specified index.  
  20.  *  
  21.  *  @param index The index of the element to retrieve.  
  22.  *  
  23.  *  @return The element at the specified index.  
  24.  *   
  25.  *  @throws RangeError If the index position does not exist in the child list.  
  26.  */   
  27. public function getElementAt(index:int):IVisualElement  
  28.   
  29. //----------------------------------  
  30. //  Visual Element addition  
  31. //----------------------------------  
  32.   
  33. /**  
  34.  *  Adds a visual element to this visual container. The element is   
  35.  *  added after all other elements and on top of all other elements.    
  36.  *  (To add a visual element to a specific index position, use   
  37.  *  the <code>addElementAt()</code> method.)  
  38.  *   
  39.  *  <p>If you add a visual element object that already has a different  
  40.  *  container as a parent, the element is removed from the child   
  41.  *  list of the other container.</p>    
  42.  *  
  43.  *  @param element The element to add as a child of this visual container.  
  44.  *  
  45.  *  @return The element that was added to the visual container.  
  46.   
  47.   
  48.  *   
  49.  *  @event elementAdded ElementExistenceChangedEvent Dispatched when   
  50.  *  the element is added to the child list.  
  51.  *   
  52.  *  @throws ArgumentError If the element is the same as the visual container.  
  53.  */     
  54. public function addElement(element:IVisualElement):IVisualElement;  
  55.   
  56. /**  
  57.  *  Adds a visual element to this visual container. The element is   
  58.  *  added at the index position specified.  An index of 0 represents     
  59.  *  the first element and the back (bottom) of the display list, unless  
  60.  *  <code>layer</code> is specified.  
  61.  *   
  62.  *  <p>If you add a visual element object that already has a different  
  63.  *  container as a parent, the element is removed from the child   
  64.  *  list of the other container.</p>    
  65.  *  
  66.  *  @param element The element to add as a child of this visual container.  
  67.  *   
  68.  *  @param index The index position to which the element is added. If   
  69.  *  you specify a currently occupied index position, the child object   
  70.  *  that exists at that position and all higher positions are moved   
  71.  *  up one position in the child list.  
  72.  *  
  73.  *  @return The element that was added to the visual container.  
  74.  *   
  75.  *  @event elementAdded ElementExistenceChangedEvent Dispatched when   
  76.  *  the element is added to the child list.  
  77.  *   
  78.  *  @throws ArgumentError If the element is the same as the visual container.  
  79.  *   
  80.  *  @throws RangeError If the index position does not exist in the child list.  
  81.  */  
  82. public function addElementAt(element:IVisualElement, index:int):IVisualElement;  
  83.   
  84. //----------------------------------  
  85. //  Visual Element removal  
  86. //----------------------------------  
  87.   
  88. /**  
  89.  *  Removes the specified visual element from the child list of   
  90.  *  this visual container.  The index positions of any elements   
  91.  *  above the element in this visual container are decreased by 1.  
  92.  *  
  93.  *  @param element The element to be removed from the visual container.  
  94.  *  
  95.  *  @return The element removed from the visual container.  
  96.  *   
  97.  *  @throws ArgumentError If the element parameter is not a child of   
  98.  *  this visual container.  
  99.  */  
  100. public function removeElement(element:IVisualElement):IVisualElement;  
  101.   
  102. /**  
  103.  *  Removes a visual element from the specified index position   
  104.  *  in the visual container.  
  105.  *  
  106.  *  @param index The index of the element to remove.  
  107.  *  
  108.  *  @return The element removed from the visual container.  
  109.  *   
  110.  *  @throws RangeError If the index does not exist in the child list.  
  111.  */  
  112. public function removeElementAt(index:int):IVisualElement;  
  113.   
  114. //----------------------------------  
  115. //  Visual Element index  
  116. //----------------------------------  
  117.   
  118. /**  
  119.  *  Returns the index position of a visual element.  
  120.  *  
  121.  *  @param element The element to identify.  
  122.  *  
  123.  *  @return The index position of the element to identify.  
  124.  *   
  125.  *  @throws ArgumentError If the element is not a child of this visual container.  
  126.  */   
  127. public function getElementIndex(element:IVisualElement):int;  
  128.   
  129. /**  
  130.  *  Changes the position of an existing visual element in the visual container.  
  131.  *   
  132.  *  <p>When you call the <code>setElementIndex()</code> method and specify an   
  133.  *  index position that is already occupied, the only positions   
  134.  *  that change are those in between the elements's former and new position.  
  135.  *  All others will stay the same.</p>  
  136.  *  
  137.  *  <p>If a visual element is moved to an index   
  138.  *  lower than its current index, the index of all elements in between increases  
  139.  *  by 1.  If an element is moved to an index  
  140.  *  higher than its current index, the index of all elements in between   
  141.  *  decreases by 1.</p>  
  142.  *  
  143.  *  @param element The element for which you want to change the index number.  
  144.  *   
  145.  *  @param index The resulting index number for the element.  
  146.  *   
  147.  *  @throws RangeError - If the index does not exist in the child list.  
  148.  *  
  149.  *  @throws ArgumentError - If the element parameter is not a child   
  150.  *  of this visual container.  
  151.  */  
  152. public function setElementIndex(element:IVisualElement, index:int):void;  
  153.   
  154. //----------------------------------  
  155. //  Visual Element swapping  
  156. //----------------------------------  
  157.   
  158. /**  
  159. *  Swaps the index of the two specified visual elements. All other elements  
  160. *  remain in the same index position.  
  161. *  
  162. *  @param element1 The first visual element.  
  163. *  @param element2 The second visual element.  
  164. */  
  165. public function swapElements(element1 :IVisualElement, element2 :IVisualElement):void;  
  166.   
  167. /**  
  168.  *  Swaps the visual elements at the two specified index   
  169.  *  positions in the visual container.  All other visual   
  170.  *  elements remain in the same index position.  
  171.  *  
  172.  *  @param index1 The index of the first element.  
  173.  *   
  174.  *  @param index2 The index of the second element.  
  175.  *   
  176.  *  @throws RangeError If either index does not exist in   
  177.  *  the visual container.  
  178.  */  
  179. public function swapElementAt(index1:int, index2:int):void;  
  180.   
  181. }  
  182. }  

 

这个接口使访问树变得容易了. 本质上, 这个接口为容器对外暴露有它哪些孩子提供了方法. 例如, FocusManager就是这样. 该接口使得 focus manager不依赖于Group 或是其它 Spark代码(除了这个接口), MX也不必增加太多代码. 我们讨论过要不要增加这些变异的(mutation) APIs,要不要MX也实现这些接口, 但我们认为这将有助有开发人员(框架开发人员) 实现所有容器(MX和Spark). 当我们看 DataGroup and SkinnableDataContainer 代码时, 你会发现他们并没有实现IVisualElementContainer接口, 尽管DataGroup有几个相似的 "只读的" 方法, 比如 numElements 和 getElementAt().

(smithfox注: 从Spark最终SDK中的代码可以验证, 如图)

 

IVisualElementContainer 持有 IVisualElementsIVisualElement 是可视化元素的一个新接口. 它包含了一些必要的属性和方法以使容器可以增加element. 他继承自 ILayoutElement 并增加了一些其它属性.

////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2003-2008 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package mx.core
{

/**
 *  The IVisualElement interface defines the minimum properties and methods 
 *  required for a visual element to be laid out and displayed in a Spark container.
 */
public interface IVisualElement extends ILayoutElement
{

    /**
     *  The owner of this IVisualElement object. 
     *  By default, it is the parent of this IVisualElement object.
     *  However, if this IVisualElement object is a child component that is
     *  popped up by its parent, such as the drop-down list of a ComboBox control,
     *  the owner is the component that popped up this IVisualElement object.
     *
     *  <p>This property is not managed by Flex, but by each component.
     *  Therefore, if you use the <code>PopUpManger.createPopUp()</code> or
     *  <code>PopUpManger.addPopUp()</code> method to pop up a child component,
     *  you should set the <code>owner</code> property of the child component
     *  to the component that popped it up.</p>
     *
     *  <p>The default value is the value of the <code>parent</code> property.</p>
     */
    function get owner():DisplayObjectContainer;
    function set owner(value:DisplayObjectContainer):void;
    
    /**
     *  The parent container or component for this component.
     *  Only visual elements should have a <code>parent</code> property.
     *  Non-visual items should use another property to reference
     *  the object to which they belong.
     *  By convention, non-visual objects use an <code>owner</code>
     *  property to reference the object to which they belong.
     */
    function get parent():DisplayObjectContainer;
    
    ...OTHER STUFF NOT DISCUSSED HERE...
}
}

(smithfox注: IVisualElement接口为什么是放在mx.core包内,确实有点怪, 但这是事实, 如图)

视觉元素的parent, 也就是容器, 直接负责布局. 视觉元素的owner是视觉元素的逻辑持有组件. 如果一个 Button在一个SkinnableContainer里, 它的parent是contentGroup而它的owner 是这个 SkinnableContainer.

请注意  parent 和 owner 属性类型是 DisplayObjectContainer 而不是 IVisualElementContainer. 这是因为在MX内, 这些属性就是 
DisplayObjectContainer. 此外, 因为 parent 属性是继承自 Flash的 DisplayObject, 我们无法改变他. 我们曾讨论过为这个属性起个新名字, 但最后我们认为这样不值得.

(smithfox注: DisplayObjectContainer是flash.display.Sprite的父类)

转载于:https://www.cnblogs.com/apaqi/archive/2012/06/17/2553012.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值