APP 开发之使用 Frame 做界面如何简化代码

【Delphi Frame 使用示范】 帮助Delphi初学者学会使用Frame编程 阅读详情
AI摘要

本文介绍用Delphi的FireMonkey框架开发APP时,使用TFrame做界面以节省资源。针对多个相同风格界面设计代码重复问题,通过将共性抽象到基类TFrameBase,让实际界面继承该基类,从而减少代码编写和界面设计操作,实现界面逐层堆叠、逐级返回的效果。

缘起

用 Delphi 做 APP,使用 Delphi 的 FireMonkey 框架。假设我们模拟微信的界面做一个 APP 的界面框架,大概是这个样子的:

1. APP 启动后,显示主界面。主界面底下有四个按钮;

2. 用户点击底下的四个按钮的某一个,整个屏幕显示下一层的界面。

3. 这个下一层的界面可能有多个,但每个界面顶部都是一个工具栏,工具栏的左侧是一个返回按钮,工具栏中间显示一行文字,提示用户当前 APP 在哪个功能界面。

4. 用户点击返回按钮,该界面消失,回到主界面。

一般的做法

在 Windows 年代,我一般会为每个界面,做一个 TForm 的设计。但是在做 APP 的时候,Delphi 官方的意思最好用 TFrame 而不是用 TForm。因为 TForm 占用更多的资源,对于手机这种硬件来说,节省点资源总是更好的。

因此,比如有4个主界面下一级的界面,我就需要设计4个 Frame,比如 TFrame1, TFrame2, TFrame3, TFrame4。

然后,主界面的4个按钮的 OnClick 事件里面,我们分别写代码:

AFrame1 := TFrame1.Create(Self);
AFrame1.Parent := Self;
AFrame1.Align := TAlignLayout.Client;
AFrame1.Visible := True;
AFrame1.BringToFront;

上面的代码,分别换成 TFrame2, TFrame3, TFrame4 重复四次。

还有,UI 设计

另外,这四个 Frame 的 UI 的整体风格应该是一致的。比如顶上那个工具栏和返回按钮,应该是一样的外观(颜色,字体,按钮形状,等等);

如果每个 Frame 都设计一遍,就算是 Delphi 可以在 IDE 里面拖拉控件搞定,在属性面板里面选择颜色之类的就能设计好,但重复4次也挺烦。如果有更多的相同风格的界面需要设计,就更麻烦。

如何少写代码少干活?

问题的解决

在 Delphi 这种面向对象的语言中,重复的事情,采用继承。我们把所有界面的共性抽象出来,做到一个基类里面,然后其它的真正需要用的界面,都从基类继承。

所以,我们首先设计好一个 Frame 作为基类。通过拖拉控件,我们设计好这个 Frame,命名为 TFrameBase。看图:

我们给这个 TFrameBase 一些所有它的子类都要用到的代码:

unit UFrameBase;
{
  Frame 的基类。子类除了继承它的界面设计,也继承关闭的方法。
  关闭方法不能直接写在按钮的 OnClick 事件里面,因为这样写,代码的方法指针和事件的绑定是在资源文件里面,
  资源文件的事件绑定不会被继承的。所有,这里需要在 Create 的地方,用代码把关闭方法和按钮的 OnClick 绑定,
  代码是可以继承的。这样就不用每个 Frame 都去写关闭代码了。
}
interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  System.Skia, FMX.Skia, FMX.Layouts, FMX.Controls.Presentation, FMX.Objects;

type
  TFrameBase = class(TFrame)
    rctContent: TRectangle;
    rctHeader: TRectangle;
    lblTitle: TSkLabel;
    btnBack: TSpeedButton;
    svgBackArrow: TSkSvg;
    rctTip: TRectangle;
    svgTipIcon: TSkSvg;
    lytTipContent: TLayout;
    lblTipDescription: TSkLabel;
    sbxContent: TScrollBox;
    rctGestureBar: TRectangle;
    rctNavigationBar: TRectangle;
    rctStatusBar: TRectangle;
    rctBackgroundChess: TRectangle;
    pbxBackgroundChess: TSkPaintBox;
  private
    { Private declarations }
    FOnCloseMe: TNotifyEvent;

    procedure DoOnCloseMe;
  public
    { Public declarations }
    procedure CloseMe(Sender: TObject); virtual;
    constructor Create(AOwner: TComponent); override;

    property OnCloseMe: TNotifyEvent read FOnCloseMe write FOnCloseMe;

    class procedure ShowMyFrame<T: TFrameBase>(AParent: TFmxObject; ACloseEvent: TNotifyEvent);
  end;

implementation

uses System.Threading;

{$R *.fmx}

procedure TFrameBase.CloseMe(Sender: TObject);
begin
  Self.DoOnCloseMe;
  Self.Hide;

  TTask.Run(
  procedure
  begin
    Sleep(20);
    TThread.queue(nil,
    procedure
    begin
      Self.Free;
    end)
  end
  );
end;

constructor TFrameBase.Create(AOwner: TComponent);
begin
  inherited;
  btnBack.OnClick := CloseMe;
end;

procedure TFrameBase.DoOnCloseMe;
begin
  if Assigned(Self.FOnCloseMe) then Self.FOnCloseMe(Self);
end;

class procedure TFrameBase.ShowMyFrame<T>(AParent: TFmxObject; ACloseEvent: TNotifyEvent);
var
  AFrame: TFrameBase;
begin
  AFrame := T.Create(AParent);
  AFrame.Parent := AParent;
  AFrame.Align := TAlignLayout.Contents;
  AFrame.BringToFront;
  AFrame.FOnCloseMe := ACloseEvent;
end;

end.

上述代码的解释:

1. FOnCloseMe 是一个事件,当这个 Frame 被关闭的时候,通过这个事件通知需要知道的地方。

2. 类方法 TFrameBase.ShowMyFrame 采用了泛型,因此在创建它的子类的时候调用这个方法,只要输入子类的类型,比如 TFrame2 则它创建的就是 TFrame2 而不是 TFrameBase;这里把创建以及创建后要做的相同代码都集中到这里,也就不需要在每个需要呈现界面的地方都重复写这样的创建代码了。

3. 退出界面的方法 CloseMe,这里需要解释一下。这个方法绑定到界面上的返回按钮的 OnClick 事件,当我们点击返回按钮,这个方法被调用。原本我在这里写的是:Self.Free;测试的时候发现,TFrame1 这个界面确实被关闭了,主界面显示出来了。但是此时如果是 WINDOWS,如果鼠标去点击主界面右上角的 X 按钮关闭程序,则点击没反应。改为点击后启动一个线程,然后让线程去执行 Self.Free 则没有这个问题。因此 CloseMe 这个函数稍微复杂一点。

4. 既然是点击返回按钮关闭当前 Frame,为什么不直接在设计期,双击 Button.OnClick 事件,然后在事件框架里面写 CloseMe,还要在 TFrameBase.Create 这个方法里面去执行事件绑定操作?如果仅仅是在 Button.OnClick 里面做设计期的绑定,继承的 Frame 是没有这个事件的。必须用代码绑定,而且这个代码要写在子类可以继承的函数或者方法里面。

实际要用的子类

假设我有一个实际要使用的子类是一个文件名列表的显示界面。在 Delphi IDE 的菜单里,选择 File->New->Other 就会弹出一个窗口。在这个窗口的左侧栏,选择 inheritedItems,右侧就会出现当前工程项目里面的所有 Form 和 Frame,在里面找到 TFrameBase,窗口底部的 inherit 的 Raido Button 处于被选择状态,点击窗口底部的 OK 按钮,IDE 就产生了一个从 TFrameBase 继承下来的 Frame,它的界面外观和 TFrameBase 一样,顶部有返回按钮。此时我们就可以在这个新的 Frame 里面开始功能设计了。

为了说清楚整个流程,这里简单说一下这个用于显示文件名列表的 Frame 的功能设计:

1. 文件名列表是在一个数据库的表里面,表有一个字段,字段名为 FileName;这个表我采用了 FdMemTable 这个控件(方便测试和演示)。

2. 拖一个 ListView 到这个 Frame 上面,设置它的 Align 为 Client 让它占满整个 Frame 中间的空白部分。

4. 鼠标右键点 Frame ,在弹出来的菜单里面选择 Bind Visually 开始可视化数据绑定。IDE 的底部出来 LiveBindings Designer 界面,在里面使用鼠标把数据表和 ListView 之间拉上线就建立好了数据绑定关系。如下图:

主界面的按钮如何显示上述 Frame

代码如下:

procedure TFmMain.SpeedButton1Click(Sender: TObject);
begin
  DmBook.OpenMyBookDB;

  TFrameBase.ShowMyFrame<TFrameRemoteFiles>(Self, DoFileListClose);
end;

上述代码的解释:

1. 首先打开数据库的表。这样才能在前面我们设计的 ListView 里面显示文件名列表;

