我对学习delphiTeacher的《delphi调用及封装Android原生控件》的学习和研究点滴体会之一

我对学习delphiTeacher的《delphi调用及封装Android原生控件》的

学习和研究点滴体会之一

        3月21日晚,和大伙一起学习了delphiTeacher王老师的《delphi调用及封装Android原生控件》,受益匪浅,过去一直没有弄明白到底delphi是如何与Android进行底层通讯的机制,现在算是顺着老师指点的迷津,摸到正确的方向和路径了,没有去看去打开王老师发的演示代码,而是课后跟着王老师视频从头到尾实作了一遍,老师的实操非常完美,分享出来,供同事们共同学习和分享:

        摘要:

  • 关于Android安卓及其相关开发的概念点滴   
  • delphi调用Android的模式
  • 关于Androidapi.AppGlue.pas
  • Android原生Activity与FMX通讯
  • delphi调用及封装Android原生控件
  • 建议你加入王老师

一、关于Android安卓及其相关开发的概念点滴

1、Linux内核

        Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。 [1] 

        Linux最早是由芬兰 Linus Torvalds为尝试在英特尔x86架构上提供自由的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有一些Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。

2、Android安卓是基于Linux内核的可移植的操作系统

        系统的基本三大层次::

        2.1、内核层:Linux,纯C语言开发

        2.2、硬件驱动层:C语言和部分汇编开发

        2.3、应用程序层:含原生的Native应用即NDK(C语言开发)和Google为了方便第三方在其上开发应用而用java和C语言混合编写的SDK发布出来。

3、NDK

        NDK 即Native Development Kit,被Google称为“NDK”。
        众所周知,Android程序运行在Dalvik虚拟机中,NDK允许用户使用类似C / C++之类的原生代码语言执行部分程序。
        NDK包括了:
        从C / C++生成原生代码库所需要的工具和build files。
        将一致的原生库嵌入可以在Android设备上部署的应用程序包文件(application packages files ,即.apk文件)中。
        支持所有未来Android平台的一系列原生系统头文件和库。
        为何要用到NDK?
        概括来说主要分为以下几种情况:
        1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库被反编译的难度较大。
        2. 在NDK中调用第三方C/C++库,因为大部分的开源库都是用C/C++代码编写的。
        3. 便于移植,用C/C++写的库可以方便在其他的嵌入式平台上再次使用。

4、Java及JNI

        java是一种开发语言,就像C和delphi的pascal语言类似的概念。但它不像C(C++)和delphi为真编译的语言,它是伪编译的,需要JVM(java虚拟机)在后台为代码做解释,简单地说,其伪编译的*.class代码是需要JVM处于运行状态并在其上执行的。

      所以,不要因为JNI是java实现的,就误解了认为 java = Android安卓,JNI本质是为了实现:Android操作系统上的Java语言实现的那部分代码和C语言实现的绝大多数代码,两者之间的互访机制:

        Delphi在其框架内,翻译了所有Android的API即AndroidAPI.*,其中包含对JNI的翻译AndroidAPI.JNI.*:将上图Java语言部分,用pasal语言来实现,从而便于与Android上的原生C语言实现代码间的交互,同时也借此实现了Android上与java写的第三方sdk的通讯。

二、delphi调用Android的模式

生命周期:

