FMXTAniCalculations (Delphi)

From RAD Studio Code Examples
Jump to: navigation, search

Description

This example shows how to use the Animation, DecelerationRate, BoundsAnimation, AutoShowing, and Shown properties to set the inertial scrolling properties for the TScrollBox scroll component. By setting these scrolling properties, you can customize the behavior of the inertial moving of a scroll view in response to a touch input (with the mouse or finger).

In the TScrollBox class, the AniCalculations property handles the TAniCalculations type object that provides support for the smooth inertial scrolling. Since AniCalculations has public (not published) visibility, you cannot set the scrolling properties in the Object Inspector. You can set the inertial scrolling properties of the AniCalculations object programmatically. For example, using the properties of AniCalculations, you can emulate smooth inertial moving of a scroll view under Windows platforms.

To build and test this example, create a Multi-Device Application - Delphi project and add the following controls on the main form:

  • A TRectangle to occupy the most part of the form.
  • A TScrollBox into TRectangle to coincide with the TRectangle.
  • A TLabel on the upper-left corner of the form.
  • Several TButton objects to populate the rectangle of the TScrollBox.
  • Short vertical and horizontal TLine objects on the upper-left corner of the form.
  • A TText object between two lines on the upper-left corner of the form.

The designed form can look like this:
Inertial Scrolling form

The corresponding InertialScrolling.FMX file can look like this:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 232
  ClientWidth = 332
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop, Phone, Pad]
  OnCreate = FormCreate
  OnShow = FormShow
  DesignerMobile = False
  DesignerWidth = 0
  DesignerHeight = 0
  DesignerDeviceName = ''
  DesignerOrientation = 0
  object Rectangle1: TRectangle
    Align = Client
    Height = 200.000000000000000000
    Margins.Left = 16.000000000000000000
    Margins.Top = 16.000000000000000000
    Margins.Right = 16.000000000000000000
    Margins.Bottom = 16.000000000000000000
    Stroke.Color = claNull
    Stroke.Dash = Dash
    Width = 300.000000000000000000
    object ScrollBox1: TScrollBox
      Touch.InteractiveGestures = [Pan]
      Align = Client
      ClipChildren = True
      Height = 200.000000000000000000
      TabOrder = 0
      Width = 300.000000000000000000
      OnPaint = ScrollBox1Paint
      object Line1: TLine
        Height = 9.000000000000000000
        LineType = Top
        Position.X = -20.000000000000000000
        Width = 40.000000000000000000
      end
      object Line2: TLine
        Height = 40.000000000000000000
        LineType = Left
        Position.Y = -20.000000000000000000
        Width = 9.000000000000000000
      end
      object Text1: TText
        Color = claBlack
        Height = 17.000000000000000000
        Text = '0, 0'
        Width = 25.000000000000000000
      end
      object Button1: TButton
        Height = 22.000000000000000000
        Position.X = 56.000000000000000000
        Position.Y = 96.000000000000000000
        TabOrder = 3
        Text = 'Button1'
        Width = 80.000000000000000000
      end
      object Button2: TButton
        Height = 22.000000000000000000
        Position.X = 8.000000000000000000
        Position.Y = 184.000000000000000000
        TabOrder = 4
        Text = 'Button2'
        Width = 80.000000000000000000
      end
    end

The example shows the scroll box populated with button controls. The bounding rectangle for all buttons forms the scrolling viewport.

Code

Add the following code into the InertialScrolling.pas file of the form:

unit InertialScrolling;

interface

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

type
  TScrollBox = class (FMX.Layouts.TScrollBox)

  end;

  TForm1 = class(TForm)
    Rectangle1: TRectangle;
    ScrollBox1: TScrollBox;
    Line1: TLine;
    Line2: TLine;
    Text1: TText;
    Button1: TButton;
    Button2: TButton;
    Label1: TLabel;
    procedure ScrollBox1Paint(Sender: TObject; Canvas: TCanvas;
      const ARect: TRectF);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure ViewportPositionChange(Sender: TObject;
                               const OldViewportPosition, NewViewportPosition: TPointF;
                               const ContentSizeChanged: Boolean);
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
// Edit these properties to change inertial scrolling parameters 
  ScrollBox1.AniCalculations.TouchTracking := [ttVertical, ttHorizontal]; // [ttVertical]; // 
  ScrollBox1.AniCalculations.Animation := True; // False; //
  ScrollBox1.AniCalculations.BoundsAnimation :=  False; // True; //
  ScrollBox1.AniCalculations.DecelerationRate :=  0.5; //DecelerationRateFast; //DecelerationRateNormal; //
  ScrollBox1.AniCalculations.AutoShowing := True; // False; //
  ScrollBox1.OnViewportPositionChange := ViewportPositionChange;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Invalidate;
end;

procedure TForm1.ScrollBox1Paint(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
var
  State: TCanvasSaveState;
  R, NewR: TRectF;
begin
  State := Canvas.SaveState;
  try
    if Assigned(ScrollBox1.Content) then
    begin
      NewR := ScrollBox1.ContentLayout.LocalRect;
      NewR.TopLeft := ScrollBox1.ContentLayout.LocalToAbsolute(NewR.TopLeft);
      NewR.BottomRight := ScrollBox1.ContentLayout.LocalToAbsolute(NewR.BottomRight);
      NewR.TopLeft :=  ScrollBox1.AbsoluteToLocal(NewR.TopLeft);
      NewR.BottomRight := ScrollBox1.AbsoluteToLocal(NewR.BottomRight);
      InflateRect(NewR, -0.5, -0.5);
      Canvas.Stroke.Color := TAlphaColorRec.Black;
      Canvas.Stroke.Dash := TStrokeDash.Solid;
      Canvas.DrawRect(NewR, 0, 0, AllCorners, 0.5);


      R := ScrollBox1.ContentBounds;
      NewR.TopLeft := ScrollBox1.Content.LocalToAbsolute(R.TopLeft);
      NewR.BottomRight := ScrollBox1.Content.LocalToAbsolute(R.BottomRight);
      NewR.TopLeft :=  ScrollBox1.AbsoluteToLocal(NewR.TopLeft);
      NewR.BottomRight := ScrollBox1.AbsoluteToLocal(NewR.BottomRight);

      Canvas.Stroke.Color := TAlphaColorRec.Red;
      Canvas.DrawRect(NewR, 0, 0, AllCorners, 0.5);
      Canvas.Fill.Color := TAlphaColorRec.Red;
      Canvas.DrawLine(PointF(NewR.Right, NewR.Top),
                      PointF(NewR.Right + 4, NewR.Top - 4),
                      0.5);

      NewR.Left := NewR.Right;
      Newr.Offset(5, -15);
      NewR.Right := NewR.Left + 200;
      Canvas.FillText(NewR,
                      'ContentBounds: ' + #13#10 +
                                          FloatTostr(R.Left) + '; ' +
                                          FloatTostr(R.Top) + '; ' +
                                          FloatTostr(R.Right) + '; ' +
                                          FloatTostr(R.Bottom),
                      False,
                      0.5,
                      [],
                      TTextAlign.Leading,
                      TTextAlign.Leading);

      NewR.TopLeft := ScrollBox1.ViewportPosition;
      NewR.BottomRight := NewR.TopLeft;
      NewR.BottomRight.Offset(10, 10);
      NewR.TopLeft := ScrollBox1.Content.LocalToAbsolute(NewR.TopLeft);
      NewR.BottomRight := ScrollBox1.Content.LocalToAbsolute(NewR.BottomRight);
      NewR.TopLeft :=  ScrollBox1.AbsoluteToLocal(NewR.TopLeft);
      NewR.BottomRight := ScrollBox1.AbsoluteToLocal(NewR.BottomRight);
      Canvas.Stroke.Color := TAlphaColorRec.Green;
      Canvas.DrawLine(NewR.TopLeft, NewR.BottomRight, 0.5);
      NewR.TopLeft := NewR.BottomRight;
      NewR.BottomRight.Offset(200, 20);
      Canvas.Fill.Color := TAlphaColorRec.Green;
      Canvas.FillText(NewR,
                      'ViewportPosition: ' + FloatTostr(ScrollBox1.ViewportPosition.X) + '; ' +
                                             FloatTostr(ScrollBox1.ViewportPosition.Y),
                      False,
                      0.5,
                      [],
                      TTextAlign.Leading,
                      TTextAlign.Leading);


    end;
  finally
    Canvas.RestoreState(State);
  end;
end;

procedure TForm1.ViewportPositionChange(Sender: TObject;
  const OldViewportPosition, NewViewportPosition: TPointF;
  const ContentSizeChanged: Boolean);
var
  S: string;
begin
  S := 'ContentLayout: ' +
              FloatTostr(ScrollBox1.ContentLayout.LocalRect.Left) + '; ' +
              FloatTostr(ScrollBox1.ContentLayout.LocalRect.Top) + '; ' +
              FloatTostr(ScrollBox1.ContentLayout.LocalRect.Right) + '; ' +
              FloatTostr(ScrollBox1.ContentLayout.LocalRect.Bottom);
  Label1.Text := S;
end;

end.

The following code is the most interesting for us:

procedure TForm1.FormCreate(Sender: TObject);
begin
// Edit these properties to change inertial scrolling parameters 
  ScrollBox1.AniCalculations.TouchTracking := [ttVertical, ttHorizontal]; // [ttVertical]; // 
  ScrollBox1.AniCalculations.Animation := True; // False; //
  ScrollBox1.AniCalculations.BoundsAnimation :=  False; // True; //
  ScrollBox1.AniCalculations.DecelerationRate :=  0.5; //DecelerationRateFast; //DecelerationRateNormal; //
  ScrollBox1.AniCalculations.AutoShowing := True; // False; //

These properties define parameters of the inertial moving of the scrolling area. Look how the Canvas.FillText method is used to write the ContentBounds, ViewportPosition strings and how Label1.Text is used to show the ContentLayout string.

Build and run the target. The example demonstrates how the form containing the scrollable area looks like:

Inertial Scrolling demo

Use the mouse to change the form size. You can test how the scrollable area inertially scrolls as a response to the mouse movement.

Change the values of the properties used in the TForm1.FormCreate. Build and run the modified target. Compare the modified behavior.

Uses

See Also

InertialMovement.TAniCalculations.Animation ( fr | de | ja )

FMX.InertialMovement.TAniCalculations.DecelerationRate ( fr | de | ja )

FMX.InertialMovement.TAniCalculations.BoundsAnimation ( fr | de | ja )

FMX.InertialMovement.TAniCalculations.AutoShowing ( fr | de | ja )

FMX.InertialMovement.TAniCalculations.Shown ( fr | de | ja )