Introducing FireUI

    We recently published a series of articles on application development in FireMonkey. Then we described the key points of building the application, including creating a database, connecting data using LiveBinding technology, and deploying the application on a mobile platform. However, we did not consider in detail the nuances of creating mobile applications. This is largely due to the fact that the process of mobile development in Delphi is evolving from version to version. In particular, in the latest XE7 version of Delphi today, the new FireUI Multi-Device Designer form designer was introduced . In this article, with the help of a small example, we will examine what FireUI is and how the development methodology has changed with its appearance.



    To do this, we will create the simplest application for testing. The user is presented with a picture containing a question and several answer options. We chose the football theme (why not?).

    We can say that this is a variation of the academic problem, so in this article we will not focus on the application logic. Instead, we describe in more detail the procedure for creating the application itself.

    The most natural choice of a DBMS for mobile applications is SQLite . As a database manager, you can use the free version of SQLite Expert or any other product for working with SQLite.

    The base will contain a single table in which information about footballs will be stored. The SQL code used to create the table will look something like this:

    CREATE TABLE [Player] (
      [Id] INTEGER NOT NULL ON CONFLICT ROLLBACK PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT, 
      [PlayerName] CHAR(100) NOT NULL, 
      [TeamNumber] INTEGER, 
      [Height] INTEGER, 
      [Weight] INTEGER, 
      [Photo] BLOB, 
      [CountryId] INTEGER);
    

    Actually, in this article we use only the Id, PlayerName, and Photo fields. The rest - touched the future. We can also fill out the table with test data using SQLite Expert.

    Upon completion of the preparation of the base, we proceed directly to writing the program.

    In XE7, the approach to creating FireMonkey applications was slightly changed. Now there is no fundamental difference between the “desktop” and the mobile application. As a result, when creating the application, we select the Multi-Device Application template.



    Such a project is capable of generating executable files for any of the platforms supported by Delphi: Windows, OS X, IOS and Android. We will be able to choose a specific platform during the creation of the project, as well as customize the user interface for a specific type of device.

    As in previous versions, we can select the template for the main project form using the Select a Multi-Device Application Type dialog.



    For our application, the Tabbed template is suitable.

    First of all, configure the connection to the database using FireDAC . To do this, we sequentially place three components on the form - TFDConnection, TFDPhysSQLiteDriverLink and TFDGUIxWaitCursor. Double-clicking on the TFDConnection component calls up the connection editor. Here we should indicate the path to the database. Set LoginPrompt to False.



    To create a list of questions and answers, we will have to form two SQL queries. These queries will be called using the TFDQuery components.

    The first query will list five random players.

    SELECT * FROM Player
    ORDER BY Random()
    Limit 5
    

    Actually, this will be a list of questions.

    The second request is similar to the first with the only difference being that one of the players will not get into this list.

    SELECT * FROM Player
    WHERE Id<> :Id
    ORDER BY Random()
    Limit 4
    

    This is a list of answers that will be generated for each of the questions. The correct answer, which will be inserted manually, is excluded from the list.

    Let's take a close look at the updated workspace designer in XE7. It has its own toolbar, located just below the main one. This panel contains two buttons and two drop-down lists.



    Lists that were available in XE5 / XE6 - have been replaced by a list of styles and a
    list of views. Buttons for changing device orientation and for displaying device masks.

    The first of the lists allows you to choose how the interface will look during the development process.
    We will return to the second list a bit later. In the meantime, place the necessary controls on the form and create the desired code.

    The basic element of the TTabControl template in our application will contain three tabs that will switch sequentially during the test. Set the TabPosition property to None so that the user does not have the ability to manually switch tabs in random order.

    On the first tab, place two buttons. Approximately as shown in the figure.



    The "Exit" button closes the application.

    procedure TfMain.btnExitClick(Sender: TObject);
    begin
      Close;
    end;
    

    The “New Game” button, as you might guess, initiates the testing procedure.

    Pressing the button will process as follows:

    procedure TfMain.btnNewGameClick(Sender: TObject);
    begin
      qPlayer.Close;
      qPlayer.Open;
      // qPlayer.First;
      TrueCount := 0;
      TabControl1.TabIndex := 1;
      FormAnswers(4);
    end;
    

    After clicking on the button, the second tab will become active.



    The player’s image will be displayed on the left, and pAnswers panel on the right - a list of answer options.

    procedure TfMain.FormAnswerArray(Count, qId: integer);
    var
      aPos, i: integer;
      rb: TRadioButton;
      BlobStream: TStream;
    begin
      Randomize;
      aPos := Random(Count - 1);
      try
        BlobStream := qPlayer.CreateBlobStream(qPlayer.FieldByName('Photo'),
          TBlobStreamMode.bmRead);
        Image1.Bitmap.LoadFromStream(BlobStream);
      finally
        BlobStream.Free;
      end;
      qAnswer.Close;
      qAnswer.ParamByName('Id').AsInteger := qId;
      qAnswer.Open;
      i := 0;
      while not qAnswer.Eof do
      begin
        if i > Count then
          Break;
        try
          rb := TRadioButton.Create(pAnswers);
          rb.Position.X := 0; // Image1.Position.X +10;
          rb.Position.Y := i * 20;
          rb.Parent := pAnswers;
          pAnswers.InsertComponent(rb);
          rb.Size.Width := 250;
        finally
        end;
        if i = aPos then
        begin
          rb.Text := qPlayerPlayerName.AsString;
          rb.Tag := qPlayerId.AsInteger;
        end
        else
        begin
          rb.Text := qAnswerPlayerName.AsString;
          rb.Tag := qAnswerId.AsInteger;
          qAnswer.Next;
        end;
        Inc(i);
      end;
    end;
    procedure TfMain.ClearRadioButtons;
    var
      i, j: integer;
    begin
      for i := pAnswers.ChildrenCount - 1 downto 0 do
      begin
        if pAnswers.Children[i].ClassNameIs('TRadioButton') then
        begin
          (pAnswers.Children[i] as TRadioButton).Visible := FAlse;
          pAnswers.Children[i].Free;
        end;
      end;
      pAnswers.Repaint;
    end;
    procedure TfMain.FormAnswers(Count: integer);
    begin
      ClearRadioButtons;
      FormAnswerArray(Count, qPlayerId.AsInteger);
    end;
    

    The TrueCount variable will contain the number of correct answers. FormAnswers procedure - forms a list of answers.

    As it is not hard to guess from the code, TRadioButton components are added to the pAnswers panel, the Text property of which will have the player's last name as the value, and the Tag property - Id. In this case, the correct option is taken from the results of the first query (qPlayer), the wrong ones - from the results of the second (qAnswer). The position of the correct answer is random.

    When you click Next, the following code is called:

    procedure TfMain.btnNextClick(Sender: TObject);
    var
      answID: integer;
    begin
      answID := GetAnswerID;
      Inc(cnt);
      if answID = qPlayerId.AsInteger then
      begin
        Inc(TrueCount);
        ShowMessage('Верно');
      end
      else
      begin
        ShowMessage('Не верно');
      end;
      if qPlayer.RecNo <> 5 then
      begin
        qPlayer.Next;
        FormAnswers(4);
      end
      else
      begin
        TabControl1.TabIndex := 2;
      end;
      // if qPlayer.Eof then
      if qPlayer.RecNo = 5 then
      begin
        lResult.Text := 'Верно ' + IntToStr(TrueCount) + ' из 5';
        btnNext.Text := 'Финиш';
      end;
    end;
    

    The GetAnswerID function is implemented as follows.

    function TfMain.GetAnswerID: integer;
    var
      i, j, k: integer;
    begin
      Result := -1;
      for i := tiQuest.ChildrenCount - 1 downto 0 do
      begin
        if tiQuest.Children[i].ClassNameIs('TTabItemContent') then
        begin
          for j := tiQuest.Children[i].ChildrenCount - 1 downto 0 do
          begin
            if tiQuest.Children[i].Children[j].ClassNameIs('TPanel') then
              for k := tiQuest.Children[i].Children[j].ChildrenCount - 1 downto 0 do
                if tiQuest.Children[i].Children[j].Children[k].ClassNameIs
                  ('TRadioButton') then
                  if (tiQuest.Children[i].Children[j].Children[k] as TRadioButton).IsChecked
                  then
                  begin
                    Result := (tiQuest.Children[i].Children[j].Children[k]
                      as TRadioButton).Tag;
                    Break;
                  end;
          end;
        end;
      end;
    end;
    

    The third tab simply displays the result.

    The new methodology for building a multi-platform application in Delphi XE7 comes down to the fact that first we place all the necessary components on the form using the Master View . Then we create a special view for each platform and type of device for which we plan to generate a project. And finally, individually customize each of the views.

    Everything that we have done so far has been performed by the Master.



    If now we run the application for execution for Windows (we select the target platform, as before, in Project Manager), then the main form will look approximately the same as in the designer.



    However, now we can select the appropriate view from the list and configure it separately. Let's see how it looks in practice.

    Add a view for Windows Desktop by simply selecting it from the list and turn on the style. To do this, use the StyleBook component.



    A working application on Windows will look something like this.



    This is how the form in the disiner will look when Windows Desktop is active.



    But for the introduction of Master, no change will occur.



    It is important to understand that changes made in one of the views will not affect the others. But if you change the view of Master, then such changes will be "cross-cutting." Naturally, with this approach, there are certain limitations. In a simplified form, they boil down to the fact that all representations on the form must have the same set of components. You cannot remove a component from one view, leaving it in others. But you can safely change the properties of components independently in each of the views.

    This approach introduces some flexibility. Let's see how the FireUI mechanism will work when creating a mobile application for Android.

    First of all, we will need to transfer the base to a mobile device. To do this, we use the Deployment Manager, familiar to us from previous versions.



    As in previous versions of Delphi, we need to handle the BeforeConnection event of the FDConnection component.

    procedure TfMain.FDConnection1BeforeConnect(Sender: TObject);
    begin
    {$IFDEF ANDROID}
      FDConnection1.Params.Values['Database'] := IncludeTrailingPathDelimiter
        (System.IOUtils.TPath.GetDocumentsPath) + 'tests.db';
    {$ENDIF}
    end;
    

    After that, connect the mobile device and create the corresponding view.



    Set the style and location of controls and run the application on a mobile device. The application takes a completely different look, but, obviously, it is one and the same application.

    In addition, we need to change the BeforeConnection event handler of the FDConnection component.

    procedure TfMain.FDConnection1BeforeConnect(Sender: TObject);
    begin
      FDConnection1.Params.Values['Database'] := IncludeTrailingPathDelimiter
        (System.IOUtils.TPath.GetDocumentsPath) + 'tests.db';
    end;
    

    And in order for the application to continue to work under Windows, in the Windows Desktop view we simply will not process this event.



    So, in general terms, we saw what changes have occurred in the methodology for developing FireMonkey applications with the advent of FireUI. Now we are talking not about a “single code base” and several applications for different platforms, but actually about a single application that is built for each of the platforms.

    In the next article, we will try to analyze the "pitfalls" that can be found in applications that support different platforms. Stay with us.

    Update 1:
    Launched application on two devices

    Also popular now: