• Blog
  • How to Use Gestures in Embarcadero Delphi FMX App

How to Use Gestures in Embarcadero Delphi FMX App

Explore the capabilities of TGestureManager in Embarcadero Delphi FMX App

Publish date:
Discover more of what matters to you

In modern applications, we often use different gestures. For example, you can zoom in or out on a Google map or image using the Zoom In/Out gesture. Another example is swiping through photos with the Swipe gesture (simply dragging your finger left or right). 

To rotate an image, you can use a simple gesture like Rotate by twisting two fingers. Often, we use the gesture of long pressing called LongTap. By holding a finger on the object in the app, you can bring up a context menu or trigger other actions. 

In this article, we will explore the capabilities of the TGestureManager component for working with gestures in the Embarcadero Delphi FMX app. 

Initial Project Setup for an Embarcadero Delphi FMX App

As an example, we’ll create an FMX app for the Android platform to demonstrate working with swipe gestures (left and right), resizing, and rotation. Also, we’ll use the Rotate gesture to implement image rotating. 

For this example, we’ll use 12 images in the PNG format.  

We’ll add these images to the project as resources for our Embarcadero Delphi FMX app on the Android platform. To do this, go to Project > Deployment.

To add a PNG image to the project, click the “Add Files” button. 

Add each of the 12 images one by one.

The last image file is called StPatrick_12.png. During the deployment of our Embarcadero Delphi app on the Android platform, all 12 PNG images will be included as private internal application files (stored in the assets/internal directory). 

After adding images to the project, build it by going to Project > Build PngLoadRes. The project is called PngLoadRes.

After successfully building the project, you receive a confirmation message:

Finally, click “Deploy” to deploy the application. 

Learn how to create dynamic reports with the features of Delphi’s FastReport

Developing the Embarcadero Delphi FMX App

To implement gestures in our application, we will use the TGestureManager component. 

To display images in the application, we will use the TImage component.

The TLabel component will be used to display the file path of the image within the application.

Adding the TGestureDetector component is necessary to enable gesture recognition on the main form. 

Next, we need to enable gestures such as swiping left and right to switch images. These are standard gestures managed via the Touch property of the main form. 

For rotating or resizing the image, we will use interactive gestures like Rotate and Zoom. It is also necessary to activate them by selecting the appropriate checkboxes. No additional configuration is necessary for the TGestureManager component.

We will declare four private fields. 

  • The FAngle field (of type Single) stores the current rotation angle when the user uses the Rotate gesture with two fingers. 
  • The FLastDistance field (of type Integer) stores the most recent distance between the user’s fingers during the Zoom gesture. 
  • The FId field (of type Integer) holds the index of the current image, from 1 to 12.
  • The FPicFilename filed (of type String) stores the full path of the PNG images. 

In the onCreate handler of the main form, we assign the FId value to zero.

To load image files, we will include the System.IOUtils library. 

Gesture handling will be implemented in the onGesture handler of the main form.

The EventInfo object contains details about the user’s gesture. The EventInfo.GestureID field holds the ID of the gesture performed by the user on the main form (in this case, Swiping left and right, Rotate, and Zoom). 

To process and identify the gestures, constants, such as sgiLeft and sgiRight, igiRotate and igiZoom, are used. 

The local variable LObj (type IControl) contains the component that the user touched (in this case, a TImage component used to display an image). The ImageCenter variable (type TPointF) will store coordinates (x, y) of the image’s center. 

Let’s observe the fragment of the program code for processing the Rotate gesture for rotating the image. 

The LObj object holds the component that the user touched (LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location))). 

We check if the user touched a TImage component (if LObj is TImage then). If true, we set the rotation angle (performed by the user’s two fingers) to the current rotation angle of the TImage component (FAngle := Image1.RotationAngle). 

If the user starts rotating the image, we adjust the angle of the TImage component (Image1.RotationAngle := FAngle – (EventInfo.Angle * 180) / Pi).

Let’s observe the fragment of the program code for processing the Zoom gesture for changing image size. 

Generally, identification of the TImage component is performed via LObj (Image1 := TImage(LObj.GetObject)) similar to Rotate. 

Then we set the center and size of the image (Image1.Width and Image1.Height) based on the distance between the user’s fingers during the Zoom gesture:

(ImageCenter := Image1.Position.Point +PointF(Image1.Width / 2, Image1.Height / 2); Image1.Width := Image1.Width + (EventInfo.Distance – FLastDistance); Image1.Height := Image1.Height + (EventInfo.Distance – FLastDistance);).

To prevent excessing resizing, we limit the minimum width and height of the TImage component (Image1.Width := Image1.Width + (EventInfo.Distance – FLastDistance);

Image1.Height := Image1.Height + (EventInfo.Distance – FLastDistance);).