2. 调用 TFrameBase 的类方法,传入真正要创建的类名:TFrameRemoteFiles;

3. DoFileListClose 是 TFmMain 的一个方法,当这个 Frame 被关闭释放的时候被调用。

总结

当一个 APP 有很多基本界面框架相同,显示的内容不同的时候,我们可以从 FrameBase 继承下来,就少写了很多代码,也节约了很多界面设计操作的时间。

最好呈现的效果是,点击某个按钮,屏幕上就显示对应的界面。点击该界面的返回按钮,因为该界面被释放了,因此就自然地显示了该界面弹出来之前的界面。如果是多级界面,逐级进入,这个框架底下,返回界面也就逐级返回,有了界面逐层堆叠,逐级返回的效果。

OpenHarmony实战开发-帧率分析工具 Frame Profiler使用指导 DevEco Studio内置Profiler分析调优工具,其中Frame分析调优功能,用于录制GPU数据信息,录制完成展开之后的子泳道对应录制过程中各个进程的帧数据,主要用于深度分析应用或服务卡顿丢帧的原因。此外,Frame任务窗口还集成了Time、CPU场景分析任务的功能,方便开发者在分析丢帧数据时同步对比同一时段的其他资源占用情况。 阅读详情

相关推荐

tkinter搭建GUI软件框架并创建不同的Frame界面实现不同的功能

在本篇博客中,小编要带大家解决的问题是如何创建一个软件窗口对象,并在窗口顶部菜单中通过按钮实现不同Frame界面的切换,在不同的Frame页面中实现访问路径等不同的功能,其中每行代码代表的意思,小编也在相应代码后面进行了注释,各位小可爱能够非常容易理解各行代码的含义,对于刚入门的小可爱们非常友好。点击MODEL菜单,切换至MODEL页面下,如图2所示,在改页面,小编为大家编写的代码可以实现访问文件夹的路径并在中间的Entry框中返回该路径,点击A'就可以实现该功能,点击完后如图3所示。

qq_43464006的博客 881

Delphi TForm 转 TFrame

Delphi之前的代码,特别是复杂VCL窗口,转为Frame时,有时需要,从头做进,操作繁杂。 本人通过,找到快速将TForm轩为TFrame的办法:

lyhoo163的专栏 5097

Tk应用程序界面之Frame容器使用

当你想创建一个窗口的应用程序,需要把一个窗口分成几块区域使用,就需要使用Frame来分割窗口,Frame就当作一个容器,然后在这个容器里添加其它组件。这种操作也叫做窗口布局。 下面就来详细演示这个容器的使用。 导入tkinter模块,命名为tk。 import tkinter as tk 继承Frame类。 class myApp(tk.Frame): def __init__(self,title = 'My App'): #调用基类构造函数 tk...

大坡3D软件开发 2750

Delphi中Frame的使用方法

Frame是组件面板上的第一个组件,但不是每个人都知道怎么用它,因为它不像Button和Label一样简单明了。实际上,Frame按钮只是打开一个Frame的列表,如果你没有创建任何的Frame,自然什么也不会看到。 那么Frame到底是什么呢?简单来说: 1. Frame是组件容器,类似Panel 2. Frame在使用上类似Components组件,但并没有构造器

1024

Delphi中frame的使用

Delphi中frame的使用Delphi中frame的使用Delphi中frame的使用Delphi中frame的使用Delphi中frame的使用

Delphi Frame使用例子, 引用类的用法

Frame使用例子, 引用类的用法 delphi中Frame重用的例子,非常好

Delphi中Frame的使用方法(1)

Frame是组件面板上的第一个组件,但不是每个人都知道怎么用它,因为它不像Button和Label一样简单明了。实际上,Frame按钮只是打开一个Frame的列表,如果你没有创建任何的Frame,自然什么也不会看到。 那么Frame到底是什么呢?简单来说: 1. Frame是组件容器,类似Panel 2. Frame在使用上类似Components组件,但并没有构造器和解析器,依赖于

技术只是工具,人生才是目标 3万+

Delphi的Frame框架示例

在使用Delphi时,相信使用Frame框架的人不多,今天无意中发现了Frame框架的妙用,其功能之强大,特示例之

半夜写代码之:APP 的 Form 的释放

在 Windows 底下,我们通常采用很多个 Form 来把复杂的界面元素,按功能划分,放到不同的 Form 上来显示。需要新的界面时,就来一个 Form2.Show 就可以了。 在 APP 底下,手机的资源比较有限,太多的 Form 会让手机受不了。因此,很多 FireMonkey 的开发建议里面,建议尽量少用 Form,多用 Frame。因为 Form 占用的资源比较多。 但是,在很多界面

