Академический Документы
Профессиональный Документы
Культура Документы
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
D E L P H I, L A Z A R U S, O X Y G E N E, S M A R T M O B I L E,
A N D P A S C AL
A N D R O I D, I O S,
R E L A T E D
M A C,
L A N G U A G E S
W I N D O W S & L I N U X
39
39
MOTION
CONTENTS
Articles
Book Review By Jim Duff
Getting Started with Lazarus and Free Pascal
Learning By Doing Author: Menkaura Abiola-Ellison
Page 5
MOTION
BLAISE
BLAISE PASCAL
PASCAL MAGAZINE
MAGAZINE
Advertisers
Barnsten 9
BetterOffice 29
BLAISE PASCAL MAGAZINE 4
Cary Jensen (Jensen Data Sytems) 19
Components 4 Developers 40
Computer Math & Games 30
Daniel Teti 10
FAST REPORT 20/21
Raize Software 35
2 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Alexander Alexeev Peter Bijlsma Michal Van Canneyt,
www.alexander@blaisepascal.ru -Editor peter @ blaisepascal.eu michael @ freepascal.org
Alexander @ blaisepascal.ru
Please note: extra space characters have been deliberately added around the @ symbol in
these email addresses, which need to be removed if you use them.
editor @ blaisepascal.eu
Authors in alphabethical order
A Alexander Alexeev L Wagner R. Landgraf, Sergey Lyubeznyy
B Peter Bijlsma, K Max Kleiner
C Michal Van Canneyt, Marco Cant, M Kim Madsen, Felipe Monteiro de Cavalho
D David Dirkse, Daniele Teti N Jeremy North,
F Bruno Fierens O Tim Opsteeg, Inoussa Ouedraogo
G Primo Gabrijeli, P Howard Page-Clark,
H Fikret Hasovic S Rik Smit, Bob Swart,
J Cary Jensen
Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68
News and Press Releases email only to editor@blaisepascal.eu
Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit,
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property of their respective owners.
Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions.
If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.
Subscriptions ( 2013 prices )
1: Printed version: subscription 65.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription 45.-- Incl. VAT 21% (including code, programs and download magazine)
Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to office@blaisepascal.eu
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal
Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)
Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
office@blaisepascal.eu
Copyright notice
All material published in Blaise Pascal is copyright SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
USING LAZARUS
LEARN TO PROGRAM
25
HARDCOVER POCKET ELECTRONIC POCKET SALE
RPi-B+ Raspberry Pi 2
16 GB 1421
Vogelaar Electronics
RPi ArchVE
RPi-ArchVE
Complete IO
6 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Book Review: Getting Started with Lazarus and Free Pascal (Continuation 2)
SUMMARY The Book
As provided at the start of this review, the author is Getting Started with Lazarus
an experienced Pascal person, and his book has been and Free Pascal
introduced to attract new users for Lazarus and Free Learning By Doing
Pascal. Given the ongoing changes and introductions Author:
of new types of hardware and software, it is relevant Menkaura Abiola-Ellison
to attract new (young and old) starters into this
positive and productive set of development tools. First published January 2015
Published by Mka Publishing Communications
CONTENTS
House
Preface
290 Moston Lane Manchester
Why use Lazarus with Free Pascal?
M40 9WB
What this book covers
ISBN 13: 978-1507632529
What you need for this book
Reader feedback
Review by
Who this book is for
James D (Jim) Duff
Conventions
Long time programmer and developer.
Errata
1 Introducing Lazarus with Free Pascal
Reader feedback
2 Setting up Lazarus
Feedback from readers is always welcome. Let us
3 Command-line Programs
know what you think about this book, what you like
4 Working with numbers
or may have disliked.
5 Some input, some logic
6 Arrays and other topics
To send us general feedback
7 OOP Fundamentals
simply send an e-mail to
8 Lazarus IDE mka.feedback@gmail.com,
9 A New Look and mention the book title.
10 OOP in action
11 Using constructors and destructors
12 Dealing with Errors and Exception Handling
13 Lazarus Packages
14 Lazarus Component Library (LCL)
15 Building An Application with LCL
16 Graphics, 2D, 3D and Animation
17 File Handling
18 Dataset handling
19 Database Introduction
20 Lazarus and the Web
21 Debugging the Code
22 Documenting the code
23 Lazarus and Other Devices, Pi, Android,
Smartphones,
and Embedded systems
24 Method glossary
25 What Next
26 Index
8 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
UPGRADE OR BUY
RAD STUDIO XE7,
DELPHI XE7 OR
C++BUILDER XE7
AND GET THE
NEXT MAJOR RELEASE
FOR FREE!
M
TO
AZING
PASCAL
DI
MAGAZINE
E
http://www.blaisepascal.eu/index.php?actie=./subscribers/UK_Book_Department
ClientDataSets and FDMemTables Compared: Part 1
starter expert by Cary Jensen
Delphi XE 7
In addition, the ClientDataSet can be configured to
For more than a year now I have been writing
communicate SQL parameters, as well as entire SQL
about FireDAC, Delphi's newest data access
framework. During this time I have focused on a statements, from the DataSnap client to the DataSnap
variety of FireDAC features, including Local SQL, server, where its DataSetProvider can bind those
the SQL preprocessor, array DML, FDMemTables, parameters and SQL statements to server-side
and much more. TDataSets.
FDMemTables, you might recall, are in- The FDMemTable is also an in-memory dataset,
memory datasets, similar to Delphi's and it is included in the FireDAC data access
ClientDataSets. Since I am considered an expert framework that ships with Delphi since XE3. It was
on ClientDataSets, having published two books
originally designed as a general in-memory dataset,
on the subject, there is one question that I've
been asked over and over. "Can I replace the
with particular attention to interaction with other
existing ClientDataSets in my applications with FireDAC datasets, such as FireDAC's FDQuery and
FDMemTables?" FDStoredProc components.
Unlike ClientDataSets, which, when paired with
For some developers the answer is yes. But for most a DataSetProvider, can load its data from any
developers the answer is no, at least not yet. While TDataSet descendant, FDMemTables are currently
FDMemTables can do many of the things that designed to work primarily with FireDAC data sets.
ClientDataSets can do, they cannot do everything. If If you are using the FireDAC TDataSets, such as
your applications use ClientDataSets only for those FDQuery, you will likely use FDMemTables only
things that an FDMemTable can do today, then the when needing an in-memory dataset, as many of the
answer is yes. And, since FDMemTables are the features supported by both ClientDataSets and
future of in-memory tables in Delphi, you can go FDMemTables can be found in the other FireDAC
ahead and replace your ClientDataSets with DataSets. These features include support for cached
FDMemTables as time permits. On the other hand, if updates, dataset persistence, aggregates and group
you are using ClientDataSets for features that are not state, and filter navigation.
currently supported by FDMemTables, replacing In fact, it is through an FDMemTable that
those ClientDataSets at this time will result in a FireDAC data sets such as FDQuery get their
potentially significant loss of features. advanced features. Specifically, internally an
This brings me to the topic of this, and the next, FDQuery engages the services of an FDMemTable, an
article in my database series. In these next two FDTableAdapter, and an FDCommand.
installments, I am going to examine the similarities
and differences between ClientDataSets and Similarities
FDMemTables. Hopefully this will give you the ClientDataSets and FDMemTables are cross-
information you need before you start replacing your platform, in-memory tables that descend from
ClientDataSets with FDMemTables, or at least TDataSet.
understand which of the ClientDataSets you are Features supported by both ClientDataSets and
currently using can be replaced. FDMemTables include:
Before I continue, let me make one thing clear. Data and change cache persistence
While I am a fan of ClientDataSets, I understand that Support for XML
FDMemTables are superior in a number of ways. As Cached updates
a result, when the time comes that FDMemTables Cloned cursors
support all of the capabilities we require from Nested data sets
ClientDataSets, FDMemTables will be preferred. InternalCalc and Aggregate persistent fields
Until that time, you will want to use ClientDataSets Cross platform compilation by all of RAD
for those features not supported by FDMemTables, Studio's compilers
and use FDMemTables when you have a choice.
OK. Enough said. Let's get to it. Because of their common TDataSet ancestor,
ClientDataSets and FDMemTables share a large
Because the ClientDataSet was originally designed number of methods through inheritance. There are
for use with MIDAS (the multi-tier framework that also a large number of properties and methods that
became DataSnap) it supports a number of features they share, but which are not inherited. These shared
that are of particular value when used in a features provide a fair amount of API compatibility. I
distributed environment. These include being able to say "fair amount" because, though there are a number
transfer sophisticated master-detail relationships, as of properties and methods with the same names, in
well as arbitrary binary data, between the DataSnap some cases there are significant differences in how
server and client. they operate. I will focus more on the differences in
the following section.
Issue Nr 1 2015 BLAISE PASCAL MAGAZINE 11
ClientDataSets and FDMemTables Compared: Part 1 (Continuation 1)
While these non-TDataSet properties and methods cached updates, nested datasets, DataSnap, and
give ClientDataSets and FDMemTables similar finally, a collection of differences that I'll call
interfaces, they cannot be treated polymorphically miscellaneous.
with respect to these properties and methods. This is In the following sections I will take a look at
because these properties and methods are not design time operations, record persistence, and query
inherited from a common ancestor, and are not the configuration. I will following this up in the next
result of a common interface implementation. issue of Blaise Pascal Magazine with a look at cached
Properties shared by ClientDataSets and updates, nested datasets, DataSnap, and the
FDMemTables, but not inherited from TDataSet, miscellaneous topics.
include Aggregates, AggregatesActive,
ChangeCount, CommandText, Data, Delta, Design Time Operations
FetchOnDemand, FileName, IndexDefs, In this section I am going to address features of
IndexFieldNames, LogChanges, MasterFields, ClientDataSets and FDMemTables that you can
MasterSource, PacketRecords, RecordStatus, access at design time. Let's begin with creating the in-
SavePoint, StatusFilter, and XMLData. Methods that memory structure from within Delphi's IDE.
fall into this category (found in both classes but not These differences can be demonstrated using the
inherited from TDataSet) include ApplyUpdates, project named DesignTime. This project includes
CancelUpdates, CloneCursor, three memo fields that describe the areas of
GetOptionalParameters, LoadFromFile, difference and provide specifics about the
LoadFromStream, MergeChangeLog, SaveToFile, ClientDataSet and FDMemTable implementations.
SaveToStream, and SetOptionalParameters. There On the right-hand side of the project's main form
may be some additional properties and methods reside a ClientDataSet and an FDMemTable, both
supported by both ClientDataSets and FDMemTables hooked up to a DBGrid through a DataSource. These
that are not inherited from TDataSet and which are components can be used to demonstrate the specific
not listed here. differences. The main form of the DesignTime project
is shown in Figure 1.
Differences
There are a number of areas where FDMemTables
and ClientDataSets differ. These include design time
operations, record persistence, query configuration,
Defining Structure
Whether you are using ClientDataSets or For an FDMemTable, you right-click the
FDMemTables, you define the structure (the fields FDMemTable and select Assign DataSet from the
and their data types) using either FieldDefs or FDMemTable's component editor. The
TFields. To use FieldDefs, you click the ellipsis FDMemTable's component editor will display a
button on the FieldDefs property in the Object dialog box listing all of the data sets that are visible to
Inspector, and then use the FieldDefs collection the FDMemTable. Complete the process by selecting
editor to define individual FieldDef definitions. To one of the data sets within scope of the FDMemTable.
use the Fields Editor, right-click the ClientDataSet or Loading a Previously Save File
FDMemTable and select Fields Editor. From the Loading data from a previously saved file is also very
Fields Editor, right-click and select New field (or simple for both ClientDataSets and FDMemTables.
press Ctrl-N). For ClientDataSets, point the FileName property to a
Once you have defined one FieldDef or TField for previously saved ClientDataSet file and then set
each column you want in the subsequent structure, Active to True. For an FDMemTable, set the
right-click the ClientDataSet and select Create FDMemTable's ResourceOptions.PersistentFileName
DataSet from the ClientDataSet's component editor. property to a previously saved FDMemTable, after
With an FDMemTable, simply set Active to True. which you set Active to True.
You clear data from both ClientDataSets and
Creating Indexes
FDMemTables using the same technique. Right-click
Creating persistent indexes at design time is pretty
the component and select Clear Data. You might also
much the same for both ClientDataSets and
have to manually delete any FieldDefs or persistent
FDMemTables. Using the IndexDefs property editor
TFields before using the ClientDataSet or
you add one IndexDef for each index that you want
FDMemTable with some other data structure.
to define. The one difference is that FDMemTables
support expression indexes, and these can use
Loading From Another DataSet
functions from FireDAC's SQL pre-processor.
To load a ClientDataSet from another data set, right-
ClientDataSets do not support expression indexes.
click the ClientDataSet and select Assign Local Data
Creating temporary indexes is also nearly
from the ClientDataSet's component editor. Select the
identical. Set the IndexFieldNames property to a
data set whose data you want to load into the
comma-separated list of the fields on which to
ClientDataSet from the displayed dialog box.
index. The one major difference here is that,
when using an FDMemTable, you can identify
one or more of the field names in the comma-
separated list to indicate you want the field
sorted in ascending order (the default) or
descending order. Follow the field name with a
colon and an A to indicate an ascending sort
order, and a colon and a D to indicate a
descending sort order. (The documentation
states that you can use an N to indicate a case-
insensitive sort, but that is the default. It does not
appear that there is a character that indicates a
case-sensitive sort order.)
As a result, assigning the following string to
an FDMemTable's IndexFieldNames property
will sort the data first by PartNo, in ascending
order, and then by Quantity in descending order.
PARTNO;QUANTITY:D
Figure 2. An FDMemTable is filtered using an expression that includes a FireDAC SQL preprocessor function
FireDAC Expressions
The final difference in design time usage is related to I've listed the use of sort order in temporary indexes
expressions. Expressions are used in filters, index and FireDAC expression engine functions in filters,
expressions, aggregates, and constraints. expression indexes, and constraints, in this section on
ClientDataSets can only use the standard design time usage. Obviously, these features are also
ClientDataSet special filter expressions, which available at runtime.
includes a collection of string, date/time, and Record Persistence
miscellaneous functions. FDMemTables, by Both ClientDataSets and FDMemTables can write
comparison, can use functions from the FireDAC their data to a file or stream. However, these
expression engine in these expressions. This is the operations are not compatible. Specifically, you
same expression engine used by the FireDAC SQL cannot load an FDMemTable with a previously
pre-processor. persisted ClientDataSet, and visa versa. While both
ClientDataSets and FDMemTables support both
The DesignTime project contains a DataModule that binary and XML-based formats, they are
includes an FDQuery. Here is the SQL statement incompatible. Importantly, FDMemTables support a
associated with that query: third format, JSON (JavaScript Object Notation), a
more current and popular format.
SELECT * FROM Customer
Record persistence is demonstrated in the project
If the FDMemTable on the main form of this project named RecordPersistence. The running main form is
is loaded from this query, the following value can be shown in Figure 3.
assigned to the Filter property.
Figure 7. The QueryConfiguration projects demonstrates You can download the sample projects described in
runtime query configuration using ClientDataSets and
this article using the following URL:
FDMemTables
http://www.jensendatasystems.com/CDSFDM
emTable1.zip
When an FDMemTable is not in the cached updates
mode, and it is hooked up through an Summary
FDTableAdapter and FDCommand, it will apply ClientDataSets and FDMemTables share many
similarities, but a number of significant differences
changes to the underlying database on a record-by- as well. In this article I have discussed the
record based. To achieve the same effect using a similarities and have started to outline the
ClientDataSet, you would have to add a call to differences. In the next issue of Blaise Pascal I will
take a look at the differences between
ApplyUpdates to each of the following event ClientDataSets and FDMemTables with respect to
handlers: AfterPost, AfterInsert, and AfterDelete. cached updates, nested datasets, DataSnap, and a
few additional areas. I will wrap up that article with
This default behavior of an FDMemTable, of being some final conclusions about where and when to
able to write changes to the underlying database use ClientDataSets and FDMemTables.
automatically, will occur even with the configuration
shown in the preceding code. Specifically, even About the Author
though the FDTableAdapter has only one Cary Jensen is Chief Technology Officer of Jensen
Data Systems. Since 1988 he has built and deployed
FDCommand, and that FDCommand holds only a database applications in a wide range of industries, and
is available for development, consulting, and training.
SELECT statement, Specifically, even though the Cary is an Embarcadero MVP, a best selling author of
FDTableAdapter has only one FDCommand, and more than 20 books on software development, and
that FDCommand holds only a SELECT statement, holds a Ph.D. in Engineering Psychology, specializing in
human-computer interaction. He is also co-presenter
that statement can be used by the FDTableAdapter to with Ray Konopka on this year's Delphi Developer Days
create DELETE, INSERT and UPDATE SQL 2015 tour.
statements at runtime. This is possible because the
SELECT statement is homogenous, selecting data Visit
http://www.DelphiDeveloperDays.com
from a single table without employing subqueries or to learn more.
calculations.
Groups
A table with the name of groups.
Pupil
A table with the names of students (pupils).
PupilGroup
A table which keeps track of which student is
part of which group: we assume that a studentl
can be part of more than one group.
presence
a table that notes who is present today.
22 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Accessing Preferences and Databases in Android FPC (continuation 1)
<?xml version="1.0" encoding="utf-8"?> TAbsenteeDBHelper =
<manifest class(ADSSqliteOpenHelper)
xmlns:android=" public
http://schemas.android.com/apk/res/android" constructor Create(aContext: ACContext);
package="eu.blaisepascal.absenteeapp" procedure onCreate(
android:versionCode="1" aDatabase: ADSSqliteDatabase);
android:versionName="1.0"> override;
<uses-sdk procedure onUpgrade(
android:minSdkVersion="9" aDatabase: ADSSqliteDatabase;
android:targetSdkVersion="9" /> aOldVersion,
<application android:label="@string/app_name" aNewVersion: jint); override;
android:icon="@drawable/icon"> procedure onOpen(aDatabase: ADSSqliteDatabase);
<activity android:name=".TGroupActivity" override;
android:label="@string/app_name"> end;
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="TPupilsActivity"/>
<activity android:name="TAbsenteePreferenceActivity"/>
</application>
</manifest>
The format of this manifest file has been discussed Some of these methods have an aDatabase parameter
in the previous article, it will not be repeated here of type ADSSqliteDatabase: This is because the
ADSSqliteOpenHelper is a helper class used in
A database back end database life cycle management that does not offer
The list of groups and students in the groups and
any real database functionality. Instead, the
presence records are stored in an SQLite database on
ADSSqliteDatabase class presents the actual
the device. This database must be created by the app
interface to an SQLite database. It offers methods
if it does not yet exist: It cannot be stored in the
to run queries, and retrieve data using cursors.
resources of the application.
When a new database must be created, the
Luckily, Android offers an API which takes care of
OnCreate method is called. It executes the
creating (and even updating) a database:
necessary SQL statements to create the database.
This functionality is present in a class called
This is done using the ADSSqliteDatabase
ADSSqliteOpenHelper.
interface, which offers an ExecSQL Method.
An application which needs to use SQLite databases
should create a descendant of this class and override The ExecSQL method gets an SQL statement as a
some methods: parameter, which it executes, much like TSQLQuery
in FPC.
onCreate procedure TAbsenteeDBHelper.onCreate(aDatabase:
ADSSqliteDatabase);
this method must create an empty database by
executing some DDL SQL statements. begin
OnUpgrade aDatabase.execSQL(CreateTableGroup);
Each database created by the Android SQLite helper aDatabase.execSQL(CreateTablePupil);
API gets a version number (an integer). If a database aDatabase.execSQL(CreateTablePupilGroup);
aDatabase.execSQL(CreateTablepresence);
must be upgraded, this method is called. The current end;
and new version are passed to this routine, and it
should execute all SQL statements needed to update SQL statements executed like this must not return a
the database. result. The 4 SQL statements that create the absentee
onOpen database are specified in 4 string constants, which
This method is called whenever the database is the interested reader can check in the sources.
opened. Since the application needs some data, some sample
Create The constructor of this class. It must set the data is inserted in the tables. In a real-world
expected current database version. This version will application, this data would be fetched from a
be checked against the version of an existing central administrative system. Here we simply create
database, and will call 'OnUpgrade' if they do not some sample data:
match.For the absentee application, the helper class is
called TAbsenteeDBHelper.
It overrides the above 4 methods:
Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
COMPONENTS
DEVELOPERS 4 23
Accessing Preferences and Databases in Android FPC (continuation 2)
// Create Groups The main activity
For I:=1 to 6 do The Group activity is the main activity.
begin
n:=JLLong.Create(i).toString;
It displays a simple list of group names (fetched from
Q:='insert into Group (gr_name) values the database), from which one must be selected.
(''Group '+n+'A'')'; Because such an activity is very common, Android
aDatabase.ExecSQL(Q); offers a ready-made list activity class:
Q:='insert into Group (gr_name) values
AAListActivity.
(''Group '+n+'B'')';
aDatabase.ExecSQL(Q); The groups activity will therefor be a descendent of
end; this list activity. It is defined as follows:
TGroupActivity = class(AAListActivity,
Pupils are created and linked to the groups with
AWAdapterView.InnerOnItemClickListener)
similar statements. protected
The other methods of the ADSSqliteOpenHelper FGroupsDescending : Boolean;
subclass are empty, except the Create method: FPupilsFirstName : Boolean;
fAdapter: AWSimpleCursorAdapter;
constructor TAbsenteeDBHelper.Create fDataHelper: TAbsenteeDBHelper;
(aContext: ACContext); fSelectedID: jlong;
begin procedure FillItems(isRefresh: Boolean);
inherited Create(aContext,'absentees', Nil, 1); public
end; procedure onCreate(
The second argument is the name of the database. No savedInstanceState: AOBundle); override;
Function onCreateOptionsMenu(
path should be specified: The Android API stores all
AMenu: AVMenu) : boolean ; override;
databases in a default, application-specific, location. Function onOptionsItemSelected(
The last argument is the version. AItem: AVMenuItem) : Boolean; override;
The Android API will check for the existence of this procedure onItemClick(
database: if it does not exist, it will call onCreate to aParent: AWAdapterView;
aView: AVView;
create it. If it exists, but has the wrong version, the aPosition: jint; aID: jlong);
'onUpgrade' method will be called instead. end;
The ExecSQL call cannot be used to run a select
The declaration contains 2 booleans which we'll use to
query, since it cannot handle a result set. To handle a
store the preference values. The
result set, the rawQuery method of the
AWAdapterView.InnerOnItemClickListener
ADSSqliteDatabase interface must be used.
interface is needed to respond to a click event from the
To get an instance of the ADSSqliteDatabase interface
list. The Group Activity is also the main activity of the
in a TAbsenteeDBHelper method, the
application, and the preferences dialog should also be
getWritableDatabase method must be used: this
launched from this. That means that we must create
ensures that an instance of ADSSqliteDatabase is
and populate the options menu. We've seen how to do
returned which can write to the database. Using this
this in the previous article:
we can now write a method that returns the names of
all the groups, sorted either ascending or descending: function TGroupActivity.onCreateOptionsMenu(
AMenu: AVMenu): boolean;
function TAbsenteeDBHelper.GetAllGroups
(sortdesc : Boolean): ADCursor; begin
var inherited;
query: String; AMenu.add(0,AVMenu.FIRST,0,R.strings.Preferences);
begin Result:=True;
query := 'select gr_id as _id, gr_name from end;
Groups order by gr_name ';
if SortDesc Getting preferences
then query := query + ' desc' Android supports saving and retrieving application
else query := query + ' asc'; preferences in a simple and straightforward way,
Result := using the PreferenceManager class. The default
getWritableDatabase.rawQuery(query, Nil);
way is to save preferences in a standardly named file
end;
in a standard directory, using XML:
Since the result is a read-only record set, we could /data/data/eu.blaisepascal.absenteeapp/
have used getReadableDatabase as well: shared_prefs/
it returns an instance which is able to read from the eu.blaisepascal.absenteeapp_preferences.xml
database, but can not necessarily write to it. The use of XML is hidden in the API, all the user of
The TAbsenteeDBHelper class has some other the API needs to know is how to read or write
methods to retrieve a list of students and set the settings.
presence or absence of a pupil, but these methods
are similar to the methods above.
24 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Accessing Preferences and Databases in Android FPC (continuation 3)
The PreferenceManager class has a function The FillItems method of the
getDefaultSharedPreferences which returns an TGroupActivity call fills the listview
interface of type ACSharedPreferences that can with items.
be used to manage the preferences. A list in Android is - just as in Gtk or Qt, but unlike
It has the functions one would expect from an API to as in windows - just a container widget: it displays a
manage preferences: list of other widgets, and simply scrolls these
function getString(para1: JLString; widgets on the display. Even simple text lists are
para2: JLString): JLString; rendered using TextView widgets. From this it
function getStringSet(para1: JLString; follows that we must tell the list view which widgets
para2: JUSet): JUSet; it needs to load and display. This is done using a
function getInt(para1: JLString;
para2: jint): jint;
layout: each widget is created from a layout, and for
function getLong(para1: JLString; our list of groups, this is the layout we'll use:
para2: jlong): jlong; <?xml version="1.0" encoding="utf-8"?>
function getFloat(para1: JLString; <LinearLayout
para2: jfloat): jfloat; xmlns:android=
function getBoolean(para1: JLString; http://schemas.android.com/apk/res/android"
para2: jboolean): jboolean; android:layout_width="fill_parent"
function contains(para1: JLString): jboolean; android:layout_height="fill_parent"
android:weightSum="100"
These calls are not very special, in fact they resemble android:orientation="horizontal" >
the methods found in the TIniFile class. <TextView
android:id="@+id/group_name"
android:layout_width="fill_parent"
Creating the Group activity android:layout_height="wrap_content"
When the group activity starts, it must do 2 things: android:layout_weight="30"
get the preferences, and connect to (or create) the android:padding="10dip"
database connection to get the group names. All this android:textSize="16sp" >
must be done in the onCreate method of the </TextView>
</LinearLayout>
activity, which can look like this:
This layout describes 1 item in the listview. In the
Const example above, it is a linear layout, containing
PrefGroups = 'GroupsDescending'; 1 TextView widget. The values of the various
PrefPupils = 'PupilsFirstName';
attributes are mostly self-explaining, and most of
procedure TGroupActivity. them have been discussed in the previous article.
onCreate(savedInstanceState: AOBundle); The widget has an ID associated with it: this is
specified by the android:id attribute: The value
Var "@+id/group_name" tells the resource packager
Prefs : ACSharedPreferences;
that it must create an ID that will identify the widget
begin instance - relative to its parent.
inherited onCreate(savedInstanceState); The value of this ID is written to the resource file and
setTitle(R.strings.name_groups); can be used to designate the widget in code,
Prefs:=APPreferenceManager.
getDefaultSharedPreferences(GetBaseContext( )); we'll get back to this later.
FGroupsDescending:=Prefs.getBoolean(PrefGroups,False); A listview gets its items from a
FPupilsFirstName:=Prefs.getBoolean(PrefPupils,False); ListAdapter class. This is an abstract
fDataHelper := TAbsenteeDBHelper.Create(Self); class from which descendants can be made. One of
FillItems(False);
these descendants is the AWArrayAdaptor class,
getListView.setOnItemClickListener(Self);
end which simply takes an array of strings (or Java objects)
and converts each item in the array to a listview
The code starts by setting the title of the activity: widget. That corresponds to the default use of a
since there is no resource file associated with the stringlist in Delphi or Lazarus.
activity, it must be set manually. It then reads the A second class, called
preferences using the preference manager: this is AWSimpleCursorAdapter, takes a database
done prior to creating the database. When the query result cursor and creates an item for each
preferences have been read, the database helper is record in the database query result: this is the data-
created: this will create and populate the database, if aware version of the ListAdaptor.
it didn't exist yet. For the Absentee application, this means a query
Last but not least, the list view is filled with items in must be run that returns all groups: Such a cursor is
the FillItems call, and the list created in GetAllGroups in the database helper
InnerOnItemClickListener is set so the class. This cursor can be used to fill the listview with
listview can react to clicks. the names of the groups with the following code:
var
cur: ADCursor;
colfrom: array[0..0] of JLString;
begin
if Not isRefresh then
begin
cur :=
fDataHelper.GetAllGroups(FGroupsDescending);
startManagingCursor(cur);
colfrom[0]:=TAbsenteeDBHelper.ColumnGroupName;
fAdapter:=AWSimpleCursorAdapter.Create(Self,
R.layout.group_list_item, cur,
colfrom, [R.id.group_name]);
setListAdapter(fAdapter);
end
else
fAdapter.getCursor.reQuery;
end;
26 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Accessing Preferences and Databases in Android FPC (continuation 5)
The intent constructor needs 2 parameter: a context, procedure TPupilsActivity.FillItems(
Isupdate: Boolean);
and an Activity class to start. The context is the var
group itself, and the class is obviously the pupils cur : ADCursor;
activity. colfrom : array[0..1] of JLString;
An intent can carry parameters, which are simply b : TCheckBoxListBinder;
named values: These parameters are then made begin
available in the receiver of the intent. For the students if IsUpdate then
dialog, one parameter to pass is obviously the ID of fAdapter.getCursor.requery
else
the group for which the students must be shown. A begin
second (boolean) parameter is the preference setting cur:=fDataHelper.GetPupilPresenceFromGroup(
for the sort order of the students. FID,fSortFirstName);
The list of students is similar to the list of groups, startManagingCursor(cur);
colfrom[0] := 'pu_name';
except that there should be a check box for each
colfrom[1] := 'pr_code';
pupil, to mark whether the student is present or fAdapter := AWSimpleCursorAdapter.Create(
absent. To achieve this, the layout of the groups Self, R.layout.pupil_list_item, cur,
listview can be copied, but must be enhanced with colfrom, [R.id.pupil_name,R.id.present]);
the following entry after the TextView tag: b:= TCheckBoxListBinder.Create;
b.flistener:=Self;
<CheckBox fadapter.setViewBinder(b);
android:id="@+id/present" setListAdapter(fAdapter);
android:layout_width="wrap_content" end;
android:layout_height="wrap_content" end;
android:layout_alignParentRight="true"
android:layout_marginLeft="4px" The custom binder class is used to format each item in
android:layout_marginRight="10px" > the list with the data from the list, for this it
</CheckBox> implements the
The changed layout can be saved as AWSimpleCursorAdapter.InnerViewBinder
pupil_list_item.xml. The id attribute shows class.
that the check box will have an ID assigned to it, TCheckBoxListBinder = Class(JLObject,
called present. The code for the students (pupils) AWSimpleCursorAdapter.InnerViewBinder)
FListener :
listview activity is almost identical to the one
AWCompoundButton.InnerOnCheckedChangeListener;
for the groups. There are some small differences: for Function setViewValue(view: AVView;
instance, in the onCreate method, the extra cursor: ADCursor;
parameters passed with the intent must be retrieved, colindex : jint) : jboolean;
end;
so it can be used in the query to retrieve all students
in the group: The FListener field is an interface that is
passed to this object: the binder will pass
procedure TPupilsActivity.onCreate(savedInstanceState:
AOBundle);
this interface to each checkbox it formats:
the checkbox will call this interface when
Const the checkbox is checked or unchecked..
SPrefSort = 'eu.blaisepascal.absenteeapp.SortFirstName'; The setViewValue method of the interface,
SParamID = 'eu.blaisepascal.absenteeapp.ID';
which does the actual formatting (or
begin binding) is implemented as follows:
inherited onCreate(savedInstanceState);
fDataHelper:=TAbsenteeDBHelper.Create(Self);
fSortFirstName:=getIntent.getBooleanExtra(SPrefSort,False);
fID:=getIntent.getLongExtra(SParamID, 0);
if (fID=0) then
Raise EAbsenteeData.Create('No ID for absentee app given');
SetTitle(R.strings.name_pupils);
FillItems(False);
end;
The Intent instance can be retrieved with getIntent,
it offers several methods to retrieve extra data in a variety of
data types. Note that, together with the ID of the student group,
the preference for sorting the students is also fetched. The code
to fill the list of items is virtually identical to the one for groups,
with the difference that a different query is used: There are 2
fields: one for the name, one for the absence indicator. To
respond to the user checking or unchecking a name, a custom
binder is used:
Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
COMPONENTS
DEVELOPERS 4 27
Accessing Preferences and Databases in Android FPC (continuation 6)
function TCheckBoxListBinder.setViewValue( Managing preferences
view: AVView; cursor: ADCursor; Besides an API for getting and setting
colindex: jint): jboolean;
preferences, Android offers also an API to create a
Var cb : AWCheckbox; puID,i : jint; s : string;
begin dialog in which the user can manipulate the
Result:=(colIndex=2); preferences: it offers a PreferenceActivity class which
if Result then shows a dialog to manage the preferences of an
begin
application. This class can be used as-is, and does not
cb:=AWCheckBox(view);
puID:=cursor.getint(0); need any additional code to manage the settings: it
cb.setTag(TIDTag.Create(puID)); must be constructed using a special set of views, each
i:=cursor.getColumnIndex('pr_code'); of which is connected to a preference setting, using a
if not Cursor.IsNull(i) then name. All that needs to be done, is specify a layout:
begin
s:=cursor.getstring(i);
<?xml version="1.0" encoding="utf-8"?>
cb.setChecked(s='+');
<PreferenceScreen
xmlns:android=
end;
cb.setOnCheckedChangeListener(flistener); "http://schemas.android.com/apk/res/android">
end; <PreferenceCategory
end; android:summary="View settings"
android:title="GUI Settings" >
The code is pretty straightforward: if the passed view <CheckBoxPreference
is the checkbox -- this can be determined from the android:key="GroupsDescending"
column index -- then it gets a tag associated with it. android:summary="When checked,
The tag can be any object. In this case a small custom groups are sorted descending"
defined object that just contains the ID of the pupil: android:defaultValue="false"
android:title="Sort groups descending"/>
this will be used in the OnCheckedChange listener. <CheckBoxPreference
After setting the tag, the check marker is set or android:key="PupilsFirstName"
cleared, based on the value of the pr_code field (a '+' android:summary="When checked,
or '-'). Finally, the OnCheckedChange listener of the pupils are sorted on firstname"
checkbox is set to the interface in FListener. android:defaultValue="false"
This OnCheckedChange listener interface is android:title="Sort pupils on first name"/>
</PreferenceCategory>
implemented in the TPupilsActivity class. It starts by
</PreferenceScreen>
getting the tag value which was added during the
bind operation: it contains the students ID, and uses The layout is divided in categories (for simplicity
this to save the value of the check to the database there is only 1 category : "Gui setting''), which can
using the data helper class: have a title and summary. These act as a purely
procedure TPupilsActivity.onCheckedChanged( visual divider of the activity. In each category,
cb: AWCompoundButton; checked: jboolean); several preference widgets can be shown. There are
Var puID : Integer; different preference widgets, depending on the kind
begin
puID:=TIDTag(cb.getTag()).ID;
of preference that must be managed. Android offers
FDataHelper.Pupilpresence[puID]:=cb.isChecked; out of the box a CheckboxPreference (or a
end; SwitchPreference) for boolean values,
The actual queries will not be presented, they are EditTextPreference, ListPreference,
MultiselectListPreference: the names speak
straightforward and simple. The activity, once called
for themselves.
from the main groups activity, looks as in figure 1.
Each preference has a title and summary, and a
default value. The key attribute gives the name of the
preference, which should of course match the name
used to retrieve the preference in code.
All that is needed to show the above preferences, is
to declare the activity class, and load the preferences
resource in the onCreate method:
TAbsenteePreferenceActivity =
class(APPreferenceActivity)
public
procedure onCreate(
savedInstanceState: AOBundle); override;
end;
Procedure
TAbsenteePreferenceActivity.onCreate(savedInstanc
eState: AOBundle);
begin
inherited onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.prefs);
Figure : The students, absent or present end;
28 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Accessing Preferences and Databases in Android FPC (continuation 7 -end)
function TGroupActivity.onOptionsItemSelected(
AItem: AVMenuItem): Boolean;
var intent: ACIntent;
begin
if AItem.getItemId()=AVMenu.First then
begin
intent := ACIntent.Create(Self,
JLClass(TAbsenteePreferenceActivity));
startActivity(intent);
Exit(True);
end
else
Result:= Inherited
onOptionsItemSelected(AItem);
end;
procedure ;
var
begin
for i := 1 to 9 do
begin
end;
end;
WIKIPEDIA
x = ..t...
y = ..t...
This circle, having a radius of 5 and center (0,0) may
X and Y are now functions themselves of interim be described by
variable t. x = 5cos(t)
A value of t now yields a value for x and also for y. y = 5sin(t)
This is called a parametric function. t has to be incremented from 0 to 360 degrees for the
Another sort of function is the periodic function. complete circle. The result of a sine or cosine function
Examples are: is -1...+1 Lissajous figures emerge when periodic
functions are written in parametric form.
y = sin(x) and also y = cos(x) More interesting shapes than circles may be
generated by applying constants or the addition or
x is an angle. multiplication of several sine or cosine functions.
See the figure below to refresh your mind: On the next page is a 2D example:
x = 2cos(t)
y = sin(t) + sin(3t)
x = cos(..t)
y = sin(..t)
z = sin(..t)
When the Z axis is painted with angle a to the x axis,
Note: the sine and cosine functions are then:
interchangeble.
px = x zcos(a)
The computer screen (still) is 2 dimensional, so for a 3 py = y + zcos(a)
dimensional illusion we have to implement a tric.
Please look at the picture below: where (px,py) are pixel coordinates of the screen and
(x,y,z) are the 3 dimensional coordinates.
- add z value of pen pixel to z value of pen on screen Unit2 stands on its own and contains the data
- write pixel if this height (z value) is greater than the formats, procedures and functions to draw the pens
screen z value and the Lissajous graphics.
Right top is the code
There is choice out of three sets of formulas, 7 colors
and 4 pen styles.
Constants may be altered by left (+) and right ()
mouseclicks. The new formulas are pictured at each
change. Settings may be saved, reloaded or a picture
may be saved as bitmap. Please refer to the source
code for more details.
36 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Checking your Android device is ready for development (Continuation 1)
cd
\Users\Public\Documents\Embarcadero\Stud
io\14.0\PlatformSDKs\adt-bundle-windows-
x86-20131030\sdk\tools
Confirm Install
Once installed the Android Composite ADB Interface
driver will be installed and you will get confirmation.
Figure 4: Set path to find Android SDK for Appmethod / RAD Studio
38 COMPONENTS
DEVELOPERS 4 Issue Nr 1 2015 BLAISE PASCAL MAGAZINE
Checking your Android device is ready for development (Continuation 3)
At this point you are ready to use your android
device for development!
Refresh your Android devices in your IDE (right
click on Android as a Target platform in the
Project Manager and it will become visible.)
- Added AMQP 0.91 client side gateway Supports Delphi/C++Builder/RAD Studio 2009
support and sample. to XE7 (32 bit, 64 bit and OSX where applicable).
- Updated StreamSec TLS transport plugin kbmMW for XE5 to XE7 includes full support for
component (by StreamSec). Android and IOS (client and server).!
- Improved performance on Indy TCP/IP
kbmMemTable is the fastest and most feature rich
Client messaging transport for large number in memory table for Embarcadero products.
of inbound messages.
- Easily supports large datasets
- Native high performance 100% developer with millions of records
defined application server with support for - Easy data streaming support
loadbalancing and failover - Optional to use native SQL engine
- Native high performance JSON and XML - Supports nested transactions and undo
- Native and fast build in M/D,
(DOM and SAX) for easy integration with
aggregation /grouping,
external systems range selection features
- Native support for RTTI assisted object - Advanced indexing features for
marshalling to and from XML/JSON, now also extreme performance
with new fullfeatured XML schema
(XSD) import Warning!
- High speed, unified database access kbmMemTable and kbmMW
(35+ supported database APIs) with
connection pooling, metadata and
are highly addictive!
Once used, and you are hooked for life!
data caching on all tiers
- Multi head access to the application server,
via AJAX, native binary, Publish/Subscribe,
SOAP, XML, RTMP from web browsers,
embedded devices, linked application
servers, PCs, mobile devices, Java systems
COMPONENTS
4
and many more clients
- Full FastCGI hosting support.
Host PHP/Ruby/Perl/Python applications in
kbmMW! DEVELOPERS
EESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI /
C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON WIN32 / 64,
.NET, LINUX, UNIX MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND TABLETS.