Watch, Follow, &
Connect with Us

Embarcadero Blogs

Latest Posts


Creating a Bluetooth LE cloud enabled luggage scale application

Every time I travel somewhere, I am concerned that my luggage might exceed the weight limit. With RAD Studio XE7 and the Wahoo Bluetooth Scale, I built a luggage scale application that allows me to weigh my luggage and store data about my luggage contents in the cloud.

The app UI consists of a TTabControl with 3 tabs:

  • WeightScale - for weighing my luggage and saving the weight and associated notes
  • Logged Data - for retrieving existing luggage data and displaying it in a list
  • Settings - for displaying my bluetooth data

In addition to the various UI components, the app also contains a TBluetoothLE component, a TStyleBook component (for loading and assigning my custom Jet style) and the following BaaS components:

  • TKinveyProvider (AppKey, AppSecret and MasterSecret match my Kinvey.com credentials)
  • TBackendQuery (connected to TKinveyProvider)
  • TBackendStorage (connected to TKinveyProvider)
  • TRestResponseDataSetAdapter
  • TFDMemTable

The scale I am working with is the Wahoo Balance scale.

Here is the Object Pascal code for my application:

unit WeightScaleForm1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Ani, FMX.StdCtrls, System.Bluetooth, FMX.Layouts,
  FMX.Memo, FMX.Controls.Presentation, FMX.Edit, FMX.Objects, IPPeerClient, IPPeerServer,
  System.Tether.Manager, System.Bluetooth.Components, FMX.TabControl,
  REST.Backend.ServiceTypes, REST.Backend.MetaTypes, System.JSON,
  REST.Backend.KinveyServices, REST.OpenSSL, REST.Backend.KinveyProvider,
  REST.Backend.Providers, REST.Backend.ServiceComponents, FMX.ListView.Types,
  Data.Bind.Components, Data.Bind.ObjectScope, REST.Backend.BindSource,
  FMX.ListView, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
  FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
  REST.Response.Adapter, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client,
  System.Rtti, System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.EngExt,
  Fmx.Bind.DBEngExt, Data.Bind.DBScope;

type

  TSensorContactStatus = (NonSupported, NonDetected, Detected);

  THRMFlags = record
    HRValue16bits: boolean;
    SensorContactStatus: TSensorContactStatus;
    EnergyExpended: boolean;
    RRInterval: boolean;
  end;

  TfrmWeightMonitor = class(TForm)
    BluetoothLE1: TBluetoothLE;
    TabControl1: TTabControl;
    WeightScale: TTabItem;
    History: TTabItem;
    Panel1: TPanel;
    Memo1: TMemo;
    Panel2: TPanel;
    btnConnect: TButton;
    lblDevice: TLabel;
    lblWeight: TLabel;
    btnDisconnect: TButton;
    Settings: TTabItem;
    ToolBar1: TToolBar;
    Label1: TLabel;
    ToolBar2: TToolBar;
    ToolBar3: TToolBar;
    Label3: TLabel;
    btnSave: TButton;
    KinveyProvider1: TKinveyProvider;
    BackendStorage1: TBackendStorage;
    AppTitle: TLabel;
    title: TLabel;
    lbs: TLabel;
    ListView1: TListView;
    BackendQuery1: TBackendQuery;
    FDMemTable1: TFDMemTable;
    RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter;
    FDMemTable1_id: TWideStringField;
    FDMemTable1Weight: TWideStringField;
    FDMemTable1_acl: TWideStringField;
    FDMemTable1_kmd: TWideStringField;
    BindSourceDB1: TBindSourceDB;
    BindingsList1: TBindingsList;
    LinkListControlToField1: TLinkListControlToField;
    FDMemTable1LuggageDetails: TWideStringField;
    ImageControl1: TImageControl;
    StyleBook1: TStyleBook;
    Image1: TImage;
    procedure btnConnectClick(Sender: TObject);
    procedure BluetoothLE1EndDiscoverDevices(const Sender: TObject; const ADeviceList: TBluetoothLEDeviceList);
    procedure BluetoothLE1CharacteristicRead(const Sender: TObject; const ACharacteristic: TBluetoothGattCharacteristic;
      AGattStatus: TBluetoothGattStatus);
    procedure btnDisconnectClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure LinkListControlToField1FilledListItem(Sender: TObject;
      const AEditor: IBindListEditorItem);
    procedure FormCreate(Sender: TObject);
    procedure HistoryClick(Sender: TObject);
  private
    { Private declarations }
    FBLEDevice: TBluetoothLEDevice;
    FWeightGattService: TBluetoothGattService;
    FWeightMeasurementGattCharacteristic: TBluetoothGattCharacteristic;
    procedure GetServiceAndCharacteristics;
  public
    { Public declarations }
  end;

const
  ScaleDeviceName = 'Wahoo Scale';
  ServiceUUID = '';
  CharactUUID = '';

  // 0x1901 is the weight service
  // 0x2B01 is the live weight characteristic readings    0x84ae0000 0001      - 38 lbs   17kg
  // 0b 10000100101011100000000000000000

  Weight_Device: TBluetoothUUID = '{00001901-0000-1000-8000-00805F9B34FB}';
  Weight_Service: TBluetoothUUID = '{00001901-0000-1000-8000-00805F9B34FB}';
  Weight_Characteristic: TBluetoothUUID  = '{00002B01-0000-1000-8000-00805F9B34FB}';

var
  frmWeightMonitor: TfrmWeightMonitor;

implementation

{$R *.fmx}
{$R *.iPhone4in.fmx IOS}

procedure TfrmWeightMonitor.btnConnectClick(Sender: TObject);
begin
  lblWeight.Text := '0';
  lblDevice.Text := '';
  Memo1.Lines.Clear;
  BluetoothLE1.DiscoverDevices(3500, [Weight_Device]);
end;

procedure TfrmWeightMonitor.btnDisconnectClick(Sender: TObject);
begin
If FBLEDevice <> nil then
  BluetoothLE1.UnSubscribeToCharacteristic(FBLEDevice, FWeightMeasurementGattCharacteristic);
  btnDisconnect.Enabled := false;  // disable disconnect from subscribe scale button
  btnConnect.Enabled := true;  // enable connect to scale button
end;

//store luggage notes in the cloud
procedure TfrmWeightMonitor.btnSaveClick(Sender: TObject);
var
  LJSON : TJSONObject;
  ACreatedObject: TBackendEntityValue;
  luggagedetails	: string;
begin
    LJSON := TJSONObject.Create;
if InputQuery('Luggage BaaS Cloud Store', 'Enter Notes about your Luggage', luggagedetails)
 then
     LJSON.AddPair('Weight', lblWeight.Text);
     LJSON.AddPair('LuggageDetails', luggagedetails);
     BackendStorage1.Storage.CreateObject('luggagecloud', LJSON, ACreatedObject);
     ShowMessage('Luggage weight and notes stored');
end;

procedure TfrmWeightMonitor.FormCreate(Sender: TObject);
begin
  BackendQuery1.Execute;
end;

procedure TfrmWeightMonitor.BluetoothLE1EndDiscoverDevices(const Sender: TObject;
  const ADeviceList: TBluetoothLEDeviceList);
var
  I: Integer;
begin
  // log
  Memo1.Lines.Add(ADeviceList.Count.ToString +  ' devices discovered:');
  for I := 0 to ADeviceList.Count - 1 do Memo1.Lines.Add(ADeviceList[I].DeviceName);

  if BluetoothLE1.DiscoveredDevices.Count > 0 then
  begin
    FBLEDevice := BluetoothLE1.DiscoveredDevices.First;
    ShowMessage('Connected to Wahoo Scale');
    if BluetoothLE1.GetServices(FBLEDevice).Count = 0 then
    begin
      Memo1.Lines.Add('No Weight services found!');
      lblWeight.Text := 'No Weight services found!';
    end
    else begin
      Memo1.Lines.Add('Wahoo Services Found: '+ IntToStr(BluetoothLE1.GetServices(FBLEDevice).Count));
      btnDisconnect.Enabled := true;  // enable disconnect from subscribe scale button
      btnConnect.Enabled := false;  //disable connect to scale button
      // get Weight Service and Characteristic
      GetServiceAndCharacteristics;
    end;
  end
  else
    lblDevice.Text := 'Weight Device not found';
end;

procedure TfrmWeightMonitor.GetServiceAndCharacteristics;
begin
  FWeightGattService := nil;
  FWeightMeasurementGattCharacteristic := nil;
  // get Weight Service by UUID
  FWeightGattService := BluetoothLE1.GetService(FBLEDevice, Weight_SERVICE);

  if FWeightGattService <> nil then begin
    memo1.Lines.Add('Service: '+FWeightGattService.UUID.ToString);
    // get Weight Characteristic
    Memo1.Lines.Add('Looking Char: '+Weight_CHARACTERISTIC.ToString);
    BluetoothLE1.GetCharacteristics(FWeightGattService);
    FWeightMeasurementGattCharacteristic := BluetoothLE1.GetCharacteristic(FWeightGattService,Weight_CHARACTERISTIC);

    if FWeightMeasurementGattCharacteristic <> nil then begin
      Memo1.Lines.Add('Char: '+FWeightMeasurementGattCharacteristic.UUID.ToString);
      // subscribe to the weight service
      BluetoothLE1.SubscribeToCharacteristic(FBLEDevice, FWeightMeasurementGattCharacteristic);
      Memo1.Lines.Add('Subscribed to Weight Measurement Characteristic');
    end
    else begin
      Memo1.Lines.Add('Weight Char not found');
      lblWeight.Text := 'Weight Char not found';
    end
  end
  else begin
    Memo1.Lines.Add('Weight Service not found');
    lblWeight.Text := 'Weight Service not found';
  end;
end;

//query BaaS provider for existing luggage data
procedure TfrmWeightMonitor.HistoryClick(Sender: TObject);
begin
  BackendQuery1.Execute;
end;

//display weight icon next to each row in TListview
procedure TfrmWeightMonitor.LinkListControlToField1FilledListItem(
  Sender: TObject; const AEditor: IBindListEditorItem);
var
  LItem: TListViewItem;
begin
  LItem := AEditor.CurrentObject As TListViewItem;
  LItem.BitmapRef := ImageControl1.Bitmap;
end;

procedure TfrmWeightMonitor.BluetoothLE1CharacteristicRead(const Sender: TObject;
  const ACharacteristic: TBluetoothGattCharacteristic; AGattStatus: TBluetoothGattStatus);
var
  LSValue: string;
  HexValue: string;
  WeightPounds : double;
begin
  if AGattStatus <> TBluetoothGattStatus.Success then
    Memo1.Lines.Add('Error reading Characteristic ' + ACharacteristic.UUIDName + ': ' + Ord(AGattStatus).ToString)
  else
  begin
    LSValue := IntToStr(ACharacteristic.GetValueAsInteger);
    Memo1.Lines.Add(ACharacteristic.UUID.ToString + ' String: ' + LSValue);
    // calculate weight - characteristic is in hectograms
    WeightPounds := (ACharacteristic.GetValueAsInteger shr 8) * 0.2205;
    Memo1.Lines.Add('Weight (pounds): '+Format('%8.2f',[WeightPounds]));
    lblWeight.Text := Format('%8.2f',[WeightPounds]);
  end;
end;

end.


posted @ Mon, 20 Oct 2014 23:49:14 +0000 by sarinadupont


Reserve your space: CodeRage 9 - the Delphi developer event of the year

CodeRage 9 - The totally technical online conference for Delphi, C++Builder and RAD Studio dedvelopers is just around the corner. The free online conference will be taking place October 28-30.


Join us for CodeRage® 9, the totally free, totally technical online conference for software developers. This year's conference brings you top industry speakers and technologists presenting on a wide variety of topics, all geared toward maximizing your software development productivity. 
The theme for this year's CodeRage conference is "You are the Developers of Things, Everything!" The conference will cover development topics for the Object Pascal and C++ programming languages with Delphi, C++Builder, RAD Studio and Appmethod. The conference will have two live stream tracks – one for Object Pascal and one for C++.


posted @ Fri, 17 Oct 2014 16:50:00 +0000 by Tim Del Chiaro


Mac OS X Yosemite Style Pack for RAD Studio XE7

Yesterday, Apple released Mac OS X Yosemite.

We have a new Yosemite style pack available for RAD Studio XE7 users that includes both a blue and a dark style. The blue style is the default Yosemite style, and the dark style is designed for Yosemite’s dark (graphite) mode.

You can get both styles and the sample project by going to: http://cc.embarcadero.com/item/30025

Here are the steps to add Yosemite styling support to your project:

1. Place TStyleBook onto the Master view
2. Set TStyleBook.Name = "YosemiteStyleBook"
3. Load Yosemite.fsf or YosemiteDark.fsf to YosemiteStyleBook
4. Use the TForm.OnCreate event and add this code:

procedure TForm1.FormCreate(Sender: TObject);
begin
  {$IFDEF MACOS}
  if (TOSVersion.Major = 10) and (TOSVersion.Minor = 10) then
     Form1.StyleBook := YosemiteStyleBook;
  {$ENDIF}
end;


posted @ Fri, 17 Oct 2014 19:50:26 +0000 by sarinadupont


iOS 8 App Store Hotfix

Hi,

A couple of days ago we released a hotfix for submitting iOS 8 applications to the Apple App Store.

This RAD Studio hotfix enables deployment of Delphi and C++ iOS applications to the Apple App Store.

It applies to RAD Studio XE5, XE6 and XE7.

You can get the hofix by going to: http://cc.embarcadero.com/item/30022

Regards,

Sarina


posted @ Fri, 17 Oct 2014 17:40:36 +0000 by sarinadupont


Mobile User Interface Design: Home Screen Navigation

Written by Sarina D.

When it comes to mobile UI design, there are several key design paradigms. I covered the different mobile UI design patterns in my recent C++ Mobile Day session.

App Home Screen Navigation Key Features
• Images arranged in a grid like layout
• Images can be annotated with text
• May span over multiple screens (you can use a TTabControl for that)
To create the user interface shown in the screenshot above, you will first need to create a new mobile application. Add a TToolbar, align it to the Top, and parent a TLabel to it. Next, drop a TGridPanelLayout component onto your form and align it to the client. By default, two columns (0,1) and two rows (0,1) are set up.
Note: Since you are going to set up event handlers for each graphic on the home screen, I would recommend adding a TTabControl (aligned to Client) with 11 invisible tabs (TabPosition = tpNone). Then add the Toolbar and GridPanelLayout components to Tab1, and link to each of the tabs when the user taps on one of the 10 home screen graphics by setting up on-click events for each graphic.

Define the width of each column, i.e. 50% to have even spacing across both columns.
For this sample application, we want to have a total of two columns with five rows. To add a row, right click on ‘RowCollection’ and add an item.
Each of the five rows should have a value of 20% to ensure even spacing across our grid.
Next, drop a total of 10 TImageControls onto your form. You could also use TImage if you want to load different images for different device resolutions (i.e. 1x resolution image, 2x resolution image etc.).Multi-select (shift select) all TImageControls in the Structure pane and change the width and height to 75 px. This ensures spacing between each of the graphics.
Select each TImageControl and select the row and column you want it to be displayed.

Last step is to add the images you want to display and setup on-click events for each graphic to navigate the user to the related content.


posted @ Mon, 13 Oct 2014 23:43:04 +0000 by J T


Creating a Navigation Drawer for your Mobile Application

Written by Sarina D.

Recently, I covered key menu styles that are used in mobile applications today. Drawer menus are very popular, as they allow you to take advantage of more screen real estate when building your app. The main application menu is hidden by default, and invoked by tapping on a menu item or swiping left/right. This type of UI can be seen in many popular mobile applications, including Facebook, Youtube and Gmail.

In this tutorial, I will cover the steps needed to create a navigation drawer for your Appmethod mobile apps.

Many thanks to Malcolm Groves who created the drawer sample that I am using and expanding for this blog post. You can download the project source from his Github repository.

This application consists of 2 layouts. One layout containing the drawer content, and one containing the main application content.

In this case, my left menu, the drawer menu, includes a top aligned toolbar with a parented label, and a client aligned Listbox with several menu items in it. The idea here is that the drawer menu contains all the main application navigation, including quick links to my account profile, app settings etc.

For demo purposes, I dropped a TListView component onto my form, parented it to lytMain and set its alignment property to alClient. I also placed a TPrototypeBindsource component onto my form to fill my TListview with some sample data. Right-clicking on the PrototypeBindSource component provides you with sample data that you can then easily bind into your Listview using the LiveBindings Designer. I also parented a TLine to lytMain to make the division between the main layout and navigation drawer more visible. You could also apply a style or use a separate background color.

If you double click on the Actionlist component on the form, you can see that we setup a custom action. We are leveraging that action for opening/closing the drawer when the user clicks on the drawer menu (three line) button or when they swipe across the main menu layout.

procedure TfrmMain.actOpenDrawerExecute(Sender: TObject);
begin
  DrawerVisible := not DrawerVisible;
end;

procedure TfrmMain.actOpenDrawerUpdate(Sender: TObject);
begin
  actOpenDrawer.Visible := not (IsPad and IsLandscape);
end;

On the form, we also placed the Gesture Manager component since we want the user to be able to swipe across the screen to show/hide the navigation drawer. In the project source, you will see that the touch gestures are setup for the toolbar parented to lytMain. In my demo, I removed the gesture that was setup on the toolbar and instead assigned the touch gesture to lytMain.

You can find the source code for this project in Malcolm Groves’ Github repository. Below is a quick video that shows my running application.


posted @ Mon, 13 Oct 2014 23:42:32 +0000 by J T


Pull to Refresh on iOS

Written by Sarina D.

Today, I thought I would show you a simple example of how to implement pull to refresh for TListView. This example doesn’t use a data source, but instead just adds 20 items to my list every time I try to refresh it. I am currently using one timer with this sample, but you could add another one for when the data is done loading to hide the animation at that time.

This sample uses the following 3 components:

TListView
TAniIndicator
TTimer

For Timer1, I set the Interval design time property to 30. ListView1 is aligned to the client. AniIndicator1 is positioned at the top and centered on my form.

refresh

Below is the entire code of my application:

    unit PullToRefresh;

    interface

    uses
    System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
    FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
    FMX.ListView.Types, Data.Bind.GenData, Fmx.Bind.GenData, System.Rtti,
    System.Bindings.Outputs, Fmx.Bind.Editors, Data.Bind.EngExt,
    Fmx.Bind.DBEngExt, FMX.StdCtrls, Data.Bind.Components, Data.Bind.ObjectScope,
    FMX.ListView;

    type
    TForm36 = class(TForm)
    ListView1: TListView;
    Timer1: TTimer;
    AniIndicator1: TAniIndicator;
    procedure ListView1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Single);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    private
    { Private declarations }
    Myposition : Integer;
    Updating : Boolean;
    public
    { Public declarations }
    end;

    var
    Form36: TForm36;

    implementation

    {$R *.fmx}
    var
    LItem: TListViewItem;
    LList : TListView;
    I: Integer;

    procedure TForm36.FormCreate(Sender: TObject);
    begin
    Timer1.Enabled := true;
    begin
    LItem := ListView1.Items.Add;
    LItem.Text := ‘Pull to refresh list & add 20 items’;
    end;
    end;

    procedure TForm36.ListView1MouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Single);
    begin
    if not Updating and (ListView1.ScrollViewPos < -40) then
    begin
    Updating := true;
    AniIndicator1.Visible := True;
    AniIndicator1.Enabled := true;
    for
    I := Myposition + 1 to Myposition +20 do
    begin
    LItem := ListView1.Items.Add;
    LItem.Text := Format(’Text %d’, [I]);
    end;
    inc(myposition, 20);
    end;
    end;

    procedure TForm36.Timer1Timer(Sender: TObject);
    begin
    if ListView1.ScrollViewPos >-40 then
    begin
    AniIndicator1.Visible := false;
    Updating := false;
    end;
    end;
    end.

posted @ Mon, 13 Oct 2014 23:41:51 +0000 by J T


Deploying and accessing local files on iOS and Android

Written by David I.

This tutorial shows you how to deploy and access files and/or folders containing files in your iOS and Android apps. To accomplish this, you create a new mobile app, a few lines of Object Pascal code, use the Project | Deployment menu item and use the System.IOUtils unit’s TPath.GetDocumentsPath method and PathDelim in your code.

The steps to create the mobile app

1) use the File->New ->Mobile Application main menu item and select the "Blank Application" project template.

2) On the form, add 1 TButton, 2 TLabel and 1 TImage components.

3) Double click on the TButton component to create the Button’s OnClick event handler.

4) Add a "uses System.IOUtils;" just below the {$R *.fmx} in the implementation section.

5) Add the following Object Pascal code in the event handler to display the documents path and to load the bitmap from the documents folder:

procedure TForm1.Button1Click(Sender: TObject);
begin
  DocPathLabel.Text := TPath.GetDocumentsPath;
  Image1.Bitmap.LoadFromFile(TPath.GetDocumentsPath + PathDelim + 'davidi_tiedye.jpg');
end;

6) In the Project Manager, select each of the Android and iOS Target Platforms to active each one to build an application for each one.

7) Use the Project | Deployment menu item and select the Android and the iOS Device platforms and for each one click the "Add Files" button in the local toolbar to select the bitmap file to deploy with the app and set the Remote Path column value for each platform.

  • For Android, set the Remote Path to .\assets\internal
  • For iOS, set the Remote Path to StartUp\Documents

Here are screen shots of each of the resulting Deployment settings:

8) Compile, Deploy and Run the app for Android (I used my Samsung Galaxy S4) and iOS (I used my iPhone 4s)

You can read additional information about mobile application development and deployment on the Appmethod DocWiki using the following URLs:


posted @ Mon, 13 Oct 2014 23:40:11 +0000 by J T


Create an Action Bar with Overflow Menu on Android

Written by David I.

On Android, an Action Bar is a top (or top and bottom aligned) toolbar that is segmented into 4 key functional areas. One of those functional areas is an Overflow menu. The ‘Overflow’ popup menu is commonly used for additional menu items on Android and accessed via the Action Bar. An Overflow menu is designed to give the application user quick access to additional/less often used features that are otherwise not accessible via more prominent menu items. Using Appmethod, you can easily implement an Overflow menu through the use of TListBox.

Here are the step-by-step instructions.

In this example we using the following 4 components:

  • TToolbar
    • Alignment: alTop
  • TSpeedButton
    • Alignment: alRight
    • StyleLookUp: detailstoolbutton
    • Margin, Right: 5 (this is set in case you want to carry this UI over to iOS as well, to account for both bordered (iOS6) and non-bordered (iOS7) button sizes
  • TListBox with several items
    • Each of the four listboxitems has a bitmap and text defined via the ItemData property
    • Visibility has been set to False
    • Height has been set to 176px (to show the listbox border right below the last listbox item)
    • Anchors: akTop, akRight
  • TShadowEffect
    • Parented to TListBox

ListBox Item Properties:

Note: For the Overflow menu (TListBox), set alignment to alNone. Since you are setting anchors, you don’t want to set any alignment. Position the listbox where you want it to appear on the form (make sure it is not parented to another component), and then set the following Anchors: akTop, akRight. If you rotate the devices, you will see that that the listbox will remain where you had positioned it.

TSpeedButton properties:

I used a larger (80×80px) icon for each listbox item bitmap so that the icons look nice on both lower resolution and high resolution displays. I also created the following on-click Event for my OverflowButton:

procedure TForm10.OverflowButtonClick(Sender: TObject);
begin
  OverflowMenu.Visible := not OverflowMenu.Visible;
  if OverflowMenu.Visible then
  begin
    OverflowMenu.ApplyStyleLookup;
    OverflowMenu.RealignContent;
  end;
end;

Below is a quick GIF recording of the Overflow menu in action.


posted @ Mon, 13 Oct 2014 23:39:30 +0000 by J T


Adding alphabetical sorting with A-Z headers to your lists

Written by Sarina D.

Recently, I covered key menu styles that are used in mobile applications today. Drawer menus are very popular, as they allow you to take advantage of more screen real estate when building your app. The main application menu is hidden by default, and invoked by tapping on a menu item or swiping left/right. This type of UI can be seen in many popular mobile applications, including Facebook, Youtube and Gmail.

In this tutorial, I will cover the steps needed to create a navigation drawer for your Appmethod mobile apps.

Many thanks to Malcolm Groves who created the drawer sample that I am using and expanding for this blog post. You can download the project source from his Github repository.

This application consists of 2 layouts. One layout containing the drawer content, and one containing the main application content.

In this case, my left menu, the drawer menu, includes a top aligned toolbar with a parented label, and a client aligned Listbox with several menu items in it. The idea here is that the drawer menu contains all the main application navigation, including quick links to my account profile, app settings etc.

For demo purposes, I dropped a TListView component onto my form, parented it to lytMain and set its alignment property to alClient. I also placed a TPrototypeBindsource component onto my form to fill my TListview with some sample data. Right-clicking on the PrototypeBindSource component provides you with sample data that you can then easily bind into your Listview using the LiveBindings Designer. I also parented a TLine to lytMain to make the division between the main layout and navigation drawer more visible. You could also apply a style or use a separate background color.

If you double click on the Actionlist component on the form, you can see that we setup a custom action. We are leveraging that action for opening/closing the drawer when the user clicks on the drawer menu (three line) button or when they swipe across the main menu layout.

procedure TfrmMain.actOpenDrawerExecute(Sender: TObject);
begin
  DrawerVisible := not DrawerVisible;
end;

procedure TfrmMain.actOpenDrawerUpdate(Sender: TObject);
begin
  actOpenDrawer.Visible := not (IsPad and IsLandscape);
end;

On the form, we also placed the Gesture Manager component since we want the user to be able to swipe across the screen to show/hide the navigation drawer. In the project source, you will see that the touch gestures are setup for the toolbar parented to lytMain. In my demo, I removed the gesture that was setup on the toolbar and instead assigned the touch gesture to lytMain.

You can find the source code for this project in Malcolm Groves’ Github repository. Below is a quick video that shows my running application.


posted @ Mon, 13 Oct 2014 23:38:58 +0000 by J T


Server Response from: BLOGS1