Firemonkey 的Presentation控件初步了解(1)
早期的FMX控件都是TStyledContronl的,也就是控件都支持Style,可以粗粗理解为控件只有外观颜色上的变化,但是控件外观的样子是一样的。后来,部分控件增加了Presentation层(控件继承自TPresentedControl),这一层支持StyledPresentation。支持Presentation层的控件不仅是外观颜色上的变化,还有外观样子上的变化,如TEdit, TGrid,TComboEdit(FMX下,这个控件和TComboBox是不一样的,而且TComboBox也不是TPresentedControl控件),TCalendar等常用控件也是继承自TPresentedControl。
但是FMX.StdCtrls下的控件,虽然继承自TPresentedTextControl或基类TPresentedControl, 如Button,Label,Panel,GroupBox等,实际是不支持Presentation的,还是TStyledControl控件。这里是通过TPresentedControl类的LoadPresentation()方法进行判别,
支持Presentation的,是有专门PresentationName的(CanUseDefaultPresentation := False),其PresentationName格式为:类名称(去了首字母"T") +”-“ + 控件类型(控件的属性TControlType名称, “Styled"和"native"),并且其PresentationName是预先注册在TPresentationProxyFactory列表中的;
而不支持Presentation的,是采用默认名称的(CanUseDefaultPresentation := True),其PresentationName格式为:”default-“ + 控件类型(控件的属性TControlType名称, “Styled"和"Platform") ,直接就交给TPresentedControl的父类TStyledControl负责,并没有在TPresentationProxyFactory中注册。
有专门PresentationName的控件,通过一个单例对象TPresentationProxyFactory(在FMX.Presentation.Factory.pas 单元)管理其PresentationName和TPresentationProxy。支持Presentation的控件PresentationName和其对应的TPresentationProxy是在系统启动的时候就加载好了,这是通过支持Presentation的控件的相应Style单元的初始化部分initialization进行的,比如TEdit控件有FMX.Edit.Style.pas,其initialization节调用了函数: TPresentationProxyFactory.Current.Register(TEdit, TControlType.Styled, TStyledPresentationProxy<TStyledEdit>); TGrid控件也有相应的FMX.Grid.Style.pas(但是这个控件没有公开OnPresentationNameChoosing)。
TPresentedControl控件提供了几种界面外观,采用了类似MVC设计模式的分层设计。各层的基类是:Mode是TDataModel,View是TStyledPresentation,Controler是TPresentedControl(控件自身)。比如对于TEdit控件,M是TCustomEditModel,V是TStyleEdit,C是TCustomEdit(控件自己)。
TPresentedControl控件的分层设计模式和常用的MVC不同(也不同于MVP)。Controler和Model有一一对应关系,Controler拥有Model,Controler直接调用Model;但是Vew(Style)是未确定的,也就是View和Controler是完全分离的(View在设计时,是在控件装载时载入的通过控件(TPresentedControl)的Loaded()方法;所以在运行时,我们可以定制的控件View(也就是Style)替代控件的默认View(Style),而不用额外创建一个控件。这是通过TPresentedContro的事件OnPresentationNameChoosing来实现);既然View(Style)是未确定的,所以Model和View之间的通信就有两种情况,一是Model到View,因为不知道具体的View,所以在设计时采用了代理设计模式,代理类有TPresentationProxy及其子类TStyledPresentationProxy,以及其它的一些辅助类,实现了Model到View的通信,二是View(Style)到Model的通信,由于Model是已知的,所以直接调用Model方法。这里的关键是Model到View(Style)的消息通信实现。
通过分层设计模式,实现外观变化是有了,但是代价也不少,比如具体控件的Style和Mode的设计,是要化不少心思的。看TEdit的代码实现,就比简单的TEdit复杂了好多。
另外,FMX的ListBox,ListView不是TPresentedControl控件,但是也实现了复杂的外观变化.......