1、模式一

        delphi调用Android第三方SDK  \LARGE \rightarrow  进入AndroidSDK开发(SDK:Software Development Kit安卓平台上第三方开发语言的软件开发包 )【  \LARGE \rightarrow  Android第三方SDK若为Java写的  \LARGE \rightarrow  Android平台的JVM(Java虚拟机负责进行解释) 】\LARGE \rightarrow  Android第三方SDK的功能开始起作用  \LARGE \rightarrow  调用Android JNI(JNI:Java Native Interface即Java原生的对Android平台的接口中间件:安卓内部的实现实际上是由C和Java两种语言混合编程分别完成各自的功能(其中Java语言实现的google发布出来的sdk),JNI提供了对这两种语言的函数等对象的互访机制)  \LARGE \rightarrow  调用AndroidAPI从此开始回调delphi  \LARGE \rightarrow  delphi进入Androidapi.AppGlue框架(是一个可访问安卓被链接的Java类和安卓原生Native这两端对象的分布式平台框架中间件  \LARGE \rightarrow  delphi进入AndroidNDK开发(NDK:Native Development Kit安卓平台的原生开发包 ) \LARGE \rightarrow  delphi释放AppGlue框架的TAndroidApplicationGlue的实例

2、模式二

        delphi直接调用Androidapi.AppGlue框架(是一个可访问安卓被链接的Java类和安卓原生Native两端功能的分布式平台框架中间件从此开始回调delphi (即delphi直接访问Android安卓操作系统)  \LARGE \rightarrow  delphi进入AndroidNDK开发(NDK:Native Development Kit安卓平台的原生开发包 ) \LARGE \rightarrow  delphi释放AppGlue框架的TAndroidApplicationGlue的实例

        就和C或C++直接访问Android操作系统,类似的模式。

三、关于Androidapi.AppGlue.pas  

  FMX.Platform.Android,
    //:引用了FMX调用Android原生对象的入口单元
      //:Androidapi.AppGlue,

Please note that this unit exports "ANativeActivity_onCreate" method, which will be executed automatically
  when Android Native Activity starts. During initial execution of this code, none of RTL or "System.pas" 
  functions are available, which includes but not limited to exception handling, locale-related compare and
  threading. Therefore, such code may only use basic language constructs and rely exclusively on "libc"
  functions provided in Bionic. This may include some Androidapi.* and Posix.* units, but special care is 
  to be taken to make sure that the required functionality is properly implemented.

  请注意:这个单元导出了“ANativeActivity_onCreate”(我加的:1个Android原生窗体的产生事件:它传入了被产生窗体的记录指针)方法当安卓原生窗体开始运行时,它将被自动执行。(我加的理解不知是否正确:在初始执行这段代码的过程中,Delphi全局的RTL运行时刻库或System.pas系统基础单元就已经开始内部调用引用它们进行Android原生窗体的初始化:包括但不限于异常处理、相关语言环境代码页及其比较、线程的处理,这个时候会立即执行BindAppGlueEvents它先触发PANativeActivity(System.DelphiActivity)^.instance的实例化在这个过程中:为了确保原子自动引用计数ARC的正常运行和保证线程安全,你不能在此时去进行外部调用RTL和System.pas,必须在原生窗体被OnCreate实例化之后:相当于就好比delphi工程正在被Application.Initialize和Application.CreateForm(TMainForm, MainForm),而Application.Run还没有跑起来之前,你不能去外部调用RTL及System.pas中复杂类型的ARC而且不能用任务或多线程异步并行产生多个Android原生窗体的实例)。因此,这些代码可能只使用基本的语言结构和完全依赖底层C所提供的(我加的)仿真“libc”函数库( 我加的:即C写的动态库):可能包括一些AndroidAPI.*和Posix.*单元特别注意确保正确地实现所需的功能
If you make any changes/additions to the code herein, it must be reviewed by the senior team members prior to
  committing to the repository. Be prepared to fully defend and explain the changes, why they are necessary and
  what problem it is purporting to solve. Any violation of the parameters by which changes may be made to this
  unit will result in the outright rejection of the proposed changes. Proceed with extreme caution.

  如果你对此做任何修改或在其之上添加代码,必须提交到代码库并由高级团队成员审核。充分准备辩护和解释这些改写为何是必要的,声明是为了解决什么问题。任何对此单元所做的违法参数的改写均可被视为超出版权而被拒绝。请谨慎。

uses
  System.Types,
  Posix.SysTypes, Posix.StdLib, Posix.DlfCn,
        //:  引用了Android底层Posix LINUX操作系统的系统类型、标准库单元、和基于C++语言的.h头文件入口描述
  Androidapi.Configuration, Androidapi.Looper,
  Androidapi.Input, Androidapi.Rect, Androidapi.NativeWindow, Androidapi.NativeActivity;
        //:  引用了最基本的AndroidAPI

type

  TAndroidApplicationGlue = class;
        //:  Android的原生应用程序类,就好比delphi的Application对象

  TAndroidApplicationCommand = (Start, Resume, Stop, Pause, InputChanged, InitWindow, TermWindow, WindowResized,
    WindowRedrawNeeded, GainedFocus, LostFocus, ConfigChanged, SaveState, LowMemory, Destroy);
  TAndroidApplicationCommands = set of TAndroidApplicationCommand;
        //:  Android应用程序主线程的命令集合及其枚举(启动、挂起、停止、暂停、输入被改变了、初始化窗体、进入命令行终端工具、窗体改变了大小、窗体需要重画、对象获取焦点、对象失去焦点、配置被改变、保存状态、系统低内存、释放),它在这里与delphi FMX进行通讯:FMX.Platform.Android :  procedure TPlatformAndroid.HandleApplicationCommandEvent(const AAppGlue: TAndroidApplicationGlue; const ACommand: TAndroidApplicationCommand);

  TOnApplicationCommand = procedure(const App: TAndroidApplicationGlue; const ACommand: TAndroidApplicationCommand) of object;
        //:  发出Android应用程序命令的匿名过程方法
  TOnInputEvent = function(const App: TAndroidApplicationGlue; const AEvent: PAInputEvent): Int32 of object;
        //:  Android应用程序触发事件生效的的匿名函数方法
  TOnContentRectChanged = procedure(const App: TAndroidApplicationGlue; const ARect: TRect) of object;
        //:  Android应用程序中触发屏幕上某个矩形区域内容改变生效的匿名过程方法

{ TAndroidApplicationGlue }

protected
procedure DoApplicationCommandChanged(const ACommand: TAndroidApplicationCommand); virtual;
        //:用设定的Android应用程序命令TAndroidApplicationCommand来请求触发事件
procedure DoContentRectChanged(const ARect: TRect); virtual;
        //:用新设定的屏幕内容矩形区域来请求触发内容改变事件

procedure AllocatedSavedState(ASavedState: Pointer; ASavedStateSize: size_t);
        //:  delphi应用程序调用Android应用程序,分配内存地址及空间

procedure FreeSavedState;
        //:  delphi应用程序调用Android应用程序,释放已分配的内存
    /// <summary>Pointer to the callback function table of the native application. You can set the functions here
    /// to your own callbacks. The callbacks pointer itself here should not be changed; it is allocated and managed
    /// for you by the framework.指向Android原生应用程序的回调函数表的指针记录。你可用它来继承并发布到你自己的回调类(我加的:这个回调类一般是由接口对象来实现的:Java的JObject或C++的*Object)。回调指针记录不应被改变,它由框架自动为你分配并管理内存</summary>
property Callbacks: PANativeActivityCallbacks read GetActivityCallbacks;
 

(*   指向Android原生应用程序的回调函数表的指针记录:

uses Androidapi.NativeActivity ;
{ These are the callbacks the framework makes into a native application.Android原生应用程序中内置的回调框架。
  All of these callbacks happen on the main thread of the application.所有回调发生在Android原生应用程序的主线程。
  By default, all callbacks are NULL; set to a pointer to your own function
  to have it called.所有回调默认nil,赋值到1个你所调用的函数的指针。
 }
  ANativeActivityCallbacks = record
    onStart: TOnStartCallback;
    onResume: TOnResumeCallback;  //:delphi的应用程序将会进行前台:TApplicationEvent.WillBecomeForeground
    onSaveInstanceState : TOnSaveInstanceStateCallback;
    onPause : TOnPauseCallback;
    onStop : TOnStopCallback;
    onDestroy : TOnDestroyCallback;
    onWindowFocusChanged : TOnWindowFocusChangedCallback;
    onNativeWindowCreated : TOnNativeWindowCreatedCallback;
    onNativeWindowResized : TOnNativeWindowResizedCallback;
    onNativeWindowRedrawNeeded : TOnNativeWindowRedrawNeededCallback;
    onNativeWindowDestroyed : TOnNativeWindowDestroyedCallback;
    onInputQueueCreated : TOnInputQueueCreatedCallback;
    onInputQueueDestroyed : TOnInputQueueDestroyedCallback;
    onContentRectChanged : TOnContentRectChangedCallback;
    onConfigurationChanged : TOnConfigurationChangedCallback;
    onLowMemory : TOnLowMemoryCallback;
  end;    

*)


public

constructor TAndroidApplicationGlue.Create(const AActivity: PANativeActivity; savedState: Pointer; savedStateSize: size_t);
        {//:  delphi应用程序调用Android应用程序,Android应用程序构造函数(产生实例):继承、产生1个Android原生窗体、绑定对该窗体的delphi事件回调(即将delphi的事件指针的值赋值给Android原生事件记录的指针(如上ANativeActivityCallbacks,具体是FMX.Platform.Android在内部处理三大类事件的赋值[窗体命令事件、内容事件、输入队列事件]procedure TPlatformAndroid.BindAppGlueEvents;,详看其代码来理解)、创建线程循环管理器、初始化输入事件记录指针nil、初始化窗体记录指针nil、初始化1个没有值的新的配置记录指针、初始化配置管理器记录指针,并为窗体分配内存空间 }

//TAndroidApplicationGlue.Create在这个过程中,delphi还做了一件伟大的事情:   Delphi: init system unit and RTL.  在这个时候,delphi就已经自动实例化了其运行时刻库RTL和初始化system.pas单元并让其开始生效!
 
procedure SystemEntry;  //:delphi进入Android操作系统的入口方法
  type
    TMainFunction = procedure;  //:定义delphi进入Android操作系统的主程序类型
  var
    DlsymPointer: Pointer;
    EntryPoint: TMainFunction;     //:定义delphi进入Android操作系统的主程序类型的入口点变量
    Lib: NativeUInt;
    Info: dl_info;
  begin
    if dladdr(NativeUInt(@app_dummy), Info) <> 0 then //:如果后台的Linux或Mac OSX(即Unix)操作系统的app仿真信息动态库的地址不为0

    begin 
      Lib := dlopen(Info.dli_fname, RTLD_LAZY); //:则dlopen打开该外部动态库文件准备读写内存,返回其句柄
      if Lib <> 0 then  //:若外部动态库文件被成功打开
      begin
        DlsymPointer := dlsym(Lib, '_NativeMain');  //:delphi进入Android获取底层POSIX操作系统标记为_NativeMain的主程序的动态库dlsym的内存指针
        dlclose(Lib);  //:delphi关闭已打开的外部动态库文件的句柄
        if DlsymPointer <> nil then  //:如果读取到的内存指针不为空
        begin
          EntryPoint := TMainFunction(DlsymPointer);  //:捕获Android操作系统的主程序的入口点的实例,并将其赋值给变量
          EntryPoint;  //:执行Android操作系统的主程序的实例
        end;
      end;
    end;

  end;

begin
{According diagram of Android lifecycle activity can change state from OnStop to OnCreate. In this case, we will
need to avoid of repeated creation of system resources. Because we release our resource
in OnDestroy (last state of Activity).根据Android生命周期活动图,Android应用程序窗体可以从OnStop到OnCreate改变状态。在这种情况下,我们将需要避免的重复创建系统资源TAndroidApplicationGlue.Create。因为我们在OnDestroy事件中释放资源(:即释放最近1次的窗体内存状态地址)。即是说:OnDestroy才是在释放窗体,而OnStop只是停止该窗体内的命令事件的活动、此时它仍处于已产生的状态Created
}
  if System.DelphiActivity = nil then  //:如果delphi进程并未设置主窗体
  begin
    // Delphi: save Android activity for future use.  //:delphi保存Android的主窗体供外来调用!
    System.DelphiActivity := activity;             //:就将Android应用程序窗体作为未来delphi进程的主窗体
    System.JavaMachine := activity^.vm;      //:系统的java虚拟机JVM的指针就是将Android应用程序进程运行所需的虚拟机
    System.JavaContext := activity^.clazz;    //:系统的java上下文环境的指针就是将Android应用程序进程运行的上下文环境指针

    FApplication := TAndroidApplicationGlue.Create(activity, savedState, savedStateSize); 

         //:Android应用程序用上述窗体及内存等参数进行实例化:相当于delphi工程的  Application.Initialize;

    activity^.instance := Pointer(FApplication);  

         //:产生Android应用程序主窗体的实例:相当于delphi工程的  Application.CreateForm(TMainForm, MainForm);
    SystemEntry;  //:delphi进入Android操作系统内部的入口点相当于delphi工程的  Application.Run;
  end;
end;

}
destructor TAndroidApplicationGlue.Destroy;
        //:  delphi应用程序调用Android应用程序,Android应用程序解构函数:取消绑定回调、释放已分配的内存空间、若有事件记录指针则取消输入事件记录指针、删除配置记录指针,并继承
class property Current: TAndroidApplicationGlue read FApplication;
        //:  delphi应用程序调用Android应用程序,用于获取当前Android应用程序的实例的类属性
property NativeActivity: PANativeActivity read FActivity;
        //:  delphi应用程序调用Android应用程序,用于获取当前正在运行的Android应用程序的原生窗体的实例的属性
property Config: PAConfiguration read FConfig;
        //:  delphi应用程序调用Android应用程序,用于获取当前用以正在运行的Android应用程序的配置的属性
property InputQueue: PAInputQueue write SetInputQueue;
        //:  delphi应用程序调用Android应用程序,当创建了输入事件记录指针后,用于Android应用程序响应用户输入事件的线程队列的赋值属性
property Looper: PALooper read FLooper;
        //:  delphi应用程序调用Android应用程序,读取与Android应用程序线程相关的循环管理器助手
property SavedState: Pointer read FSavedState write FSavedState;
    /// <summary>This is the last instance's saved state, as provided at creation time. It is NULL if there was no state.
    /// You can use this as you need; These variables should only be changed when processing
    /// a TAndroidApplicationCommand.SaveState, at which point they will be initialized to NULL and you can malloc your
    /// state and place the information here.  In that case the memory will be freed for you later.</summary>
  {这是最后一个实例保存的内存状态地址,它在创建时赋值。如果没有内存状态地址则为零。
  你可以根据需要使用它;当处理TAndroidApplicationCommand.SaveState时,这些变量才能被重新赋值。
  此时他们会先初始化为零,你可以重新分配内存地址并在此处理信息。在此情况下,将为你稍后释放内存做好准备。}
        //:  delphi应用程序调用Android应用程序,读写Android应用程序上一个实例的内存状态地址。
property SavedStateSize: size_t read FSavedStateSize write FSavedStateSize;
        //:  delphi应用程序调用Android应用程序,读写Android应用程序上一个实例的内存地址的空间大小。
property Window: PANativeWindow read FWindow;
        //:  delphi应用程序调用Android应用程序,若已产生或初始化Android应用程序的原生窗体,这个属性用于读取可在其上进行绘制的窗体的记录指针(具体可以读取窗体的可绘制的BitMapManage的表面)
    { Events }
property OnApplicationCommandEvent: TOnApplicationCommand read FOnApplicationCommandEvent write FOnApplicationCommandEvent;
        //:  delphi应用程序调用Android应用程序,用Android应用程序命令TAndroidApplicationCommand对其所要触发的事件进行赋值的可读可写的匿名方法
property OnContentRectEvent: TOnContentRectChanged read FOnContentRectEvent write FOnContentRectEvent;
        //:  delphi应用程序调用Android应用程序,Android应用程序所要触发的屏幕矩形区域的内容发生改变的事件的可读可写的匿名方法
property OnInputEvent: TOnInputEvent read FOnInputEvent write FOnInputEvent;
    /// <summary>Fill this in with the function to process input events. At this point the event has already been
    /// pre-dispatched, and it will be finished upon return. Return if you have handled the event, 0 for any default
    /// dispatching.在这个输入点等待你所赋值并指派的事件的返回并结束,若未赋值,默认程序类型的句柄=0</summary>
        //:  delphi应用程序调用Android应用程序,Android应用程序已预先触发的用户输入事件的可读可写的匿名方法

const
  /// <summary>Looper data ID of events coming from the AInputQueue of the application's window, which is returned as
  /// an identifier from ALooper_pollOnce().来自Android应用程序窗体的用户输入事件,从线程池首次返回的标记常量=2
  LOOPER_ID_INPUT = 2;

  /// <summary>Start of user-defined ALooper identifiers.用户自定义的Android应用程序主线程的启动标记常量=3</summary>
  LOOPER_ID_USER = 3;

/// <summary> Dummy function you can call to ensure glue code isn't stripped.用于确保本AppGlue分布式平台框架中间件没有被移除(即生效)的仿真过程(”应用程序傀儡“、”应用程序仿真“)</summary>
procedure app_dummy

四、Android原生Activity与FMX通讯

 上面消息订阅与处理这一步,有朋友在听课过程中粘了这段代码通过平台服务TPlatformServices,我们也经常用到类似接口调用的方式,但其实还是与1、TApplicationEventMessage的实现是有区别的:详见:https://blog.csdn.net/pulledup/article/details/105065671

搜并参考FMX自己是怎样做原生Abdroid下TMediaControl封装的:class(TMedia    \LARGE \rightarrow 

\LARGE \rightarrow   FMX.Media.Android.TAndroidVideo

【如何将原生控件在UI上显示出来:ZOrderManager:DX10.3新功能:】

\LARGE \rightarrow   TAndroidVideo.ZOrderManager: TAndroidZOrderManager read GetZOrderManager; 

  /// <summary>:用于管理平台原生控件的帮助类Helper class used to manage platform controls</summary>

\LARGE \rightarrow  FMX控件(TControl实际通过控件所在的TCommonCustomForm,若为TFrame需接口调用)获取平台原生控件管理器:

    FMX.Platform.Android.WindowHandleToPlatform(const AHandle: TWindowHandle).ZOrderManager;

    FMX.Platform.Android.WindowHandleToPlatform(const AHandle: TWindowHandle): TAndroidWindowHandle;

            FMX.Platform.UI.Android.TAndroidWindowHandle.Create(const AForm: TCommonCustomForm);

            FMX.Platform.UI.Android.TAndroidWindowHandle.Show;

            FMX.Platform.UI.Android.TAndroidWindowHandle.Hide;  

            FMX.Platform.UI.Android.TAndroidWindowHandle.Form;  

            FMX.Platform.UI.Android.TAndroidWindowHandle.FormLayout: JViewGroup read FFormLayout;//:窗体中的原生子控件  //JViewGroup\LARGE \rightarrowJView = interface(JObject)\LARGE \rightarrowJObject:Androidapi.JNI.JavaTypes.JObject = interface(IJavaInstance)\LARGE \rightarrowIJavaInstance:  Androidapi.JNIBridge.IJavaInstance = interface(IJava):描述java实例方法的根接口

4.5.1、描述Java类方法的所有接口必须从IJavaClass这个接口来获取:
所有Java对象,无论是本地对象local还是导入对象imports,都必须使用IJavaClass接口
作为“根”接口'root' interface。
本地对象是Delphi实现的对象,它们实现了一些java协议或接口。
导入对象是java的对象,它们由java运行时创建,java运行时
已被封装在动态创建的便于从Delphi进行访问的接口中。
4.5.2、而描述Java实例方法的接口必须源自IJavaInstance。

            FMX.Platform.UI.Android.TAndroidWindowHandle.View: JFormView read FView write FView;

            FMX.Platform.UI.Android.TAndroidWindowHandle.ZOrderManager: TAndroidZOrderManager read GetZOrderManager;

            FMX.Platform.UI.Android.TAndroidWindowHandle.Bounds: TRectF read GetBounds write SetBounds;//:窗体尺寸大小

            FMX.Platform.UI.Android.TAndroidWindowHandle. Owner: TWindowHandle read FOwner;

           //...............

\LARGE \rightarrow   ZOrderManager.AddOrSetLink(const AControl: TControl; const AView, AChildrenView: T);

    /// <summary>:添加或设置FMX控件及Android原生控件JView(含子控件)的1个新的链接到平台控件管理器:即用平台控件管理器将两者绑定产生关联:Adds or set new link of TControl and T to manager.</summary>

\LARGE \rightarrow   ZOrderManager.UpdateOrderAndBounds(const AControl: TControl);

   /// <summary>:调用FMX控件(其其所在窗体)在原生窗体上的填充顺序和填充坐标(即层叠式样式表CSS的层次和位置坐标),并通过上述FMX控件获取平台原生控件管理器的Bounds属性自动获取了原生控件的尺寸大小Calls of FixOrder and FixBounds.使用控件的坐标改变平台原生控件的坐标。而UpdateBounds(const AControl: TControl):Updates platform view coordinates using control coordinates.</summary>

五、delphi调用及封装Android原生控件

  

1、注册控件


procedure Register;
  //:delphi唯一首字母必须大写的保留字,为了注册组件

implementation

procedure Register;
begin
  RegisterComponents('PulledupUI',[TTencentSuperPlayer]);
    //:第1个参数注册到组件面板的哪个页面
    //:数组:放哪些做好的控件类
end;
 

2、安装控件

选你的DX工具适配的版本下(如win32):Install

重启DX后,面板显示组件:

3、设计时添加控件:

  [ComponentPlatformsAttribute(pidAllPlatforms)]

    //:在对象池中为对象添加前导属性[ ]:(此处为:为控件声明平台可用性,否则只能在pidWin32(默认)下拖拽到设计时UI):在其实例化之前定义或要执行的代码 //:uses system.classes;
  TTencentSuperPlayer =class(TControl)

(* //其中,pidAllPlatforms:

type
  TPlatformIds = UInt32;
const

  { Platform identifiers }
  pidWin32          = $00000001;
  pidWin64          = $00000002;
  pidOSX32          = $00000004;
  pidiOSSimulator32 = $00000008;
  pidiOSSimulator   = pidiOSSimulator32 deprecated 'Use pidiOSSimulator32';
  pidAndroid32Arm   = $00000010;
  pidAndroid        = pidAndroid32Arm deprecated 'Use pidAndroid32Arm';
  pidLinux32        = $00000020;
  pidiOSDevice32    = $00000040;
  pidiOSDevice      = pidiOSDevice32 deprecated 'Use pidiOSDevice32';
  pidLinux64        = $00000080;

  pidWinNX32        = $00000100;
  pidWinIoT32       = $00000200; // Embedded IoT (Internet of Things) Windows w/ Intel Galileo
  pidiOSDevice64    = $00000400;
  pidWinARM32       = $00000800;
  pidOSX64          = $00001000;
  pidLinux32Arm     = $00002000;
  pidLinux64Arm     = $00004000;
  pidAndroid64Arm   = $00008000;

  pidiOSSimulator64 = $00010000;

  pidAllPlatforms = pidWin32 or pidWin64 or
                    pidOSX32 or pidOSX64 or
                    pidiOSDevice32 or pidiOSDevice64 or
                    pidiOSSimulator32 or pidiOSSimulator64 or
                    pidAndroid32Arm or
                    pidLinux64;

  { Platform family identifiers }
  pfidWindows     = pidWin32 or pidWin64;
  pfidOSX         = pidOSX32 or pidOSX64;
  pfidiOS         = pidiOSDevice32 or pidiOSDevice64 or
                    pidiOSSimulator32 or pidiOSSimulator64;
  pfidAndroid     = pidAndroid32Arm;
  pfidLinux       = pidLinux64;

*)

procedure TTencentSuperPlayer.Paint;
begin //:Paint后对[ComponentPlatformsAttribute(pidAllPlatforms)]全平生效:
  inherited;
  //设计时绘制控件://:绘制控件,delphi VCL和FMX,可视化控件(可视化对象)的外观都是画出来的,用画布CanvasPaint出来:
  if (csDesigning in ComponentState) then
  begin
    //绘制控件背景颜色:
    Canvas.Fill.Kind:=TBrushKind.Solid;
    Canvas.Fill.Color:=TAlphaColorRec.Slateblue;
    Canvas.FillRect(RectF(0,0,Width,Height),0,0,[],1);

    //Canvas.Stroke.Kind:=TBrushKind.None;
    //绘制控件名称:
    Canvas.Fill.Color:=TAlphaColorRec.White;
    Canvas.FillText(
      RectF(0,0,Width,Height),
      Name, //:名称
      true,
      1,
      [],
      TTextAlign.Center,
      TTextAlign.Center
    );
  end;

end;

 六、结束:

    王老师讲的非常的好,向你推荐购买他的产品和服务delphi OrangeUI控件包和OrangeSDK第三方控件封装包,不仅可以用上好的工具提升你的delphi APP开发加速和品质,还可以学习到delphi FMX下的深层次的直播和内容。

http://www.orangeui.cn/product.php

http://www.orangeui.cn/downloadsdk.php

http://www.orangeui.cn/download.php

发布了80 篇原创文章 · 获赞 9 · 访问量 9861

猜你喜欢

转载自blog.csdn.net/pulledup/article/details/105040901