pcplayer的博客 1080

VOLTE信令流程-IMS注册篇(五)

目录 VOLTE信令流程-IMS 注册 VOLTE信令流程-IMS 注册/注销 VOLTE信令流程-VoLTE呼叫流程 VoLTE-VoLTE VoLTE-CS CS-VoLTE CS-CS VOLTE信令流程 VOLTE信令流程-VOLTE-VOLTE信令 VOLTE信令流程-INVITE详细信息 VOLTE信令流程-eSRVCC信令流程 VOLTE信令流程-IP短信 VOLTE信令流程-IMS 注册 IMS注册流程简述  获取鉴权向量 IMS用户将注...

gender123的博客 2万+

qemu-system-aarch64参数详解与实战配置指南

本文详细解析了qemu-system-aarch64的核心参数,包括-machine、cpu、smp、m、drive和netdev等关键配置。通过嵌入式内核调试、完整系统运行和外设模拟三大实战场景,提供可直接使用的配置示例与性能优化技巧,帮助开发者在x86主机上高效构建和调试ARM64虚拟环境。

weixin_29197051的博客 351

python中frame用法_Python实例之wxpython中Frame使用方法

本节为大家分享的例子是wxpython Frame的用法。例子:代码如下:#!/usr/bin/python# -*- coding: GBK -*-# simple.pyimport wxapp = wx.App()frame = wx.Frame(None)frame.Show()app.MainLoop()例2,代码如下:#!/usr/bin/python告诉程序 python 解释器的路径...

weixin_39600837的博客 3085

Nest的限流方案

多维度限流(如 IP、用户 ID、接口级别、请求类型)可配置化(支持不同环境和接口动态配置)可观测性(接入日志、监控系统)高性能和分布式支持(Redis 限流器)与鉴权鉴权链路集成(结合 JWT、API Token)以下是一个“标准// 优先按用户ID限流,否则按IP在main.ts维度建议限流存储推荐 Redis 支持多实例限流 Key 设计支持 IP + 用户ID + 路由等多种维度限流策略精细化配置,接口粒度调整异常处理自定义响应体、日志告警观测性。

Bruce__taotao的博客 1196

wxWindows Frame程序简化版

wxWindows  Frame程序简化版前几天给出了一个最简单的Hello World 程序, 由于这几天我还在学习wxWindows, 所以把今天做的第一个Frame框架程序写出来,给大家分享.1.  新建一个Win32 Application,名字为Frame,然后选择Empty Project,点Finish完成.2.  Insert 一个Class, Class Type为Generic

糯米糊糊的专栏 2479

【FPGA】DDR3调试总结

DDR3管脚根据不同的功能可以分为:数据组、地址组、控制组和电源组。如图上所示,共96个管脚。

m0_51679724的博客 4967

YOLO26魔改:ICIP2026 SDTA|单深度转置注意力注入C2PSA,长程依赖与局部细节双增强,mAP提升显著

多尺度适配:SDTA的深度卷积与C2PSA的空间注意力结合,增强对多尺度目标的结构感知,改善遮挡与密集场景下的检测鲁棒性。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 292

python中frame是什么意思_Python实例之wxpython中Frame使用方法

本节为大家分享的例子是wxpython Frame的用法。例子:#!/usr/bin/python# -*- coding: GBK -*-# simple.pyimport wxapp = wx.App()frame = wx.Frame(None)frame.Show()app.MainLoop()例2,#!/usr/bin/python告诉程序 python 解释器的路径,只是在 linux...

weixin_39639096的博客 2224

YTM32B1M SDK解析31:软件I2C驱动详解

YTM32B1M微控制器软件I2C驱动解析 本文详细介绍了YTM32B1M微控制器中通过GPIO引脚模拟I2C总线的软件实现方案。核心内容包括: 驱动架构:采用swi2c_config_t配置结构体和swi2c_state_t状态结构体管理I2C参数 功能接口:提供初始化、数据传输(主/从模式)、底层控制等API 应用示例:包含基本配置、EEPROM读写、传感器数据采集、总线扫描等典型应用场景 优化建议:给出引脚选择、时序配置、错误处理等方面的最佳实践 该驱动在硬件I2C资源不足时提供可靠替代方案,支持标准

VehSwHwDeveloper的博客 158

wxPython 基本框架与运行原理 -- App 与 Frame

