FireMonkey - Embarcadero Community

第 21 回エンバカデロ・デベロッパーキャンプ
[W5]ワークショップ
FireMonkey アプリケーション構築実習
目次
1. 概要説明
1-1. FireMonkey とは
2. 2D のピザアプリケーション
2-1. 最初のピザアプリケーション
演習 1. 2D のピザアプリケーションの作成
2-2. 効果とスタイルの使用
演習 2. 効果の適用
演習 3. スタイルの適用
2-3. アニメーションの使用
演習 4. アニメーションの使用
3. 3D のピザアプリケーション
演習 5. 3D のピザアプリケーション
4. DataBinding のピザアプリケーション
演習 6. DataBinding の使用(cds ファイルを使用)
5. 課題(時間に余裕のある受講者の方へ)
5-1. 課題
5-2. 解答例
1. 概要説明
FireMonkey とは
FireMonkey は、ビジュアルに優れたアプリケーションを Delphi や C++Builder で作成するための新しいアプリ
ケーションプラットフォームです。FireMonkey ではベースとなる OS のネイティブグラフィックススタイルライ
ブラリを使用し、HD/3D グラフィックス、柔軟性のあるスタイル、効果、アニメーション、新しいバインディン
グモデルなどが提供されています。
また、FireMonkey で構築されたアプリケーションは Windows/Mac OSX でのクロスコンパイルが可能です。
コードは CPU およびオペレーティングシステムにあわせてネイティブコンパイルされます。ランタイム、インタ
ープリタ、JIT コンパイラのようなものはありません。
配布またはインストールするライブラリはありません。FireMonkey プラットフォームはコンパイルされて、実際
のアプリケーション内に組み込まれるため、作成したアプリケーションを簡単に配布することができます。
DirectX や OpenGL のようなさまざまな GPU ライブラリに適合させることができます。
FireMonkey では、コンパイルされたネイティブアプリケーションにより、クロスプラットフォームかつオープン
な環境で、CPU および GPU の最高のパフォーマンスを実現する一方、仮想マシン、インタープリタ、ランタイ
ムなどをユーザーのコンピュータにインストールする必要はありません。
この「FireMonkey アプリケーション構築実習」では、具体的なアプリケーション構築の例題を通して、FireMonkey
によるプログラミング手法を学びます。
2. 2D のピザアプリケーション
2-1. 最初のピザアプリケーション
FireMonkey HD アプリケーションの基本的な設計方法等について学びます。
この演習では、Button, Edit, Listbox を配置し、ボタンをクリックすると ListBox に Edit に入力した内容が追加される
という基本動作のコーディングまで行います。
演習 1.
2D のピザアプリケーションの作成
1. メニューの [ファイル | 新規作成 | FireMonkey HD アプリケーション – Delphi] 、または、[ファイル | 新規作
成 | FireMonkey HD アプリケーション – C++Builder] を選択します。
2. ツールパレットの Standard カテゴリから

TButton

TEdit

TListBox
をフォーム上に配置します。
注意:
VCL の場合は、TPanel 等の限られたコンポーネントのみ親子関係にすることができましたが、FireMonkey 上では何
でも親子関係にできます。そのため、ツールパレットからダブルクリックして配置すると、例えば TButton の中(子
として)に TEdit が配置されることがありますのでご注意ください。
3. それぞれのコンポーネントを、適当な大きさに調整します。
4. 各コンポーネントの Name プロパティの値を次のように設定します。

TForm : PizzaForm

TEdit : edTopping

TButton : btnAddTopping

TListBox : lbToppings
5. ここで、一旦プロジェクトを保存しておきましょう。[ファイル | すべて保存] とし、FireMonkey2D フォルダを
作成し、保存します。
6. 次にフォームのキャプションと、ボタンの表示を修正します。

PizzaForm の Caption プロパティの内容を “ Build My Pizza, 101 “ に変更します。

btnAddTopping の Text プロパティの内容を “トッピングの追加” に変更します。
注意:
VCL フォームの場合、Button コンポーネントの表示は Caption プロパティですが、FireMonkey の Button コンポー
ネントの場合は Text プロパティとなります。
7. 変更した画面のイメージは以下のようになります。
8. btnAddPizza(Button) をマウスでダブルクリックし、以下のコードを記述します。
Delphi
lbToopings.Items.Add(edTopping.Text);
C++Builder
lbToppings->Items->Add(edTopping->Text);
9. [ファイル | すべて保存] でここまでの作業を保存します。
10. [実行 | 実行] で、作成したアプリケーションを実行します。
入力のボックスに何か入力し、[トッピングの追加]のボタンをクリックすると、右のリストに入力した内容が追加され
ます。
2-2. 効果とスタイルの適用
FireMonkey では、定義済みの表示効果を使用して、コントロールに簡単に表示効果を追加することができます。
演習 2. 効果の適用
この演習では、演習 1 で作成したフォームに表示効果を追加します。
1.
ツールパレットの Effects カテゴリ内の TShadowEffect をフォーム上に追加します。
2.
構造ペイン上で ShadowEffect をドラッグして、btnAddTooping にドロップし、以下の図のように親子関係にな
るように設定します。ボタンの周りの影が濃くなったことを確認できると思います。
3.
次に TRefrectionEffect をフォームに追加し、edTopping にドラッグ&ドロップで関連付けます。
4.
RefrectionEffect の Length プロパティの値を 1 に設定します。これにより、edTopping コントロールの下に鏡の
ようにうっすらと表示が映るようになります。
5.
TCrumpleTransitionEffect をフォームに追加し、lbToppings にドラッグ&ドロップで関連付けます。
6.
CrumpleTransitionEffect の Progress プロパティの値を 1 に設定します。これにより、ldToppings の外枠が軽く
クシャっとした感じになります。
この3つの効果の追加で、フォームは以下のようになります。
7.
メニューより、[ファイル | すべて保存 ] を選択し、ここまでの作業を保存します。
8.
メニューの [実行 | 実行] を選択し、作成したアプリケーションを動作させます。
edTopping に値を入れると、その値がコントロールの下に鏡のように表示されることを確認できます。
lbToppings のクシャクシャ感を変化させるために、ここで TButton を1つ追加します。
9.
ツールパレットからフォームに TButton を追加します。
10. Name プロパティを btnCrumple に修正します。
11. Text プロパティを “クシャクシャ” に設定します。
12. ツールパレットからフォームに TShadowEffect を追加し、btnCrumple にドラック&ドロップします。
13. btnCrumple をダブルクリックし、以下のコードを追加します。
Delphi
CrumpleTransitionEffect1.Progress := CrumpleTransitionEffect1.Progress + 1;
C++Builder
CrumpleTransitionEffect1->Progress = CrumpleTransitionEffect1->Progress + 1;
14. フォームの大きさの変化と共にコントロールの大きさも変化するよう、ツールパレットの Layouts カテゴリから
TScaledLayout コンポーネントをフォームに追加し、フォーム上に広げます。
15. 次に、構造ペイン上で、フォーム上のすべてのコンポーネントを ScaledLayout コンポーネントにドラック&ドロ
ップします。
16. TScaledLayout コンポーネントの Align プロパティを alClient に設定します。
17. オブジェクトインスペクタ上で、edTopping の「イベント」タブを選択し OnKeyDown と OnTypeing イベント
の部分をクリックし、それぞれのイベント内に以下のコードを記述します。
Delphi
ReflectionEffect1.UpdateParentEffects;
C++Builder
ReflectionEffect1->UpdateParentEffects();
18. ツールパレットからフォーム上に TTimer を追加します。
19. オブジェクトインスペクタ上で、Timer1 の Interval プロパティの値を 500 に設定します。
20. オブジェクトインスペクタ上で、Timer1 の「イベント」タブを選択し、OnTimer イベントの部分をダブルクリッ
クします。
21. OnTimer イベントが作成されますので、以下のコードを記述します。
Delphi
Salf.Invalidate;
C++Builder
this->Invalidate();
22. メニューより、[ファイル | すべて保存 ]を選択し、ここまでの作業を保存します。
23. メニューより、[実行 | 実行]を選択し、作成したアプリケーションを動作させます。フォームの大きさを変更す
ると、その大きさと共に中のコンポーネントも縮小/拡大されます。また、[クシャクシャ]のボタンを押していく
と lbToppings の外枠が変化していきます。
24. デザイナ上で、lbToppings を選択し、マウスの右ボタンをクリックして表示されたポップアップメニューから「項
目の追加」を選択、またはオブジェクトインスペクタ上で「項目の追加」を選択し、ibToppings に TListBoxItem
を追加します。
25. ツールパレットから TImegeControl をフォームに追加し、構造ペイン上で TListBoxItem にドラッグ&ドロップ
で関連付けます。
26. 追加した TListBoxItem の大きさを調整します。
27. ボタンの画像を追加するため、オブジェクトインスペクタ上で、TImegeControl の Bitmap プロパティの横の […]
ボタンをクリックし、
「編集…」を選択します。
28. ビットマップエディタが表示されますので [読み込み]のボタンをクリックして、ボタンの画像を選択し [開く]
のボタンをクリックし、更に [OK]のボタンをクリックします。
29. また、TListBoxItem の Text プロパティの値を “トマト” に変更します。
30. オブジェクトインスペクタ上で、TImageControl を選択し、
「イベント」タブをクリックします。表示されたイベ
ントの「OnClick」の部分をダブルクリックして、OnClick イベントを生成し、以下のコードを記述します。
Delphi
lbToppings.Items.Delete(0);
C++Builder
lbToppings->Items->Delete(0);
これによりあらかじめ ListBox 内に表示されている×ボタンをクリックすると、ListBox の項目が削除されるという動
作が実装されます。
スタイルの適用
FireMonkey では、どのビジュアルコンポーネントに対しても異なるスタイルで描画することができます。
また、スタイルに、ネストしたコントロールを追加することができるので、画像の付いたボタンなどの
組み合わせによるスタイルの作成も可能です。
演習 3. スタイルの適用
1.
メニューの [ファイル | 新規作成 | FireMonkey HD アプリケーション – Delphi] 、または、[ファイル | 新規作
成 | FireMonkey HD アプリケーション – C++Builder] を選択します。
2.
ツールパレットの Standard カテゴリから

TButton

TListBox

TSizeGrip
を、Additional カテゴリから
TComboEdit

フォーム上に配置します。
3.
ここで、一旦プロジェクトを保存しておきましょう。[ファイル | すべて保存] とし、¥FireMonkey2DStyle フォ
ルダを作成し、このフォルダ以下に保存します。
4.
先程と同様に以下のコンポーネントの Name プロパティを変更します。

TButton → btnAddTopping

TListBox → lbToppings

TComboEdit → cbToppings
次にフォームのキャプションと、ボタンの表示を修正します。
5.
PizzaForm の Caption プロパティの内容を “ Build My Pizza, 101 “ に変更します。
6.
btnAddTopping の Text プロパティの内容を “トッピングの追加” に変更します。
7.
TSizeGrip コンポーネントの Align プロパティを alScale に設定します。
8.
画像ファイルを¥FireMonkey2DStyle フォルダ下にコピーします。
cbTopping の項目を、Form 作成時に動的に作成します。
コピーした画像ファイルのファイル名を読み込み、cbToppings の Items に追加することによって、項目を作成してい
ます。
9.
uses に IOUtils を追加します。(C++Builder の場合は #include <IOUtils.hpp> を追加します)
10. Form の Create イベントに以下を記述します。
Delphi
var
strArray: TStringDynArray;
aStr: string;
begin
strArray := TDirectory.GetFiles('.¥..¥..¥', '*.jpg');
for aStr in strArray do
cbToppings.Items.Add(TPath.GetFileNameWithoutExtension(aStr));
end;
C++Builder
UnicodeString aStr;
TStringDynArray strArray = TDirectory::GetFiles(".¥¥..¥¥..¥¥", "*.jpg");
for (int i = 0; i < strArray.Length; i++) {
aStr = strArray[i];
cbTopping->Items->Add(System::Ioutils::TPath::GetFileNameWithoutExtension(aStr));
}
11. btnAddTopping の OnClick イベントを作成し、先程と同様に選択した内容を lbToppings に追加する処理を記述
します。
Delphi
lbToopings.Items.Add(cbToppings.Text);
C++Builder
lbToppings->Items->Add(cbToppings->Text);
12. [ファイル | すべて保存] でここまでの内容を保存し、[実行|実行] で動作を確認します。
ボタンのスタイルを変更します。
13. 丸いボタンに変更するため、btnAddTopping をデザイナ上で選択して、マウスの右ボタンをクリックします。
14. 表示されたポップアップメニューより「カスタムスタイルの編集」を選択し、スタイルエディタを開きます。
15. btnAddToppingStyle1 のツリーにある 3 つの TRectangle の XRadius プロパティと YRadius プロパティの値を
すべて 55 に設定します。
16. [適用して閉じる]のボタンをクリックし、デザイン画面に戻り、btnAddTopping の Height プロパティと Width
プロパティの値を 110 に設定します。これにより丸いボタンが表示されます。
次に、ListBox に追加される項目に対してもスタイルを作成し、適用します。
17. まず btnAddTopping のスタイルの設定で自動的にフォームに追加された StyleBook1 コンポーネントをダブルク
リックします。
18. ツールパレットから TLayout を、スタイルエディタの「スタイルの作成」の部分へドラッグ&ドロップします。
19. 追加された新しいスタイルに対し、オブジェクトインスペクタ上の StyleName プロパティを imagelabel に変更
します。
次に、このスタイルに、トッピング名を表示するための TText と、画像を表示する TImage を追加します。
20. まず、imagelabel スタイルのレイアウトの大きさを適度な大きさに変更します。
21. ツールパレットの Shapes カテゴリ内にある TText と TImage を imagelabel にドラッグ&ドロップします。
22. 先程と同様に StyleName プロパティを Text1 から Text へ、 Image1 から Image に設定します。
23. TText の Font の Size プロパティを 11 から 22 に変更します。
24. それぞれのコンポーネントの大きさを図のように調整します。
25. [適用して閉じる] をクリックします。
次に、ボタンがクリックされた際に、ListBox に TListBoxItem を追加し、追加された ListBoxItem にこのスタイルを
適用して、選択されたトッピングとその画像が追加されるコードを記述します。
26. 作業用に TImage をフォーム上に追加し、TImage の Visible プロパティを false に設定しておきます。
27. 同じく作業用のエリアを private 部分に宣言しておきます。
Delphi
private
{ private 宣言 }
strTopping: string;
// 追加
C++Builder ( .h ファイル側)
private:
// ユーザー宣言
UnicodeString strTopping;
// 追加
28. btnAddTopping をダブルクリックし、現在記述されているコードを以下のように書き換えます。
Delphi
var
listItem: TListBoxItem;
itemText: TText;
itemImage: TImage;
begin
// lbToppings.Items.Add(cbToppings.Text);
if not Assigned (cbToppings.ListBox.Selected) then
begin
ShowMessage ('何も選択されて'+#13#10+'いません');
Exit;
end;
strTopping := cbToppings.ListBox.Selected.Text;
Caption := 'Adding' + strTopping;
Image1.Bitmap.LoadFromFile('.¥..¥..¥'+ strTopping +
'.jpg');
listItem := TListBoxItem.Create(lbToppings);
listItem.Parent := lbToppings;
listItem.StyleLookup := 'imagelabel';
itemText := listItem.FindStyleResource('Text') as TText;
if Assigned (itemText) then
itemText.Text := strTopping ;
itemImage := listItem.FindStyleResource('Image') as TImage;
if Assigned (itemImage) then
begin
itemImage.Bitmap := Image1.bitmap;
listItem.Height := Image1.bitmap.height;
end
else
ShowMessage('バインドする'+#13#10+'イメージがありません');
Image1.Bitmap.Clear(claWhite);
Image1.Position.X := 32;
end;
C++Builder
//
lbToppings->Items->Add(cbToppings->Text);
if (cbToppings->ListBox->Selected == NULL) {
ShowMessage("何も選択されて¥r¥n いません");
} else {
strTopping = cbToppings->ListBox->Selected->Text;
Caption = "Adding " + strTopping;
Image1->Bitmap->LoadFromFile(".¥¥..¥¥..¥¥"+ strTopping +".jpg");
TListBoxItem *listItem = new TListBoxItem(lbToppings);
listItem->Parent = lbToppings;
TText *itemText;
TImage *itemImage;
listItem->StyleLookup = "imagelabel";
itemText = (TText*)(listItem->FindStyleResource("Text"));
itemText->Text = strTopping;
itemImage = (TImage*)(listItem->FindStyleResource("Image"));
if (itemImage != NULL) {
itemImage->Bitmap = Image1->Bitmap;
listItem->Height = Image1->Bitmap->Height;
} else {
ShowMessage("バインドする¥r¥n イメージがありません");
}
Image1->Bitmap->Clear(claWhite);
Image1->Position->X = 32;
}
2-3. アニメーションの使用
FireMonkey の特徴として「アニメーション化」があります。
これは、プロパティに時間的に変化する値を設定し動作させるというものです。
オブジェクトインスペクタから簡単に設定することもできますし、コードとして記述したものを実行させることもで
きます。
演習 4. アニメーションの使用
演習 3 で作成したフォームへアニメーションによる動作を追加します。
まず、フォームが表示された際に、cbToppings(ComboEdit) が横から現れるように、アニメーションの動作をつけま
しょう。
1.
デザイン画面上で cbToppings を選択し、オブジェクトインスペクタで Position の X プロパティの部分にある▼
を押し、
「TFloatAnimation の新規作成」を選択します。
2.
TFloatAnimation コンポーネントが作成されますので、それぞれのプロパティの値を以下のように設定します。
Duration
1.5
Enabled
True
Name
aniSlideInCombo
StartValue
3.
-200
StopValue
32 (現在の cbToppings の Position.X の値に)
Trigger
IsVisible=True
これだけです。[ファイル|すべて保存] でいったん保存し、[実行|実行] で実行すると、左から cbToppings が現
れます。
次に [トッピングの追加] ボタンを押した際に、画像が回転しながら現れ、lbToppings(ListBox) に追加される部分の
アニメーションを設定します。
4.
デザイン画面上で Image1 を選択し、オブジェクトインスペクタ上で、Visible プロパティの値を True に戻しま
す。
5.
同じく、デザイン画面上で Image1 を選択し、オブジェクトインスペクタ上で、Position プロパティの X の部
分にある▼をクリックし 「TFloatAnimation の新規作成」を選択します。
6.
TFloatAnimation が作成されますので、オブジェクトインスペクタ上で、プロパティの値を以下のように設定しま
す。
7.
Duration
2
Name
aniMoveImage
StartFromCurrent
True
StopValue
400
もう一度デザイン画面に戻り、先程と同様に Image1 を選択し、オブジェクトインスペクタ上で、RotationAngle
プロパティにある▼をクリックし「TFloatAnimation の新規作成」を選択します。
8.
こちらも TFloatAnimation が作成されますので、オブジェクトインスペクタ上で、プロパティの値を以下のよう
に設定します。
AutoReverse
True
Duration
1.5
Name
aniRotateImage
StartFromCurrent
True
StopValue
720
また、ボタンをクリックして上記のアニメーションが終了した時点で、lbToppings に追加されるように、以前の演習
で記述したコードを別のイベントのコードとして記述します。
9.
デザイン画面上で Image1 を選択し、オブジェクトインスペクタ上で、Position プロパティの X の部分に追加
された aniMoveImage の所にある […]ボタンをクリックします。
10. オブジェクトインスペクタ上で aniMoveImage 側に移動したら、「イベント」タブをクリックして OnFinish イ
ベント部分をダブルクリックします。
11. aniMoveImageFinish イベントが自動的に作成されますので、btnAddTopping の OnClick イベントのコードの以
下の部分をコピー&ペーストします。コピー元の方は {….} または /* … */でくくってコメントにしておきます。
Delphi
var の宣言部分と Image1.Bitmap.LoadFromFile('.¥..¥..¥'+ strTopping +
'.jpg'); の次の文から最後まで
C++Builder
Image1->Bitmap->LoadFromFile(".¥¥..¥¥..¥¥"+ strTopping +".jpg"); の次の文から } の前まで (if の{}に気
を付けてください)
12. 設定したアニメーションがボタンを押したときに動作するように、btnAddTopping の OnClick イベントを以下の
ように修正します。
Delphi
procedure TForm1.btnAddToppingClick(Sender: TObject);
{
var
listItem: TListBoxItem;
itemText: TText;
itemImage: TImage;
}
begin
// lbToppings.Items.Add(cbToppings.Text);
if not Assigned (cbToppings.ListBox.Selected) then
begin
ShowMessage ('何も選択されて'+#13#10+'いません');
Exit;
end;
strTopping := cbToppings.ListBox.Selected.Text;
Caption := 'Adding' + strTopping;
Image1.Bitmap.LoadFromFile('.¥..¥..¥'+ strTopping +
'.jpg');
aniRotateImage.Start;
aniMoveImage.StopValue := lbToppings.Position.X + 20;
aniMoveImage.Start;
{ 以下コメントアウト
listItem := TListBoxItem.Create(lbToppings);
listItem.Parent := lbToppings;
listItem.StyleLookup := 'imagelabel';
C++Builder
//
ListBox1->Items->Add(ComboEdit1->Text);
if (cbToppings->ListBox->Selected == NULL) {
ShowMessage("何も選択されて¥n いません");
} else {
strTopping = cbToppings->ListBox->Selected->Text;
Caption = "Adding " + strTopping;
Image1->Bitmap->LoadFromFile(".¥¥..¥¥..¥¥"+ strTopping +".jpg");
aniRotateImage->Start();
aniMoveImage->StopValue = lbToppings->Position->X + 20;
aniMoveImage->Start();
}
/* 以下コメントアウト 最後の }はコメントの直前に移動
TListBoxItem *listItem = new TListBoxItem(lbToppings);
listItem->Parent = lbToppings;
13. これで完成です。[ファイル|すべて保存]して、[実行|実行]でアプリケーションを動作させてみましょう。
コード部分はそれぞれ以下のようになります(コメントにした部分は省いています)
Delphi
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Layouts, FMX.ListBox,
FMX.Edit, FMX.Objects, FMX.Ani;
type
TForm1 = class(TForm)
cbToppings: TComboEdit;
btnAddTopping: TButton;
lbToppings: TListBox;
SizeGrip1: TSizeGrip;
StyleBook1: TStyleBook;
Image1: TImage;
aniSlideInCombo: TFloatAnimation;
aniMoveImage: TFloatAnimation;
aniRotateImage: TFloatAnimation;
procedure FormCreate(Sender: TObject);
procedure btnAddToppingClick(Sender: TObject);
procedure aniMoveImageFinish(Sender: TObject);
private
{ private 宣言 }
strTopping: string;
public
{ public 宣言 }
end;
var
Form1: TForm1;
implementation
uses IOUtils;
{$R *.fmx}
procedure TForm1.aniMoveImageFinish(Sender: TObject);
var
listItem: TListBoxItem;
itemText: TText;
itemImage: TImage;
begin
listItem := TListBoxItem.Create(lbToppins);
listItem.Parent := lbToppings;
listItem.StyleLookup := 'imagelabel';
itemText := listItem.FindStyleResource('Text') as TText;
if Assigned (itemText) then
itemText.Text := strTopping ;
itemImage := listItem.FindStyleResource('Image') as TImage;
if Assigned (itemImage) then
begin
itemImage.Bitmap := Image1.bitmap;
listItem.Height := Image1.bitmap.height;
end
else
ShowMessage('バインドする'+#13#10+'イメージがありません');
Image1.Bitmap.Clear(claWhite);
Image1.Position.X := 32;
end;
procedure TForm1.btnAddToppingClick(Sender: TObject);
begin
if not Assigned (cbToppings.ListBox.Selected) then
begin
ShowMessage ('何も選択されて'+#13#10+'いません');
Exit;
end;
strTopping := cbToppings.ListBox.Selected.Text;
Caption := 'Adding' + strTopping;
Image1.Bitmap.LoadFromFile('.¥..¥..¥'+ strTopping +
'.jpg');
aniRotateImage.Start;
aniMoveImage.StopValue := lbToppings.Position.X + 20;
aniMoveImage.Start;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
strArray: TStringDynArray;
aStr: string;
begin
strArray := TDirectory.GetFiles('.¥..¥..¥', '*.jpg');
for aStr in strArray do
cbToppings.Items.Add(TPath.GetFileNameWithoutExtension(aStr));
end;
end.
C++Builder
.h 側
//--------------------------------------------------------------------------#ifndef bcbUnit1H
#define bcbUnit1H
//--------------------------------------------------------------------------#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include <FMX.Edit.hpp>
#include <FMX.Types.hpp>
#include <FMX.Layouts.hpp>
#include <FMX.ListBox.hpp>
#include <FMX.Objects.hpp>
#include <FMX.Ani.hpp>
//--------------------------------------------------------------------------class TForm1 : public TForm
{
__published:
// IDE で管理されるコンポーネント
TComboEdit *cbToppings;
TButton *btnAddTopping;
TListBox *lbToppings;
TImage *Image1;
TStyleBook *StyleBook1;
TFloatAnimation *aniSlideInCombo;
TFloatAnimation *aniMoveImage;
TFloatAnimation *aniRotateImage;
void __fastcall btnAddToppingClick(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
void __fastcall aniMoveImageFinish(TObject *Sender);
private: // ユーザー宣言
UnicodeString strTopping;
public:
// ユーザー宣言
__fastcall TForm1(TComponent* Owner);
};
//--------------------------------------------------------------------------extern PACKAGE TForm1 *Form1;
//--------------------------------------------------------------------------#endif
.cpp 側
//--------------------------------------------------------------------------#include <fmx.h>
#include <IOUtils.hpp>
#pragma hdrstop
#include "bcbUnit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::btnAddToppingClick(TObject *Sender)
{
if (cbToppings->ListBox->Selected == NULL) {
ShowMessage("何も選択されて¥r¥n いません");
} else {
strTopping = cbToppings->ListBox->Selected->Text;
Caption = "Adding " + strTopping;
Image1->Bitmap->LoadFromFile(".¥¥..¥¥..¥¥"+ strTopping +".jpg");
aniRotateImage->Start();
aniMoveImage->StopValue = lbToppings->Position->X + 20;
aniMoveImage->Start();
}
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
UnicodeString aStr;
TStringDynArray strArray = TDirectory::GetFiles(".¥¥..¥¥..¥¥", "*.jpg");
for (int i = 0; i < strArray.Length; i++) {
aStr = strArray[i];
cbToppings->Items->Add(System::Ioutils::TPath::GetFileNameWithoutExtension(aStr));
}
}
//--------------------------------------------------------------------------void __fastcall TForm1::aniMoveImageFinish(TObject *Sender)
{
TListBoxItem *listItem = new TListBoxItem(lbToppings);
listItem->Parent = lbToppings;
TText *itemText;
TImage *itemImage;
listItem->StyleLookup = "imagelabel";
itemText = (TText*)(listItem->FindStyleResource("Text"));
itemText->Text = strTopping;
itemImage = (TImage*)(listItem->FindStyleResource("Image"));
if (itemImage != NULL) {
itemImage->Bitmap = Image1->Bitmap;
listItem->Height = Image1->Bitmap->Height;
} else {
ShowMessage("バインドする¥r¥n イメージがありません");
}
Image1->Bitmap->Clear(claWhite);
Image1->Position->X = 32;
}
//---------------------------------------------------------------------------
3. 3D のピザアプリケーション
ここまで FireMonkey の 2D フォームを使用して、基本的な効果やスタイルの適用、アニメーションの設定などを行
ってきました。このセクションでは、2 次元オブジェクトを 3 次元空間で動かし、FireMonkey 3D の基本的な扱い方
を学びます。
演習 5. 3D のピザアプリケーション
1.
新しくアプリケーションを作成します。[ファイル|新規作成|その他] を選択し、表示された「新規作成ダイアロ
グ」の左側のツリーから 「Delphi プロジェクト」または「C++Builder プロジェクト」を選択し、右側の項目か
ら「FireMonkey 3D アプリケーション」を選択します。
2.
フォームを作成されたところで、[ファイル|すべて保存]で、 ¥FireMonkey3D フォルダを作成し、生成したフォ
ーム等を保存します。
3.
次にツールパレットからフォーム上に
TBufferLayer3D コンポーネントと TLayer3D コンポーネントを以下の
ように配置します。また双方の Projection プロパティを pjScreen に設定します。
4.
BufferLayer3D と 親子関係になるようにツールパレットから TImage3D を 2 つ置きます。先程と同様に
TImage3D それぞれの Projection プロパティは pjScreen にします。また、それぞれの TImage3D に対してアニ
メーションを設定したいので、ツールパレットから TFloatAnimation を TImage3D それぞれに配置します。
5.
Layer3D 側ですが、これも Layer3D と親子関係になるように、ツールパレットから TAniIndicator, TLabel, TPanel
を置きます。また TPanel と親子関係になるようにツールパレットから TLabel を配置します。(構造ペインの状
態を確認しつつ作業を行ってください)
6.
配置するコンポーネントは以上です。次にそれぞれのコンポーネントのプロパティをオブジェクトインスペクタ
上で設定します。
■Form3D1
Caption
Pizza メニュー3D
Color
Black
■BufferLayer3D1
Align
alMostLeft
Height
408
Projection
pjScreen
Resolution
50
Width
600
ZWrite
False
■Image3D1
Align
alCenter
Height
350
Projection
pjScreen
TwoSide
False
Width
500
■FloatAnimation1
AnimationType
atInOut
Enabled
True
Duration
2
Interpolation
itBack
StartValue
1
Stopvalue
179
PropertyName
RotationAngle.Y
■Image3D2
Align
alCenter
Height
350
Projection
pjScreen
TwoSide
False
Width
500
■FloarAnimation2
AnimationType
atInOut
AutoReverse
True
Duration
2
Enabled
True
Interpolation
itBack
StartValue
-180
PropertyName
RotationAngle.Y
■Layer3D1
Align
alClient
Fill.Color
White
Fill.kind
bkSolid
Height
408
Projection
pjScreen
Width
274
■AniIndicator1
Enabled
True
Height
113
TabOrder
0
Width
105
■Label1
Font.Size
44
Height
87
TabOrder
2
Text
Pizza Menu
TextAlign
taCenter
Width
249
■Panel1
Align
alCenter
Height
200
TabOrder
1
Width
393
■Label2
Align
alCenter
Height
121
TabOrder
0
Text
Pizza ショップメニュー: 画像をクリックすると
ページをめくるように反転します。またメニュー
をクリックすると、この部分がクルクルと反転し
ます。
7.
TextAlign
taCenter
Width
241
ここまで設定したところで、[ファイル|すべて保存]で保存します。
8.
この段階で実行すると、左側のイメージの部分のみ反転するというアニメーションを確認することができます。
次に、この左側をクリックすると再度反転するというアクション処理を記述しましょう。
9.
構造ペイン上で Image3D1 を選択し、オブジェクトインスペクタで「イベント」タブをクリックします。
10. 表示されたイベントの OnClick の右の部分に TurnPage と入力します。イメージがクリックされた際に実行され
る TurnPage に関するコードが自動生成されます。
11. 生成された TurnPage に以下のコードを記述します。Y 軸を中心に 180 度回転させる仕組みとなります。
Delphi
FloatAnimation1.StartValue := Image3D1.RotationAngle.Y;
FloatAnimation1.StopValue := Image3D1.RotationAngle.Y + 180;
FloatAnimation2.StartValue := Image3D2.RotationAngle.Y;
FloatAnimation2.StopValue := Image3D2.RotationAngle.Y + 180;
FloatAnimation1.Start;
FloatAnimation2.Start;
C++Builder
FloatAnimation1->StartValue = Image3D1->RotationAngle->Y;
FloatAnimation1->StopValue = Image3D1->RotationAngle->Y + 180;
FloatAnimation2->StartValue = Image3D2->RotationAngle->Y;
FloatAnimation2->StopValue = Image3D2->RotationAngle->Y + 180;
FloatAnimation1->Start();
FloatAnimation2->Start();
12. また Image3D2 の OnClick イベントにも、この TurnPage を関連付けます。
13. [ファイル|すべて保存]で保存します。
14. [実行|実行] で実行させ、左側の部分をクリックすると、ページが回転するのを確認できます。
もうひとつのアニメーションですが、これは設計時にアニメーションオブジェクトを設定するのではなく、実行時に
アニメーションを定義して動作するようにコードを記述します。
15. デザイン画面上で、Layer3D1 を選択し、オブジェクトインスペクタで「イベント」タブをクリックし、Image3D
の時と同様に OnClick イベントの部分に FlipSide と入力し、クリックした際に実行されるコードを生成します。
16. 生成された FlipSide に以下のコードを記述します。
Delphi
Layer3D1.AnimateFloat('RotationAngle.Y',360, 2, TAnimationType.atInOut, TInterpolationType.itBack);
Layer3D1.AnimateFloat('Position.Z', 500, 1);
Layer3D1.AnimateFloatDelay('Position.Z', 0, 1, 1);
C++Builder
Layer3D1->AnimateFloat("RotationAngle.Y",360, 2, TAnimationType::atInOut, TInterpolationType::itBack);
Layer3D1->AnimateFloat("Position.Z", 500, 1);
Layer3D1->AnimateFloatDelay("Position.Z", 0, 1, 1);
Y 軸を中心に回転させる動作と、Z 軸に対する移動の動作を記述しています。
2D の時は X 軸と Y 軸に対する平面的な動作のアニメーションでしたが、3D にすると Z 軸が加わり、
奥行きのある 3D
の動作を行わせることができます。
17. [ファイル|すべて保存]で保存します。
18. [実行|実行] で実行させ、右側の部分をクリックすると、記述したアニメーションが動作するのを確認できます。
仕上げにピザの画像を Image3D に張り付けて、回転するたびに別の画像が表示される部分を作成します。
19. 画像ファイルは、¥FireMonkey3D フォルダ下にコピーしておきます。
20. ファイルに関する IO 処理を行うため、Delphi では、uses に IOUtils を追加します。また、C++Builder では、
#include <IOUtils.hpp>と #include <System.Types.hpp>(.h 側) を追加します。
21. 作業用のエリアを private に追加します。
Delphi
private
{ private 宣言 }
imagesList: TStringDynArray;
imagePos: Integer;
C++Builder
private: // ユーザー宣言
TStringDynArray
imagesList;
int imagePos;
22. 画像に関する処理を行う関数を public に作成します。
Delphi
Public
{ public 宣言 }
procedure LoadNewImage;
C++Builder
public:
// ユーザー宣言
__fastcall TForm3D1(TComponent* Owner);
void __fastcall LoadNewImage();
23. 記述した、LoadNewImage にカーソルを載せた状態で、Ctrl + Shift + C キーを押すと、関数の実装部が生成され
ますので(Delphi のみ)、ここに以下のコードを記述します。
Delphi
procedure TForm1.LoadNewImage;
begin
if (imagePos mod 2) = 0 then
begin
Image3D1.Bitmap.Clear(claWhite);
Image3D1.Bitmap.LoadThumbnailFromFile(imagesList[imagePos], 500, 350, false);
end
else begin
Image3D2.Bitmap.Clear(claWhite);
Image3D2.Bitmap.LoadThumbnailFromFile(imagesList[imagePos], 500, 350, false);
end;
Inc(imagePos);
if imagePos >= Length (imagesList) then
imagePos := 0; // reset
end;
C++Builder の場合は、以下のコードを記述します。
C++Builder
void __fastcall TForm3D1::LoadNewImage()
{
if (imagePos%2 == 0) {
Image3D1->Bitmap->Clear(claWhite);
Image3D1->Bitmap->LoadThumbnailFromFile(imagesList[imagePos], 500, 350, false);
} else {
Image3D2->Bitmap->Clear(claWhite);
Image3D2->Bitmap->LoadThumbnailFromFile(imagesList[imagePos], 500, 350, false);
}
imagePos++;
if (imagePos >= imagesList.Length) {
imagePos = 0; // reset
}
}
24. また、フォームの生成時にイメージファイルを読み込むよう Form3D の OnCreate イベントを作成し、以下のコ
ードを記述します。
Delphi
procedure TForm1.Form3DCreate(Sender: TObject);
begin
imagesList := TDirectory.GetFiles('.¥..¥..¥', '*.jpg');
imagePos := 0;
LoadNewImage;
LoadNewImage;
end;
C++Builder
void __fastcall TForm3D1::Form3DCreate(TObject *Sender)
{
imagesList = TDirectory::GetFiles(".¥¥..¥¥..¥¥", "*.jpg");
imagePos = 0;
LoadNewImage();
LoadNewImage();
}
25. 最後に前に作成した TurnPage の最初に、画像ファイルを読み込むための関数呼び出しを追加します。
Delphi
procedure TForm1.TurnPage(Sender: TObject);
begin
LoadNewImage;
FloatAnimation1.StartValue := Image3D1.RotationAngle.Y;
FloatAnimation1.StopValue := Image3D1.RotationAngle.Y + 180;
<以下省略>
C++Builder
void __fastcall TForm3D1::TurnPage(TObject *Sender)
{
LoadNewImage();
FloatAnimation1->StartValue = Image3D1->RotationAngle->Y;
FloatAnimation1->StopValue = Image3D1->RotationAngle->Y + 180;
<以下省略>
26. 以上で、完成です。[ファイル|すべて保存] で保存し、[実行|実行]で作成したアプリケーションを動作させます。
4. DataBinding のピザアプリケーション
FireMonkey には、TDBxxx のような特別なデータ対応コントロールはありません。LiveBinding と呼ばれる、オブジェ
クトをプロパティを介して相互にバインドする機能を用います。
この演習では、読み取り専用のデータエディタを作成します。
演習 6. DataBinding の使用(cds ファイルを使用)
1.
[ファイル|新規作成|FireMoneky HD アプリケーション – Delphi] または [ファイル|新規作成|FireMonkey HD ア
プリケーション – C++Builder]で新規にアプリケーションを作成します。
2.
[ファイル|すべて保存] で ¥FireMonkeyDB フォルダを作成し、生成されたファイルを保存します。
3.
また、.cds ファイルをこのフォルダにコピーしておきます。
4.
ツールパレットから TClientDataSet と TDataSource をフォーム上に置きます。
5.
デザイナ画面上で ClientDataSet1 を選択し、マウスの右ボタンを押して表示されたポップアップメニューより
「MyBase テーブルから読み込み」を選択します。
6.
表示されたダイアログで、種別を「クライアントデータセット(*.cds)」とし、表示された .cds ファイルを選択し
ます。
7.
デザイナ画面上で、DataSource コンポーネントを選択し、オブジェクトインスペクタ上で、DataSet プロパティ
の値を ClientDataSet1 に設定します。
8.
次に、ツールパレットから 、以下のコンポーネントをフォーム上に置きます。

TBindNavigator

TBindScopeDB

TLabel x5

TEdit x3

TMemo

TImageControl
配置のイメージは以下を参考にしてください。
9.
次に、デザイナ画面上で BindScopeDB1 コンポーネントを選択し、オブジェクトインスペクタで、BindScopeDB1
の DataSource プロパティに DataSource1 を設定します。
10. デザイナ画面上で BindNavigator1 コンポーネントを選択し、オブジェクトインスペクタで、BindNavigator1 の
BindScpe プロパティに BindScopeDB1 を設定します。
11. Label コンポーネントの Text プロパティの値を、それぞれ次のように設定します。
Label1
ID
Label2
名前
Label3
値段
Label4
説明
Label5
イメージ
値を表示する Edit, Memo, ImageControl と ClientDataSet で読み込まれたデータのフィールドとを関連付けます。
12. デザイン画面で Edit1 を選択し、オブジェクトインスペクタの「DB フィールドへのリンク」をクリックします。
13. DB リンクの新規作成のダイアログが表示されます。
14. 表示された DB フィールドの Id を選択し [OK]ボタンを押します。
これで、ClientDataSet に読み込まれたデータの Id フィールドと Edit1 が関係付けられ、データが表示されます。
また、この際、自動的に TBindList コンポーネントが生成され、フォーム上に配置されます。
15. 同様に、Edit2 には Name フィールドを、Edit3 には Price フィールドをそれぞれ関連付けます。
16. また、Memo1 には Description を、ImageControl1 には Image フィールドをそれぞれ関連付けます。
17. Memo コンポーネントですが、デフォルトでは WordWrap が False ですので、オブジェクトインスペクタ上で
True に設定し、文字が折り返されて表示されるようにします。
18. ここまでの内容を保存します。[ファイル|すべて保存] で保存し、[実行|実行] で動作させます。
上部のナビゲータボタンを操作し、レコードの移動を行うと、そのレコードのデータがフォーム上に表示されるのを
確認することができます。
このように、特にコードを記述することなく LiveBinding を使用し、データとそれぞれのコンポーネントを関連付け、
処理を行うことが可能です。
このフォームに1つ機能を追加します。
データベースの Image フィールドに対し、読み込んだ画像ファイルのイメージで更新するという機能です。
19. フォーム上に TButton と TOpenDialog コンポーネントを追加します。
20. デザイナ画面上で追加した Button1 を選択し、オブジェクトインスペクタ上で、Button1 の Text プロパティの
値を「イメージの追加」に設定します。
21. 次にデザイナ画面上で Button1 をダブルクリックし、OnClick イベントを生成します。
22. 生成した OnClick イベントに以下のコードを記述します。
Delphi
if OpenDialog1.Execute then
begin
ClientDataSet1.Edit;
(ClientDataSet1.FieldByName('Image') as TGraphicField).LoadFromFile(OpenDialog1.FileName);
ClientDataSet1.Post;
end;
C++Builder
if (OpenDialog1->Execute()) {
ClientDataSet1->Edit();
((TGraphicField*)ClientDataSet1->FieldByName("Image"))->LoadFromFile(OpenDialog1->FileName);
ClientDataSet1->Post();
}
23. [ファイル|すべて保存] で保存します。
24. [実行|実行] で動作させ、何もイメージが表示されていないところで [イメージの追加] ボタンをクリックし、画
像イメージのファイルを指定すると、先程まで空白だったイメージの部分に、指定した画像イメージが表示され
ます。
5. 課題(時間に余裕のある受講者の方へ)
今まで学んだことを使用して、演習で作成したアプリケーションの動作を拡大してみましょう。
5-1. 課題

演習 5 で作成した 3D のピザアプリケーションですが、左側の画像の回転中にマウスでクリックすると、画像自
身は変わるのですが終点が途中になってしまうという問題があります。
回転のアニメーションの開始と終了の際に、マウスを捕捉しないように、あるプロパティを設定するコードを追
加し、この問題を解決してください。

演習 5 と演習 6 の応用で、フォーム上に StringGrid を使用して、魚の図鑑情報データベースのレコード(種別と一
般名と Special name のみ) を表示します。マウスで StringGrid 内のレコードをクリックすると、横に表示された
説明とイメージ部分が回転して該当するレコードの説明部分とイメージを表示するアプリケーションを作成して
ください。
1.
HD アプリケーションで作成した場合、回転する部分は TViewPort3D と TLayer3D を入れ子にして回転部分
を作成します。
2.
3D アプリケーションで作成した場合、TLayer3D を2つ用意し、一方は StringGrid、もう一方は説明とイメ
ージを扱うように設計します。
3.
アニメーションの回転方向は、好きな方向に回転させてください。
4.
製品付属のスタイルを使用します。
5.
イメージ部分には、表示効果を付けて鏡に映ったようにします。
5-2. 解答例

3D のピザアプリケーションですが、HitTest プロパティを使用します。 HitTest プロパティが True に設定されて
いる場合、マウスの OnClick イベントおよび OnDblClick イベントを捕捉します。
回転中は OnClick イベントを捕捉しないように、回転させるための TurnPage の先頭に
Delphi
Image3D1.HitTest := False;
Image3D2.HitTest := False;
C++Builder
Image3D1->HitTest = False;
Image3D2->HitTest = False;
のコードを記入して、このコンポーネントに対するクリック動作を無効にします。
あとは、FloatAnimation1 および FloatAnimation2 の OnFinish イベントを作成し、このイベント内で上記で False
にした部分を True にセットするコードを記述します。
Delphi
Image3D1.HitTest := True;
Image3D2.HitTest := True;
C++Builder
Image3D1->HitTest = True;
Image3D2->HitTest = True;

応用のアプリケーションですが、以下に HD アプリケーションとして作成した場合の手順を記します。
1.
[ファイル|新規作成|FireMonkey HD アプリケーション – Delphi]または[ファイル|新規作成|FireMonkey HD
アプリケーション – C++Builder] で設計を開始します。
2.
ツールパレットから、フォーム上に TClientDataSet, TDataSource, TBindScopeDB を置きます。
3.
ClientDataSet1 に対し「MyBase テーブルからの読み込み」を実行し、biolife_j.xml ファイルを選択して関連
付けます。
4.
DataSource1 の DataSet プロパティに ClientDataSet1 を、BindScopeDB1 の DataSource プロパティに
DataSource1 をそれぞれ関連付けます。
5.
フォーム上に TStringGrid, TStyleBook を置きます。
6.
StringGrid1 の「DB データソースへのリンク」で、BindScopeDB1 を選択して [OK]ボタンをクリックして関
連付けを行います。
7.
設計画面上で、StringGrid1 を選択し、マウスの右ボタンを押し「カラムエディタ」を選択します。
8.
カラムエディタ上で、マウスの右ボタンを押し「すべてのフィールドを追加」を選択します。
9.
追加されたフィールドの内 Category, Common_name, Species name 以外のフィールドは削除します。これ
で StringGrid1 上には 3 つのフィールドのみ表示されるようになります。
10. 構造ペイン上で StringGrid1|LiveBindings|DBLinkStringGrid11|Columns|Category を選択し、オブジェクトイ
ンスペクタ上で Header プロパティの値を Category から種別に変更します。
11. また、Width プロパティの値を 100 とします。
12. 同様に構造ペイン上で StringGrid1|LiveBindings|DBLinkStringGrid11|Columns|Common_name を選択し、オ
ブジェクトインスペクタ上で Header プロパティの値を Common_name から一般名に変更します。
13. また、Width プロパティの値を 180 とします。
14. さらに、構造ペイン上で “StringGrid1|LiveBindings|DBLinkStringGrid11|Columns|Speciaes name” を選択し、
オブジェクトインスペクタ上で Width プロパティの値を 180 とします。
15. 設計画面上で、StyleBook1 をダブルクリックします。
16. [読み込み]をクリックし、Styles ディレクトリ 内の AquaGraphite を選択し、[適用して閉じる]をクリック
します。
17. オブジェクトインスペクタ上で、Form1 を選択し、StyleBook プロパティに StyleBook1 を設定します。
18. フォーム上に TViewPort3D を置き、Align プロパティを alRight に、Width プロパティを 340 に設定しま
す。
19. この時点で、フォームの大きさと StringGrid と ViewPort3D の位置関係を調節します。
20. ViewPort3D1 上に TLayer3D を置き、Projection プロパティを pjScreen に、Align プロパティを alClient に
設定します。
21. Layer3D1 上に TMemo, TImageControl を置き、それぞれ「DB データソースへのリンク」で、Notes, Graphic
フィールドと関連付けます。
22. Memo1 の WordWrap プロパティの値を True にします。
23. ImageControl1 に TReflectionEffect を追加し、鏡のように表示される効果を追加します。
24. Layer3D 上の Memo1 と ImageControl1 の位置と大きさを調節します。
25. オブジェクトインスペクタ上で、StringGrid1 のイベントタグを選択し、OnClick イベントをダブルクリック
し、以下のコードを記述します。(回転の方向は一例です)
Delphi
Layer3D1.AnimateFloat('RotationAngle.Z',360,1,TAnimationType.atInOut, TInterpolationType.itSinusoidal);
Layer3D1.AnimateFloat('Position.Z',500,1,TAnimationType.atInOut, TInterpolationType.itSinusoidal);
Layer3D1.AnimateFloatDelay('Position.Z',0,1,1,TAnimationType.atInOut, TInterpolationType.itSinusoidal);
ReflectionEffect1.UpdateParentEffects;
C++Builder
Layer3D1->AnimateFloat(“RotationAngle.Z”,360,1,TAnimationType::atInOut, TInterpolationType::itSinusoidal);
Layer3D1->AnimateFloat(“Position.Z”,500,1,TAnimationType::atInOut, TInterpolationType::itSinusoidal);
Layer3D1->AnimateFloatDelay(“Position.Z”,0,1,1,TAnimationType::atInOut, TInterpolationType::itSinusoidal);
ReflectionEffect1->UpdateParentEffects();
26. 保存し、実行して動作を確認します。