The position of the TImage component is adjusted relative to the main form: (Image1.Position.X := ImageCenter.X – Image1.Width / 2; Image1.Position.Y := ImageCenter.Y – Image1.Height / 2;).

After that, the current distance between the user’s fingers is stored for subsequent Zoom gestures.

Let’s observe the fragment of the program code for processing the Swipe gesture for swiping images left and right. 

The FId field will only accept values in the range 1 to 12:

(if FId > 12 then FId := 12 else if FId < 1 then FId := 1). 

The full file path to the PNG image is assigned to the FPicFilename field: (FPicFilename := TPath.Combine(TPath.GetDocumentsPath, ‘StPatrick_’ + IntToStr(FId) + ‘.png’)).

We show the full path to the image in a TLabel: 

(Label1.Text := FPicFilename).

Then we load the image file from the internal app folder into the TImage component and display it to the user: (Image1.Bitmap.LoadFromFile(FPicFilename))

When the user is performing the Swipe left gesture (sgiLeft), the FId field is incremented to load the next image (FId := FId + 1).

When the user is performing the Swipe right (sgiRight), the FId field is decremented to load the previous image (FId := FId – 1).

The source code of the main form module is shown below:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
unit Main;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
FMX.Objects, System.IOUtils, FMX.Gestures, System.Notification,
FMX.Controls.Presentation, FMX.Layouts;
type
TForm1 = class(TForm)
Image1: TImage;
Label1: TLabel;
GestureManager1: TGestureManager;
Layout1: TLayout;
procedure FormCreate(Sender: TObject);
procedure FormGesture(Sender: TObject; const EventInfo: TGestureEventInfo;
var Handled: Boolean);
private
{ Private declarations }
FAngle: Single;
FLastDistance: Integer;
FId: Integer;
FPicFilename: string;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
FId := 0;
end;
procedure TForm1.FormGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
var
LObj: IControl;
ImageCenter: TPointF;
begin
case EventInfo.GestureID of
igiRotate:
begin
LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
if LObj is TImage then
begin
Image1 := TImage(LObj.GetObject);
if TInteractiveGestureFlag.gfBegin in EventInfo.Flags then
FAngle := Image1.RotationAngle
else if EventInfo.Angle <> 0 then
Image1.RotationAngle := FAngle – (EventInfo.Angle * 180) / Pi;
end;
end;
igiZoom:
begin
LObj := Self.ObjectAtPoint(ClientToScreen(EventInfo.Location));
if LObj is TImage then
begin
if (not (TInteractiveGestureFlag.gfBegin in EventInfo.Flags)) and
(not (TInteractiveGestureFlag.gfEnd in EventInfo.Flags)) then
begin
Image1 := TImage(LObj.GetObject);
ImageCenter := Image1.Position.Point +
PointF(Image1.Width / 2, Image1.Height / 2);
Image1.Width := Image1.Width + (EventInfo.Distance – FLastDistance);
Image1.Height := Image1.Height + (EventInfo.Distance – FLastDistance);
if (Image1.Width < 30) or (Image1.Height < 30) then
begin
Image1.Width := 30;
Image1.Height := 30;
end;
Image1.Position.X := ImageCenter.X – Image1.Width / 2;
Image1.Position.Y := ImageCenter.Y – Image1.Height / 2;
end;
FLastDistance := EventInfo.Distance;
end;
end;
sgiLeft:
begin
if FId > 12 then
FId := 12
else if FId < 1 then
FId := 1;
FPicFilename := TPath.Combine(TPath.GetDocumentsPath,
'StPatrick_' + IntToStr(FId) + '.png');
Label1.Text := FPicFilename;
Image1.Bitmap.LoadFromFile(FPicFilename);
FId := FId + 1;
end;
sgiRight:
begin
if FId > 12 then
FId := 12
else if FId < 1 then
FId := 1;
FPicFilename := TPath.Combine(TPath.GetDocumentsPath,
'StPatrick_' + IntToStr(FId) + '.png');
Label1.Text := FPicFilename;
Image1.Bitmap.LoadFromFile(FPicFilename);
FId := FId – 1;
end;
end;
end;
end.

Let’s demonstrate the functionality of our Embarcadero Delphi FMX app on the Android platform.

Explore our Delphi services!
We handle Delphi migration, integration, and development, fully sharing project risks and responsibilities.
Learn more

Subscribe to our newsletter and get amazing content right in your inbox.

This field is required
This field is required Invalid email address
By submitting data, I agree to the Privacy Policy

Thank you for subscribing!
See you soon... in your inbox!

confirm your subscription, make sure to check your promotions/spam folder

Subscribe to our newsletter and get amazing content right in your inbox.

You can unsubscribe from the newsletter at any time

This field is required
This field is required Invalid email address

You're almost there...

A confirmation was sent to your email

confirm your subscription, make sure to check
your promotions/spam folder