flex4.5 如何通过后台给group添加scroller?用html5 viewportt的方式 我加完总是不显示滚动条。求大神指点。。。

1403人阅读
这篇文章是拷贝过来的,读完确实让我对Flex了解的更通透了,文章比较长,请耐心看完。
英文原文:&
翻译的原创链接: &&&转载请注明, 文中如果有什么错误的地方或是讲的不清楚的地方,欢迎大家留言.
这是一篇难得的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的皮肤文件如下:
&s:Skin xmlns:fx=&/mxml/2009& xmlns:s=&library:///flex/spark&
minWidth=&23& minHeight=&23&&
&fx:Metadata&
[HostComponent(&ponents.Button&)]
&/fx:Metadata&
&s:states&
&s:State name=&up& /&
&s:State name=&over& /&
&s:State name=&down& /&
&s:State name=&disabled& /&
&/s:states&
&!-- background --&
&s:Rect left=&0& right=&0& top=&0& bottom=&0&
width=&70& height=&23&
radiusX=&2& radiusY=&2&&
&s:stroke&
&s:SolidColorStroke color=&0x5380D0& color.disabled=&0xA9C0E8& /&
&/s:stroke&
&s:SolidColor color=&0xFFFFFF& color.over=&0xEBF4FF& color.down=&0xDEEBFF& /&
&!-- label --&
&s:Label id=&labelDisplay& /&
尽管Button看上去是一个叶子结点, 但因为皮肤的存在, 实际上他包含了孩子. 为访问这些元素,所有SkinnableComponent对象都定义了skin属性.
这样就可以通过Button.Skin实例来访问Rectangle&和Label. 如要访问Label,
你可以写成:myButton.skin.getElementAt(2)或是&myButton.skin.labelDisplay.由于labelDisplay是&Button&的
skin part, 所以你可可以直接写成&myButton.labelDisplay.
同样的原则也一样适用在SkinnableContainer.&SkinnableContainer是容器所以天然就有孩子, 但同时他们也是SkinnableComponent,所以也有一个皮肤以及来自皮肤的孩子.
(smithfox注: SkinnableContainer的确是继承自SkinableComponent, 见图)
还是以Panel为例:
&s:Button /&
&s:Label /&
&s:CheckBox /&
&/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, 和一个 但实际上真正的孩子是一个panel皮肤实例. button, label, 和 checkbox 向下变成了皮肤中contentGroup的孩子.
有几种方法可以访问panel中的Button:&myPanel.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中,&Skin,&Group,
和&SkinnableContainer&实现了这个接口,持有着可视化元素. 为保持一致性, MX的&Container&也实现了这个接口,
不过只是对addChild(), numChildren, 等函数的封装....
这个接口使访问树变得容易了. 本质上, 这个接口为容器对外暴露有它哪些孩子提供了方法. 例如,FocusManager就是这样. 该接口使得 focus manager不依赖于Group&或是其它 Spark代码(除了这个接口),
MX也不必增加太多代码. 我们讨论过要不要增加这些变异的(mutation) APIs,要不要MX也实现这些接口, 但我们认为这将有助有开发人员(框架开发人员) 实现所有容器(MX和Spark). 当我们看 DataGroup and SkinnableDataContainer 代码时, 你会发现他们并没有实现IVisualElementContainer接口, 尽管DataGroup有几个相似的
&只读的& 方法, 比如&numElements&和&getElementAt().
(smithfox注: 从Spark最终SDK中的代码可以验证, 如图)
IVisualElementContainer&持有&IVisualElements.&IVisualElement&是可视化元素的一个新接口.
它包含了一些必要的属性和方法以使容器可以增加element. 他继承自&ILayoutElement&并增加了一些其它属性.
(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的父类)
MX 组件和有上面有着相同的概念, 但是大部分隐藏在后台. Spark组件则因为皮肤化就变得更加透明.
一个MX button有一个孩子, 就是TextField. 这个孩子是直接通过addChild() (没有皮肤)方法加到Button的. 例如, 这个Button的TextField就是Button的孩子. 所以如果你查看Button的孩子, 他将返回给你这个TextField. 如果你问这个TextField父亲, 他将返回这个Button.
在Spark中, 一个Button只有一个孩子, 皮肤对象. 皮肤对象包含了一个Label. 如果你问Button的显示对象孩子, 它将告诉你它有一个孩子:皮肤. 如果你想确认Button皮肤的孩子, 你应该调用皮肤对象中的方法.
容器有些难懂,它包括了组件孩子和皮肤孩子. 在MX中, Panel的显示列表包含了皮肤孩子和一个叫&contentPane&的组件孩子. panel的所有组件孩子都放到这个contentPane. 这和Spark非常象; 然而, 在MX中对开发人员隐藏了太多细节. 如果你问Panel的显示列表孩子, 它其实对你撒谎了, 它返回你这个contentPane孩子(Panel的组件孩子). 为访问皮肤孩子, 可以通过rawChildren&属性返回孩子列表.
如果你问Panel的组件孩子的它的父亲是谁, 它会告诉你是这个panel, 但实际上他的父亲应该是contentPane.
在Spark中,&IVisualElementContainer接口可以让你访问孩子. 这也是Spark组件宣布谁是他的可视化孩子的方式.Group&和&SkinnableContainer&都实现了这个接口.
另外, MX的&Container&也实现了这个接口. 但那只是对显示列表APIs的一种封装,&IVisualElementContainer&提供了唯一的,一致的访问容器孩子的方法.
在Spark中,&SkinnableContainer 仍然有DisplayList API(smithfox注: 就是在Flex 3中的操作children的函数, 比如addChild). 但是, 但是如果你想试图通过这些API操作 DisplayList, 我们将抛出一个运行时异常. 当你访问&numChildren&或是&getChildAt()函数,
不像在MX中, Spark会如实地返回他的显示列表. 当你调用SkinnableContainer的 &content API& (numElements, getElementAt()) &,它将返回它的组件孩子 (contentGroup的实际的所有孩子). 要访问皮肤孩子 (就象MX组件中的&rawChildren&), 你需要调用skin对象的方法. 当你问Panel组件的孩子问谁是它的parent, 它会返回contentGroup&(不象MX返回这个Panel).
但是有另外一个属性会返回Panel, 那就是owner. owner属性MX也有, 但是在MX中和它parent属性返回的是一样. 在Spark中, owner 和 parent则指向了不同的对象.
在Spark中, 有两个主要的容器类型: 一个容纳可视化元素,另一个容纳数据项.&DataGroup&和&SkinnableDataContainer&用来容纳数据项.&Group&和&SkinnableContainer&用来容纳可视化元素.
一个数据容器能容纳任何东西, 但特别是用来容纳非可视元素 (比如.-真正的数值). 有关数据容器重要的一点是它们支持 项渲染,就是将数据项转换为可视元素.
DataGroup&有能力将随意的非可视化元素呈现到屏幕. 因此, 项渲染器正好可以加到布局树中. 某些情况下, 甚至于可视化元素,比如&UIComponents&和&GraphicElements,
也被包装成项渲染器. 为向开发人员展现这个设计思路,我们考虑一下以下几个可选方案:
DataGroup 和 SkinnableDataContainer 设计成叶子节点, 他们的实际可视化孩子不能被访问
DataGroup 和 SkinnableDataContainer 实现IVisualElementContainer接口. 当问屏幕上有几个可视化元素时, 我们只返回当前屏幕正在被渲染的那些元素. Mutation APIs RTE(RuntimeException).
DataGroup 和 SkinnableDataContainer 实现IVisualElementContainer接口. 当问屏幕上有几个可视化元素时, 我们返回所有项个数. 如果用户访问一个还未曾被渲染过的项时, 我们就创建并且渲染.
我们决定向DataGroup增加&&只读&的 element APIs, 象numElements,&getElementAt(), 和&getElementIndex().
还有另一个API,getItemIndicesInView()决定哪些数据项在屏幕显示.
象MX一样, 项渲染器的&owner&属性总是和组件的 owner属性是一样的. 项渲染器的&parent&属性负责渲染.
这两个图显示了项渲染的运行.
你会注意到DataGroup&在组件树中没有孩子.
这是因为它被看着是渲染数据的叶子节点. 下图是DataGroup的布局树例子:
(注: TextBox 已更名为 Label)
上面例子中, 字符串不是一个可视化的元素并且需要一个项渲染器. 创建一个项渲染器包装这个字符串对象. 它的owner属性就是DataGroup. 因为设置了一个 itemRendererFunction 对象,所以 Employee
Object 和其它的字符串一样都会得到处理.
开发人员通常只和组件树打交道. 布局和效果就像FocusManager一样和布局树打交道. 只有像Group的 DisplayObject的sharing code这样的底层的代码才和显示树打交道.
public interface IVisualElementContainer {
public function get numElements():
public function getElementAt(index:int):IVisualE
public function getElementIndex(element:IVisualElement):
public function addElement(element:IVisualElement):IVisualE
public function addElementAt(element:IVisualElement, index:int):IVisualE
public function removeElement(element:IVisualElement):IVisualE
public function removeElementAt(index:int):IVisualE
public function setElementIndex(element:IVisualElement, index:int):
public function swapElements(element1:IVisualElement, element2:IVisualElement):
public function swapElementsAt(index1:int, index2:int):
public interface IVisualElement extends ILayoutElement {
owner:DisplayObjectC
parent:DisplayObjectC
...other stuff not discussed here...
public class UIComponent implements IVisualElement {
owner:DisplayObjectC
parent:DisplayObjectC
...other stuff...
public class GraphicElement implements IVisualElement {
owner:DisplayObjectC
parent:DisplayObjectC
...other stuff...
[DefaultProperty(&content&)]
public class Group extends GroupBase implements IVisualElementContainer {
[write-only] mxmlContent:A
public function get numElements():
public function getElementAt(index:int):IVisualE
public function getElementIndex(element:IVisualElement):
public function addElement(element:IVisualElement):IVisualE
public function addElementAt(element:IVisualElement, index:int):IVisualE
public function removeElement(element:IVisualElement):IVisualE
public function removeElementAt(index:int):IVisualE
public function setElementIndex(element:IVisualElement, index:int):
public function swapElements(element1:IVisualElement, element2:IVisualElement):
public function swapElementsAt(index1:int, index2:int):
public class Skin extends Group {
public class SkinnableComponent extends UIComponent {
function get skin():S
[CSS] function set skinClass:C
[DefaultProperty(&content&)]
public class SkinnableContainer extends SkinnableContainerBase implements IVisualElementContainer {
[write-only] mxmlContent:A
public function get numElements():
public function getElementAt(index:int):IVisualE
public function getElementIndex(element:IVisualElement):
public function addElement(element:IVisualElement):IVisualE
public function addElementAt(element:IVisualElement, index:int):IVisualE
public function removeElement(element:IVisualElement):IVisualE
public function removeElementAt(index:int):IVisualE
public function setElementIndex(element:IVisualElement, index:int):
public function swapElements(element1:IVisualElement, element2:IVisualElement):
public function swapElementsAt(index1:int, index2:int):
[SkinPart] contentGroup:G
public class Container extends UIComponent implements IVisualElementContainer {
public function get numElements():
public function getElementAt(index:int):IVisualE
public function getElementIndex(element:IVisualElement):
public function addElement(element:IVisualElement):IVisualE
public function addElementAt(element:IVisualElement, index:int):IVisualE
public function removeElement(element:IVisualElement):IVisualE
public function removeElementAt(index:int):IVisualE
public function setElementIndex(element:IVisualElement, index:int):
public function swapElements(element1:IVisualElement, element2:IVisualElement):
public function swapElementsAt(index1:int, index2:int):
[DefaultProperty(&dataProvider&)]
public class DataGroup extends UIComponent {
dataProvider:IL
itemRenderer/itemRendererF
public function get numElements():
public function getElementAt(index:int):IVisualE
public function getElementIndex(element:IVisualElement):
public function getItemIndicesInView():Vector.;
[DefaultProperty(&dataProvider&)]
public class SkinnableDataContainer extends SkinnableContainerBase {
dataProvider:IL
itemRenderer/itemRendererF
[SkinPart] dataGroup:DataG
//遍历这些树的样例代码:
public function walkTree(element:IVisualElement, proc:Function):void
proc(element);
if (element is IVisualElementContainer)
var visualContainer:IVisualElementContainer = IVisualElementContainer(element);
for (var i:int = 0; i & visualContainer.numE i++)
walkTree(visualContainer.getElementAt(i));
public function walkLayoutTree(element:IVisualElement, proc:Function):void
proc(element);
if (element is SkinnableComponent)
var skin:Skin = SkinnableComponent(element).
walkTree(skin);
else if (element is IVisualElementContainer)
var visualContainer:IVisualElementContainer = IVisualElementContainer(element);
for (var i:int = 0; i & visualContainer.numE i++)
walkTree(visualContainer.getElementAt(i));
// expand this to MX and IRawChildrenContainer?
public function walkUpTree(element:IVisualElement, proc:Function):void
while (element!= null)
proc(element);
element = element .
public function walkUpLayoutTree(element :IVisualElement, proc:Function):void
while (element != null)
proc(element );
element = element .
更多关于 parent/owner
有一种看待parent&属性的方法是: &谁布局我&. 如果你是一个DisplayObject, 这同时也对应着你的物理显示列表parent. (GraphicElements这里做了一点伪装,因为他们并不是显示对象,但也同一个概念).
owner属性的用途:
它能告诉你在组件树(或是SkinnableContainer中的elements)中谁是你的父亲
它能告诉项渲染器哪个数据容器负责他们
它还用在弹出窗口, 象&DateField, 它告诉你谁在负责这个弹出窗口.
看待owner属性的方式就是: 一个元素的&owner&指向了负责它的组件.
需要做的一些变化(smithfox注: 这个是Flex 4的设计规格,所以应该是说给adobe开发人员听的)
为GraphicElement增加parent和owner属性. 在适当的地方将这些属性(项渲染器和SkinnableContainer)衔接起来.
建议主要还是创建和实现这些接口.
已经分开了Group和DataGroup. 这就可以按完全独立的规范性工作项目来运作.
最后, 还需要做些虚拟化规范相关的工作, 以实现怎样呈现这样已经渲染过的元素.
重要的/有争议的 观点:
这些不同的DOM树有些让人迷惑, 我们需要向Flex开发人员做些明确的解释.
我们引入owner&属性是因为这样人们可以遍历逻辑DOM树. 我们引入&parent&属性是因为这样人们可以遍历布局DOM树. 我们考虑过是否不需要parent属性,因为它的类型是DisplayObjectContainer,
在将来的某个时候, parent 节点不必一定是DisplayObjectContainers. 但现在还是,&parent这个名字比其它名字要适合一些. 如果我们决定使容器变成非DisplayObjects, 那时我们可能会贯彻到底,
将所有的DisplayObject都变成是可选的l.
Walking the layout tree requires knowledge of&SkinnableComponent&and&Skin. This means Mustella (or other places) will
need to bring these classes in (or treat them as untyped).
MX也实现IVisualElementContainer接口.
owner&属性看上去有3个不同的用途.
Scroller&也实现&IVisualElementContainer&接口以宣布它有一个孩子. 我们考虑过为&decorators(装饰)& 创建一个单独的接口, 但我们倾向更通用这样也能处理&HDividedBoxes&.
这些&getter&方法将能在Scroller使用, 其它的就抛出运行时异常.
我们考虑过在gumbo容器中支持flash原生display objects, 但最后还是否决了.
我们需要支持新的组件工具包和那些用老的组件工具包制作的swc. 一种解决方案是always link in UIMovieClip and the other FCK classes. 这些新定义的类将会实现IVisualElement&和&IVisualElementContainer接口.
因为这些类是新定义的, 它们将会覆盖老版本的基类. 另一个解决方案是只更新组件工具包而不再支持老的scw. 我们需要更多的PM的决定; 但是不管怎么样, 这样类是需要更样的.
我们同时有多套Flash DisplayObjectContainer APIs, 他们分别继承自Group/DataGroup/SkinnableComponent (addChild(), getChildAt(), 等). 为处理这个问题, 所有mutation(变异的) APIs调用 (addChild(), removeChild(), swapChildren(), 等...) 都将抛出运行时异常. 只有允许调用&getters& 类的方法. 我们也试图在正常API&(getChildAt, numChildren,
etc...)调用时也抛出异常, 但会有架构方面的问题, 比如UIComponent的 removeChildAt 方法就依赖于这些API.&如果这原来是一个优先事项, 我们可以在这些方法中都加入运行异常并且提供新的方法, 比如 $getChildAt_SkinnableComponent之类. 然后我们在这些新API的基础上改动所有framework的代码. 这样做又有新的问题:&[Child APIs vs. Item APIs].
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:490838次
积分:5835
积分:5835
排名:第1462名
原创:80篇
评论:485条
姓名:高爽
生日:1989.6
学历:本科
学校:黑龙江科技大学
毕业:2012.7
爱好:骑行、乒乓球、旅行、摄影
邮箱&MSN:
阅读:11110
文章:14篇
阅读:204640
(1)(1)(1)(1)(1)(1)(2)(5)(1)(4)(2)(4)(3)(2)(1)(1)(1)(4)(4)(3)(2)(5)(1)(3)(1)(11)(16)(8)(2)4906人阅读
大家知道,Spark组件中很多组件不自带滚动条,如果想用滚动条效果,需要手动添加。
如VBox组件在Flex 4.5以后推荐使用VGroup代替,而VGroup就没有滚动条效果,要想做到有工具条效果有两种方式:
1、通过组件添加
&s:Scroller width=&100& height=&100&&
&s:Group horizontalScrollPosition=&50& verticalScrollPosition=&50&&
&mx:Image width=&300& height=&400& source=&@Embed(source='assets/logo.jpg')&/&
&/s:Group&
&/s:Scroller&
2、通过代码添加
这里需要注意的是,虽然看到上面的Scroller标签在Group标签之外,但是Scroller不是普通的容器,它不是通过来添加。只能通过viweport来添加了。
代码为:var p:Panel = new Panel();
var sc:Scroller = new Scroller();
panel.addElement(sc);
sc.viewport = visualElement as IV
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:125164次
积分:1729
积分:1729
排名:第10080名
原创:30篇
转载:125篇
评论:26条
(1)(3)(1)(1)(2)(11)(6)(8)(2)(11)(23)(12)(14)(23)(37)}

我要回帖

更多关于 scroller 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信