《wxPython in Action》 chapter 1、2 笔记 wxPython 是 wxWidgets 的 Python 实现,“w” for Microsoft Windows and “x” for Unix Xserver. 1. 概述 wxPython 中,最基本的组件是 wx.App 和 wx.Frame。 每个 wxPython 应用必须包含这两个组件。wx.App...

weixin_33858249的博客 672

Java全栈开发:充电桩管理系统项目实战

技术收获深入理解分布式系统架构掌握高并发场景解决方案提升系统设计能力经验教训前期需求分析的重要性技术选型需要考虑长期维护测试覆盖率的重要性未来展望引入AI预测故障支持更多支付方式优化用户体验通过此项目的开发,不仅实现了充电桩的智能化管理,也积累了宝贵的全栈开发经验。项目中的很多技术方案和架构设计都具有普遍适用性,可以在其他类似项目中借鉴使用。@Componentstatic {// 初始化状态转换规则。

exlink2012的专栏 1251

RK3588部署YOLO模型 包含模型量化+npu轻量化+ubuntu+RGA图像加速和多线程模型加速 包源码

本专栏为深度学习部署全栈学习体系,包含68篇由浅入深的教程,专为零基础学习者设计。内容涵盖TensorRT与RK3588平台部署技术,包含Jetson系列和RK系列硬件部署实战,采用"理论+源码+实战"三位一体的教学模式。学习路径从基础环境搭建到模型优化、容器化部署、多硬件平台适配,最终掌握工业级AI部署能力。特色包括C++基础教学、Docker容器化、INT8量化、多线程优化等核心技术,并附带YOLOv5、人脸检测、车牌识别等多个实战项目源码。适合算法工程师转部署方向、嵌入式开发者及对

FJN110的博客 3730

Frame 架构全解析:frame-app、frame-core、frame-updater 三 crate 分工详解

Frame 是一款用 Rust 编写的 FFmpeg GUI 媒体转换工具,它的代码由 frame-app、frame-core、frame-updater 三个核心 crate 组成:界面、转换引擎与自动更新各司其职。本文带你完整看懂这套 Rust 多 crate 架构的分工设计与协作流程,无需阅读源码也能建立清晰的整体认知。 [![Frame 架构应用主界面预览:文件队列、预览面板与设置面板

gitblog_00009的博客 186

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

re-frame 应用状态检查指南:从 REPL 到 GUI 的 app-db 探查方案

`app-db` 是 re-frame 应用中全部应用状态的唯一存放处,也是事件流、订阅信号图的"根数据源"。在实际开发中,无论排查状态异常、验证事件处理器(event handler)的修改结果,还是调试订阅数据流,开发者最常遇到的第一个问题都是:"我该怎么查看 `app-db` 里到底有什么?" 本文以 re-frame 官方 FAQ《Inspecting-app-db》为骨架,结合本仓库的源

gitblog_00172的博客 736

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Python实例之wxpython中Frame使用方法

为大家分享的例子是wxpython Frame的用法。 #!/usr/bin/python -- coding: GBK -- simple.py import wx app = wx.App() frame = wx.Frame(None) frame.Show() app.MainLoop() #!/usr/bin/python 告诉程序 python 解释器的路径,只是在 linux 系统下...

ergouzia的博客 2637

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

纯血鸿蒙开发教程-帧率分析工具 Frame Profiler使用指导

DevEco Studio内置Profiler分析调优工具,其中Frame分析调优功能,用于录制GPU数据信息,录制完成展开之后的子泳道对应录制过程中各个进程的帧数据,主要用于深度分析应用或服务卡顿丢帧的原因。此外,Frame任务窗口还集成了Time、CPU场景分析任务的功能,方便开发者在分析丢帧数据时同步对比同一时段的其他资源占用情况。

m0_70749039的博客 1283

wxPython开发总结---Frame,Panel以及一种项目架构

关注公众号“码农帮派”,查看更多系列技术文章: 前一段时间用Python开发了一个教育类的PC桌面应用,边学边开发,现在陆续的将开发过程中学到的一些东西分享记录下来,项目使用了wxPython框架。 wxPython的官网:https://www.wxpython.org/ wxPython各种控件用法官方手册 :http://xoomer.virgilio.it/i...

天马行空的专栏 1万+
上一篇: Delphi程序响应 TEdgeBrowser 页面里面的点击
下一篇: Delphi 12 基于 Indy 的 WebServer 的 https 实现的问题
pcplayer
博客等级 码龄26年 633粉丝 284原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值