您的位置:首页 > 编程语言 > Delphi

学习: Delphi FireMonkey 结构性初略分析

2014-12-27 07:30 267 查看
Delphi 下的FireMonkey,很好地实现了 DirectUI与跨平台。学习了解他,对DirectUI编程及项目的跨平台实现有一定帮助。
虽然作为开发者个体,并不需要了解太多这些东西,只要求拿来能用能实现功能就行,但对 FireMonkey的学习分析,对自己程序设计思想的提升,会有一定帮助。

昨天用FireMonkey控件写了一个小例子,发现他的 Animation类在实现控件的小动画时,很高效,很灵活。
初步印象是 FireMonkey的内核有很多值得学习的地方,尤其他的界面渲染上,可以深入了解。鉴于都有代码,了解只是时间上的问题。

今天才开始认真地看了下 FireMonkey的代码。跟踪查看了他的程序启动的各个步骤。先初略整理一下。在了解框架之后,再逐步深入了解其他各方面。

FireMonkey跨平台实现:

FM为了考虑跨平台,使用了很多服务接口类,来转嫁各种服务任务处理。

应用程序管理接口IFMXApplicationService
系统字体接口IFMXSystemFontService
窗体创建接口IFMXWindowService
系统菜单接口IFMXMenuService
计时器接口IFMXTimerService
鼠标拖曳接口IFMXDragDropService
粘贴板接口IFMXClipboardService
鼠标形状接口IFMXCursorService
鼠标消息接口IFMXMouseService
系统桌面接口
IFMXScreenService
本地化与文本输出接口IFMXLocaleService, IFMXTextService
上下文菜单显示接口IFMXContextService
绘图与设备接口IFMXCanvasService, IFMXDeviceService
界面外观与窗口外观接口IFMXStyleService, IFMXWindowBorderService
其他接口IFMXSystemInformationService, IFMXLoggingService
IFMXFullScreenWindowService, IFMXListingService, IFMXSaveStateService,
IFMXDeviceMetricsService, IFMXGestureRecognizersService,
IFMXWindowsTouchService
看似这些接口很全很复杂。其实,所有接口都转到一个平台服务类来进行处理。这个平台服务类根据操作系统的不同分为 TPlatformWin, TPlatformIOS, TPlatformMac, TPlatformAndroid.

用接口转到这个类来处理,与直接用这类来处理有什么区别?

在调用服务接口类完成相应任务时,被调用的服务是一个公用的接口,与平台无关。所以程序中的所有对象都可以调用接口来实现相关服务任务。而不用考虑这些服务是怎样实现的。调用者只需提交指令。
然后服务接口会根据指令。并根据指定的平台,交由平台服务类去进行具体服务任务的完成。

当然,也可以让程序中所有对象直接调用服务平台类来实现任务。但是每个对象在发出任务指令时,都要先进行平台判断与指定。
每发出一次指令都指定一次平台,与通过通一的接口,让接口统一去指定平台。可想而知,哪个更科学,更方便。

当然完成这个接口体系,任务宏大。先要规划十几个服务接口的服务内容。然后再由平台去完成这些服务任务。而每个接口要负责的指令都得先归类规划好。

了解了跨平台,再来细说具体一个程序的运行流程。

程序启动流程分析:

FM的程序在启动时,会在装载单元文件FMX.Platform 时首先执行 RegisterCorePlatformServices 过程。来注册对应的平台服务类对象。

FM是根据宏定义(MSWINDOWS 或 IOS 或 ANDRODIO)来决定 uses 那个平台的 Services 单元。并执行这个单元里的 RegisterCorePlatformServices。如Windows系统,uses的是 MX.Platform.Win 单元

RegisterCorePlatformServices 执行,首先创建的一个服务类对象TPlatformWin,这个类对象有N多个接口(前面所列的各种服务接口),再将这个TPlatformWin注册到各个接口服务中去。如: TPlatformServices.Current.AddPlatformService(IFMXMouseService, PlatformWin); 以便让程序中的对象(组件)调用相应接口时,能转回到平台服务对象来处理。

PlatformWin 在创建的时候,先向全局原子表添加一个字符串,(FM 就是用这个字符串标识来查找自己的 FORM 的)。同时创建一个 Application类对象。来接管系统的消息和负责窗体的创建。

完成服务类 PlatformWin 与应用类 Application 的创建后。程序的初始化基本完成。(当然FM还执行了其他N多配套的初始化动作。如创建一个Screen对象,建立一个管理窗口的堆栈。来负责管理应用程序的所有窗体)。

初始化完成后,开始启动 Applicaion。进入程序的主体运行机制。
3ff0
启动步骤如下:

执行Application的Initialize。只是方便让开发者员加入程序初始化代码。

执行 Application.CreateForm, 创建第一个窗体 From。并指定为MainForm

执行 Application.Run; 进入消息循环。

总结:

程序启动

装入FMX.Platform

执行RegisterCorePlatformServices

创建平台服务类TPlatformWin

注册各种平台服务接口

创建应用类TApplication

Application. Initialize用户初始化

Application.CreateForm创建第一个窗体,并指定为MainForm

MainForm指定 ParneWnd时,创建 Application的影子窗体,

Application的影子窗体创建时,绑定消息处理过程 WndPro

Application.Run 进入消息循环

程序开始运行并处理消息。

TApplication分析

TApplication的Create并没太多的动作。只是创建了一个TIdleMessage和TApplicationFormFactor,这两个的具体作用以后再了解。

Application 的Initialize 也不执行任何动作。

Application 的CreateForm负责程序所有窗体的构建。Application 的 FCreateForms 类 来取得所创建的窗体的指针与Class类型, Instance 信息。并将创建的类保存一个TFormRegistry 堆栈。

Application 的创建窗体,只是创建了窗体的类对象 Form。而真正显示在桌面上我们能看到的窗体,是在 Form 创建时,通过Form 的 CreateHandle 来创建完成。而CreateHandle事件又调用了窗体服务接口WinService 的CreateWindow。这个WinService,其实就是初始化过程中注册了的服务类 PlatformWin。真正执行创建窗体的动作,经过七转八拐最后交给了 PlatformWin 的 CreateWindow。(FM 的很多任务,都是通过这种接口转移来实现的)

PlatformWin的CreateWindow是个复杂的过程。他根据传来的Form类,设定WindowClass的各个参数。这中间还考虑到了窗体在Delphi的IDE环境下的一些设定。同时,他还判定窗体是普通窗体还是Popup型窗体。还会为窗体注册各种服务接口,如Menu,DropDrag等。

Form在创建实体窗口时。会指定他的ParneWnd为 ApplicationHWND; ApplicationHWND会调用 PlatformWin的CreateAppHandle来创建一个FMAppClass。这时,Application的真正的实体隐形窗口FMAppClass Window 就出生了。

FM的消息循环,正是绑定FMAppClass窗体的句柄以及消息过程 WndPro 来实现的。

Applicaion 会在创建第一个窗体时,将其指定为 MainForm。用户的交互操作,都是在 MainForm 上实现的。

Application的消息循环:

Application执行Run. 进入消息循环

Run 会调用 App服务接口 AppService。当 然AppService又指向了PlatformWin. 执行PlatformWin 的 Run.

PlatformWin.Run 中会执行 Application 的HandleMessage。当然又是通过AppService 回到执行 PlatformWin 的 HandleMessage。

PlatformWin 的HandleMessage 才开始正式的消息循环。在这里,我们能看到熟悉的 PeekMessage, TranslateMessage 和 DispatchMessage。

HandleMessage 通过消息处理过程 WndPro. 来过滤和分发所有消息

当得到 WM_QUIT或 FMAppClass 的 WM_CLOSE 时,退出消息循环,终止程序。

总结:

Application创建 (没太多动作)

Application初始化 (没动作)

Application CreateForm 创建主窗体Form类

Form类执行 CreateHandle 创建窗口实体,将指定ParneWnd

Form指定 ParneWnd时,创建 Application的影子窗体,

创建Application的影子窗体,并绑定消息过程 WndPro

Application执行Run. 进入消息循环

Run执行 PlatformWin的HandleMessage

HandleMessage执行WndPro 过滤与分发消息。

程序进入正常的消息过程。

处理消息,实现交互操作。

得到 WM_QUIT或 FMAppClass 的 WM_CLOSE 时,退出消息循环,终止程序。

消息分发机制分析:

Screen对象会把Ap
3ff0
plication CreatForm产生的Form全加入一个队列。

在消息循环中,根据消息句柄 hWnd 在Screen对象中找对应的Form。并将消息派发给Form。如果Form=nil,执行 Application的消息检测。侦测WM_CLOSEWM_DESTROY等消息,判断是否退出程序。

进行窗口消息过虑,响应 WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSEMOVE,WM_MOUSEWHEEL 等鼠标消息WM_CHAR, WM_KEYDOWN,WM_KEYUP等按键消息。

对不同的消息作不同的处理,主要是鼠标与按键类的消息分发。执行相应的 MouseDown, MouseUp, KeyDown, KeyUp 事件。

Form的鼠标消息处理:

Form有三个私有对象 FCaptured, FFocused,FHovered。

FCaptured:记录按下时的鼠标移所在位置的对象。
FHovered:记录正常(未按下时)的鼠标移所在位置的对象。
FFocused:记录键盘焦点对象。

在消息体制内,From 通过ObjectAtPoint来确定消息对象Obj。并将对应的消息分发给Obj. 如MouseUp,MouseDown,MouseMove等,先取得鼠标位置的Obj.然后根据情况是否派发消息给Obj。

MouseUp,MouseDown 事件会先侦测 Obj的Drag (拖曳事件) 执行BeginAutoDrag或终止 Drag。在没有Drag时,直接将消息分发给 Obj。这时FCaptured 对象在处理Drag起到了关键作用。

Form在处理MouseMove时,会更新 Hovered。并触发原 Hovered的MouseLeave和新的 Hovered(即当前Obj)MouseEnter。这时再将MouseMove消息再派发给Obj。

CursorService 来负责鼠标形状的改变。

按键消息 KeyDown,KeyUp 是通过 Focused 派发。

Captured是在鼠标按下时取得的对象。全程将鼠标消息派发给 Captured, 以确保按下后鼠标拖动时与MouseUp, MouseLeave消息处理。

总结:

Run执行循环,并通过WndPro过滤,并分发消息

根据hWnd查找Form,将消息派发给Form

处理Form的消息,过滤消息,

处理FORM自身的消息,并响应相关事件。

派发相关消息 如鼠标,键盘等给Form里的Object

通过Captured,Hovered,Focused 三个对象来细化消息处理

让Object响应,并执行相应的事件。实现交互操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: