详细描述
此处的技术和方法可以在通用上下文中被描述为协调对输出设备上的用户界面元素的大小和位置的更新的布局管理器应用程序,但这些技术和方法还达到除了这些目的之外的其它目的。在一个实现中,此处所描述的技术中的一种或多种可被实现为诸如
Silverlight等平台、诸如
等操作系统中的特征,或者来自负责协调对输出设备上的用户界面元素的更新的任何其它类型的程序或服务的特征。
在一个实现中,提供协调对用户界面元素的大小和位置的更新以使得然后可将这些更新呈现在输出设备上的布局管理器。如此处所使用的术语“用户界面元素”旨在包括任何用户界面元素,诸如列表框、组合框、树结构、单选按钮、日历、窗口、表单、面板及其组合。正在不断地创建用户界面对象的新的实现并且所公开的这些示例也涵盖尚未专门命名的用户界面元素。将用户界面元素组织成节点树以使得能够跟踪和更新它们的状态。如此处所使用的术语“树”旨在包括以分层结构或允许标识用户界面元素之间的关系的其它结构来表示的多个用户界面元素。如此处所使用的术语“节点”旨在包括多个用户界面元素的树中的单个用户界面元素。
当给定程序的影响用户界面元素的各部分运行时,布局管理器使用节点树来跟踪这些对用户界面元素的改变。例如,可以更新树中的节点的一个或多个状态指示符以标识该用户界面元素的一个或多个细节已经改变。作为一非限制性示例,可能已经执行改变用户界面上的特定文本框中所包含的文本的字体大小的代码。在一个实现中,在已改变的节点上标记脏指示符以便将该节点标记为具有可能需要在下一次重新呈现该用户界面时更新的一个或多个已改变的细节。这些状态指示符由布局管理器来跟踪并且然后用于确定对显示在输出设备上的给定程序的用户界面元素应呈现什么更新。
例如,状态指示符可由布局管理器在重新呈现对用户界面的更新之前调用的布局过程使用。如此处所使用的术语“布局过程”旨在包括确定用户界面元素的大小和位置的过程。如此处所使用的术语“布局”旨在包括已经确定用户界面元素的大小和位置以反映迄今已影响该用户界面元素的改变的事实。布局过程可包括测量过程和/或安排过程。“测量过程”是推断特定用户界面元素想要多大的过程。在测量过程期间,节点通过确定其所有子节点所需大小来确定该节点所需大小,或者如果该节点没有子节点,则该节点通过其本身需要的大小来确定该节点所需大小。在测量其子节点(如果有的话)时,该节点提供大小约束,该大小约束通常是父节点留着分配给子节点的该节点仍然必须测量的大小;该大小限制还可以是在一个或两个维度上无限制的。在一个实现中,不管节点是否具有子节点,如果已经指定诸如高度、最小高度、最大高度、宽度、最大宽度和最小宽度等其它约束,则也可以使用这些约束。“安排过程”是其中父元素将子节点已经分配到以便按其显示的位置和大小通知给该子节点的过程。该大小可以小于、大于或等于该节点在测量过程中指示它所需要的大小。该节点将相应地确定其本身在屏幕上的位置和大小。也可使用诸如拉伸模式、水平对齐和垂直对齐等属性(如果指定的话)来确定节点的位置和/或大小。
在图4-8中更详细地描述的某些实现中,使用已经在先辈节点上发生的改变的状态来使布局过程(例如,测量和/或安排过程)更高效地执行。如此处所使用的术语“先辈节点”旨在包括到指定节点的向上线性链中所包含的任何前导节点。如此处所使用的术语“父节点”旨在包括指定节点的直接父节点(例如,父亲)。在对给定节点执行布局过程(测量和/或安排过程)时,布局管理器确定任何先辈节点是否已改变。如果任何先辈节点已经改变,则放弃正在进行的对已改变的先辈节点下面的后代节点(例如,子节点)任何布局(测量和/或安排),并且在该已改变的先辈节点处继续布局过程(测量或安排)。如果对于指定节点的任何先辈节点都未改变,则布局过程(测量和/或安排)通过沿着后代节点的路径向下来继续。在一个实现中,通过认识到已经作出对先辈节点的改变,可通过在先辈层而不是在后代层处理改变来避免浪费工作。该工作否则可能已经花费在处理对后代节点的改变上,而该改变在稍后处理对先辈节点的更新时会变得过时。这些技术中的某一些现在将更详细地探究。
如图1所示,用于实现本系统的一个或多个部分的示例性计算机系统包括诸如计算设备100等计算设备。在其最基本的配置中,计算设备100通常包括至少一个处理单元102和存储器104。取决于计算设备的确切配置和类型,存储器104可以是易失性的(如RAM)、非易失性的(如ROM、闪存等)或是两者的某种组合。该最基本配置在图1中由虚线106来示出。
另外,设备100还可具有附加特征/功能。例如,设备100还可包含附加存储(可移动和/或不可移动),包括但不限于磁盘、光盘或磁带。这样的附加存储在图1中由可移动存储108和不可移动存储110示出。计算机存储介质包括以用于存储诸如计算机可读指令、数据结构、程序模块或其他数据之类的信息的任何方法或技术实现的易失性和非易失性、可移动和不可移动介质。存储器104、可移动存储108和不可移动存储110都是计算机存储介质的示例。计算机存储介质包括但不限于,RAM、ROM、EEPROM、闪存或其他存储器技术、CD-ROM、数字多功能盘(DVD)或其他光存储、磁带盒、磁带、磁盘存储或其他磁存储设备、或者可用于存储所需信息并且可由设备100访问的任何其他介质。任何这样的计算机存储介质都可以是设备100的一部分。
计算设备100包括允许计算设备100与其它计算机/应用程序115进行通信的一个或多个通信连接114。设备100还可以具有诸如键盘、鼠标、笔、语音输入设备、触摸输入设备等输入设备112。还可以包括诸如显示器、扬声器、打印机等输出设备111。这些设备在本领域中公知且无需在此处详细讨论。在一个实现中,计算设备100包括布局管理器应用程序200。布局管理器应用程序200将在图2中更详细地描述。
现在转向图2,并继续参考图1,示出了在计算设备100上操作的布局管理器应用程序200。布局管理器应用程序200是驻留在计算设备100上的应用程序之一。然而,可以理解,布局管理器应用程序200可另选地或另外地被具体化为一个或多个计算机上的计算机可执行指令和/或与图1所示的不同的变型。另选地或另外地,布局管理器应用程序200的一个或多个部分可以是系统存储器104的一部分、可以在其它计算机和/或应用程序115上、或可以是计算机软件领域的技术人员能想到的其它此类变型。
布局管理器应用程序200包括程序逻辑204,其负责执行在此描述的一些或全部技术。程序逻辑204包括用于协调对用户界面元素的大小和位置的更新的逻辑206(如以下参考图3所描述的);用于在元素树中跟踪用户界面元素的脏状态的逻辑208(如以下参考图4-7所描述的);用于更新元素树的受影响部分的逻辑210(如以下参考图6所描述的);用于使用树中的节点上的特殊状态来查找脏子树的根节点以便在更新期间避免浪费的重新计算的逻辑212(如以下参考图4-7所描述的);以及用于操作布局管理器应用程序200的其它逻辑220。
图3是具有节点的两个不同的脏子树的示例性节点树的图示。如此处所使用的术语“子树”旨在包括树中的较小的节点子集,或换言之,树中树。如此处所使用的术语“脏子树”旨在包括被标记为脏并且只有一个节点的父节点不为脏的一个或多个连续先辈和后代节点。查看一些示例以使得该定义更清楚。在图3所示的示例中,存在两个脏子树。第一个子树包含画布1。画布1是第一个子树的唯一成员,因为它不具有也为脏的直接先辈或后代节点。第二个子树包含画布3和R4。画布3和R4具有先辈/后代关系,是连续的,并且画布3是该子树中不具有也为脏的直接先辈节点的唯一成员。脏子树的概念将在对诸如图6等稍后的某些附图的讨论中使用。
现在转向图4-8,并继续参考图1-3,更详细地描述了用于实现布局管理器应用程序200的一个或多个实现的各阶段。在某些实现中,图4-8的过程至少部分地在计算设备100的操作逻辑中实现。图4-5提供了使用先辈改变来使布局过程更高效的某些高级示例。图6-8描述了用于将这些较宽泛的概念应用于示例性实现的某些更详细的技术。
现在转向图4,示出了例示在使用先辈改变的状态来使布局过程(测量、安排等)更高效时所涉及的各阶段的一个实现的处理流程图240。对给定节点调用布局过程(阶段242)。布局管理器应用程序200确定该给定节点的任何先辈节点是否已经改变(阶段244)。如果任何先辈节点已经改变(判定点246),则放弃正在进行的对已改变的先辈节点下面的后代节点的布局(阶段248)并且在已改变的先辈节点处继续布局过程(阶段249)。如果没有先辈节点已经改变(判定点246),则布局过程通过沿着后代节点的路径向下来继续(阶段250)。
现在转向图5,示出了例示在当节点的大小已经改变时将父节点标记为脏以避免浪费工作时所涉及的各阶段的一个实现的处理流程图270。对指定节点开始布局过程(阶段272)。如果该节点的所需大小已经改变(判定点274),则在该指定节点的父节点上标记脏指示符(阶段278),放弃当前布局过程(阶段278),并且对父节点执行布局过程(阶段280),如在其它附图中更详细地描述的。如果节点的所需大小未改变(判定点274),则布局过程对该节点的下一兄弟节点继续(阶段282)。
图6是示出在通过处理脏子树来在呈现之前更新布局时所涉及的各阶段的一个实现的处理流程图320。在开始更新布局过程时,布局管理器200确定窗口大小是否改变或者是否在树中某处需要测量元素(例如,测量为脏)(判定点322)。窗口可包括其中正在显示用户界面元素的任何屏幕区域,诸如整个屏幕、屏幕中的窗口、浏览器页面区域等。如果这些条件中的任一个为真,则测量脏子树的根节点(阶段324)并且然后递归地再次执行评估(以便处理每一个节点的子节点)。如果这些条件中没有一个为真(判定点322),则布局管理器200确定是否在树中某处需要安排元素(例如,安排为脏)(判定点326)。如果是,则安排根节点(阶段328)并且该过程向上循环返回至判定点322。
如果在树中某处安排不为脏(判定点326),则激发任何排队的大小已改变事件(阶段330)以允许该应用程序在以其新大小显示元素之前响应于该元素改变其大小来执行可任选的自定义代码。如果树为脏(判定点332),则该过程向上循环返回至判定点322。如果树不为脏(判定点332),则激发布局已更新事件(阶段334)以允许应用程序在以其新大小和/或位置显示已更新的元素中的任一个之前响应于更新布局操作完成来执行自定义代码。如果树为脏(判定点336),则该过程向上循环返回至判定点322。如果树不为脏(判定点336),则该过程退出(阶段338)。
现在转向图7,示出了例示在对指定节点执行布局过程时所涉及的某些详细阶段的一个实现的处理流程图350。如果指定节点本身不为脏(判定点352),则布局管理器200检查以查看该指定节点是否具有脏后代节点(判定点358)并遵循对于脏后代节点路径的处理,如即将描述的。如果指定节点本身为脏(判定点352),则对该指定节点执行布局过程(阶段354)。如果先辈节点现在已被标记为脏(判定点356),则对于该指定节点放弃布局过程(阶段357)。
如果在对指定节点执行布局后先辈节点尚未被标记为脏(判定点356),则布局管理器200确定该指定节点是否具有脏后代节点(判定点358)。如果在判定点352发现指定节点本身不为脏,则布局管理器还确定该指定节点是否具有脏后代节点。在任一种情况下,如果指定节点不具有脏后代节点(判定点358),则布局过程退出(阶段362)。如果指定阶节点不具有脏后代节点(判定点358),则检索(阶段360)并布置(阶段364)下一子节点。在布置子节点后,布局管理器200确定先辈节点是否被标记为脏(判定点366)。如果是(判定点366),则放弃布局过程(阶段357)。如果先辈节点未被标记为脏(判定点366),则布局管理器200确定该节点本身是否为脏(判定点367)。如果节点本身为脏(判定点367),则对该节点执行布局过程(阶段354)并且遵循布局过程的其它阶段如上所述地执行。如果节点本身不为脏(判定点367),则在存在更多子节点的情况下处理下一子节点(阶段368)。如果不存在更多子节点(判定点368),则该过程向上循环返回至处理脏子树中的当前父节点的下一兄弟节点(阶段352)。
在一个实现中,在每一个节点上维护六个不同的指示符以便作出上述决定中的某一些并且确定如何高效地处理更新。这六个示例性指示符在下文中示出。
·测量为脏(IsMeasureDirty)位:将节点标记为关于测量为脏
·在到测量为脏的路径上(IsOnPathToMeasureDirty)位:将节点标记为具有关于测量的脏后代节点
·安排为脏(IsArrangeDirty)位:将节点标记为关于安排为脏
·在到安排为脏的路径上(IsOnPathToArrangeDirty)位:将节点标记为具有关于安排的脏后代节点
·先辈为脏(IsAncestorDirty)位:将节点标记为具有脏先辈节点
·在栈上(IsOnStack)位:标记其布局(测量或安排)正在进行中
在其它实现中,可使用更少或更多的指示符。这些指示符现在将为了说明起见在以下代码样本中使用。该样本走查用于执行图6和7中所描述的布局过程的某些示例性代码。
布局管理器::更新布局伪代码
While(true)
{
If(显示区域大小改变,或者根节点已将“测量为脏”或“在到测量为脏的
路径上”标志置位)
{
pRoot->测量(显示区域大小);
}
Else if(根节点已将“安排为脏”或“在到安排为脏的路径上”标志置位)
{
pRoot->安排(显示区域大小矩形);
}
Else
{
If(存在需要激发的大小已改变事件)
激发大小已改变事件
If(根节点不需要测量或安排(参见上文))
激发布局已更新事件
}
Else
Break;(退出while循环)
}
CUI元素::测量伪代码
CUI元素::测量(可用大小)
{
在测量栈上=true;
先辈为脏=false;
while(true)
{
if(测量为脏‖可用大小已经改变)
{
测量内部(可用大小);
If(先辈为脏‖布局挂起)
Break;
在测量为脏路径上=false;
}
Else if(在测量为脏路径上)
{
在测量为脏路径上=false;
Foreach(各子节点中的子节点)
{
If(子节点->测量为脏‖子节点->在测量为脏路径上)
{
子节点->测量(子节点->前一约束);
}
If(先辈为脏)
{
在测量为脏路径上=true;
转到清理;
}
If(测量为脏)
{
Break;//退出foreach循环
}
}
}
Else
Break;//退出while循环
}
清理:
在测量栈上=true;
}
CUI元素::安排伪代码
CUI元素::安排(最终矩形)
{
在安排栈上=true;
先辈为脏=false;
while(true)
{
if(任一节点需要测量)
break;//退出while循环
if(安排为脏‖最终矩形已经改变)
{
布置内部(可用大小);
If(先辈为脏‖布局挂起)
Break;
在安排为脏路径上=false;
}
Else if(在安排为脏路径上)
{
在安排为脏路径上=false;
Foreach(各子节点中的子节点)
{
if(任一元素需要测量)
break;//退出foreach循环
if(子节点->安排为脏‖子节点->在安排为脏路径上)
{
子节点->安排(获取安排矩形());
}
if(先辈为脏)
{
在安排为脏路径上=true;
Break;//退出foreach循环
}
}
}
Else
Break;//退出while循环
}
清理:
在安排栈上=true;
}
如在更新布局过程中描述的,如果显示区域大小改变,或者根节点已将“测量为脏”或“在到测量为脏的路径上”标志置位为真(即,其本身为脏或具有脏后代节点),则调用该测量过程。否则,如果已将“安排为脏”或“在到安排为脏的路径上”标志置位,则调用安排过程。
示例性代码样本中示出的测量和安排过程然后在适当时递归地循环通过节点树以取决于对此处(且图7中)所描述的各种准则的评估来确定是沿先辈节点向上测量或安排还是沿后代节点向下测量或安排。
图8是示出在使布局过程无效时所涉及的各阶段的一个实现的处理流程图390。如果指定节点已经为脏(判定点392),则使布局无效过程退出(阶段394)。如果指定节点还不为脏(判定点392),则将指定节点标记为脏(阶段396)。如果指定节点是当前正在布置的节点的先辈节点(判定点398),则将从该节点到当前正在布置的节点的直接路径上的所有节点(但不包括该节点)标记为具有脏先辈节点(阶段400)。如果该节点不是当前正在布置的节点的先辈节点(判定点398),则使布局无效过程退出(阶段402)。
以下示出某些示例性源代码以便更详细地示出图8的过程。在所示示例中,使测量无效(InvalidateMeasure)过程检查以查看指定节点是否已经在栈上(即,已经在测量),并且如果是,则使测量无效过程退出。将该指定节点标记为脏,并且然后将先辈节点标记为脏。
UI元素::使测量无效()
{
//如果我们已经在测量,则不要弄脏我们。
If(当前在栈上==this)
{
Return;
}
//将我们自己标记为脏
为脏=true;
//标记所有先辈节点上的面包屑
先辈节点=this.父节点;
While(先辈节点!=NULL&&先辈节点.在到脏节点的路径上==
false)
{
先辈节点.在到脏节点的路径上=true;
先辈节点=先辈节点.父节点;
}
//现在,如果我们在栈上,则将从栈上的当前节点到
//我们(但不包括我们)标记为具有脏先辈节点
If(在栈上)
{
子节点=栈上的当前节点;
While(子节点!=NULL&&子节点!=this)
{
子节点.先辈节点为脏=true;
子节点=子节点.父节点;
}
}
}
这些非限制性代码示例只是为了进一步说明这些技术中的某一些而提供的。计算机软件领域的技术人员可以理解存在可将源代码编写为实现此处所描述的技术中的部分或全部的众多其它方式。
尽管用结构特征和/或方法动作专用的语言描述了本主题,但可以理解,所附权利要求书中定义的主题不必限于上述具体特征或动作。相反,上述具体特征和动作是作为实现权利要求的示例形式公开的。落入在此所述和/或所附权利要求所描述的实现的精神的范围内的所有等效方案、更改和修正都期望受到保护。
例如,计算机软件领域普通技术人员将认识到,此处所讨论的示例可以在一个或多个计算机上不同地组织来包括比这些示例中所描绘的更少或更多选项或特征。