画像の一元的なコレクションとしての TImageList 画像リストの使用
目次
FireMonkey の TImageList 画像リストでは、一元管理した小さい画像のコレクションを FireMonkey アプリケーションの GUI 要素(コントロールやメニューなど)で使用するための、機能の充実したツール群を提供しています。FireMonkey の TImageList 画像リストは、VCL の Vcl.Controls.TImageList 画像リストと同じ目的で使われますが、機能が進化しています。FireMonkey の TImageList 画像リストでは、多数のアイコンやビットマップの集合を効率的に管理することができ、マルチプラットフォーム アプリケーションで使用すると便利です。
FireMonkey はマルチプラットフォーム フレームワークである(Windows、iOS、macOS、Android に対応)ため、FireMonkey アプリケーションはさまざまな画面密度および画面解像度のデバイスで動作します。 FireMonkey 多重解像度ビットマップを使用すると、密度や解像度やプラットフォーム(Windows、iOS、macOS、Android)が異なっても、FireMonkey アプリケーションの画像やアイコンが正しく表示されます。 多重解像度ビットマップは、画像は同じだけれども倍率が異なるビットマップ項目を集めたものです。
画像リストを使用するよう推奨する理由
GUI アプリケーションでは、コントロール(ボタン、メニュー項目、さまざまなリストの項目など)の多くに小さい画像(アイコン、ピクトグラム、ウィジェットなど)を含めることができます。たとえば、TMenuItem には Bitmap というプロパティがあります。このプロパティを使用して、ファイルから画像を読み込むことができます。画像をアプリケーションに読み込むには、これが最もわかりやすく、おそらく最も便利な方法です。新しいコントロールごとに、固有の画像を読み込んで表示することができます。
その他に、アプリケーションで使用するすべての画像をコレクションに一元的に格納し、各コントロールにはそのコレクション内の画像に対する参照だけを持たせるという方法があります。スタイルに含まれる画像を使用する場合がそれに該当します。たとえば、ボタンの[オブジェクト インスペクタ]で StyleLookup プロパティを選択し、矢印をクリックしてドロップダウン リストを展開し、searchtoolbutton
を選択します。すると、ボタンに '拡大鏡' のアイコンが表示されます。この方法は、ほとんどの標準的なアイコンを含む固定化されたコレクションの場合に便利です。ただし、実際のアプリケーションでは、独自のアイコンを読み込んだり格納する必要があることが少なくありません。そのような場合には、TImageList コンポーネントがかなり役立ちます。
TImageList を使用する概略手順は以下のとおりです。
- フォーム(できれば特別に作成したデータ モジュールのもの)に TImageList 画像リスト(非ビジュアル コンポーネント)を配置します。
- 画像をこの TImageList オブジェクトに読み込みます。
- [オブジェクト インスペクタ]で、コントロールの Images プロパティを選択し、矢印をクリックしてドロップダウン リストを展開し、TImageList オブジェクトの参照を選択します。
- ImageIndex プロパティで、表示したい画像の(画像リスト オブジェクト内での)番号を選択します。
新しいコントロールごとに、ステップ 3 ~ 4 を繰り返します。TImageList オブジェクトに必要な画像が含まれていない場合には、ステップ 2 を繰り返します。どこかのフォルダにある必要な画像を単に選択するのではなく、この手順をすべて実行して TImageList を使用する方法の利点を説明するために、以下では TImageList の使用例を紹介します。
最初の例は次のようなものです。経験豊富なプログラマの場合、集めた画像の数は 9000 ~ 10000 を超えることがあります。そのような場合に、具体的な望みの画像をコレクションから探し出すことは困難です。それに対して、相当に複雑なアプリケーションでも、使用する画像は多くて 100 ~ 200 個です。アプリケーション内の多くのコントロールでは同じ(あるいはよく似た)画像を使用しますし、それらの画像の使い方に一貫性を持たせるのは望ましいことです。たとえば、1 年ほど前にプログラムの最初のバージョンを開発したときに、当時の同僚が保存ボタンに '青い 3 インチ ディスケット' のアイコンを割り当てていたとします。今度プログラムの新しいバージョンを開発することになって、同じような保存ボタンを持つ新しいフォームを作成し、そのボタンに同じアイコンを割り当てる必要があるとします。ただし、このアイコンを見つけるのは困難かもしれません。TImageList オブジェクトを使用している場合には、この問題は発生しません。TImageList オブジェクトを展開すると表示されるドロップダウン リストから、このアイコンを選べばいいだけだからです。
次に 2 つ目の例です。旧式の白熱電球のアイコンではなく、最新の節電型の電球のアイコンを使う時期だと、上司が判断したとします。いくつのコントロール(およびメニュー項目)でこのアイコンを使用しているでしょうか。すべてのアイコンを変更するのにどれだけの時間が必要でしょうか。すべてのアイコンが 1 つの画像のコレクションに一元的に格納されているのであれば、このような大量の置き換えも簡単にできます。一元管理している画像コレクション内のアイコンを変えるだけでよいからです。 さらには、画像のコレクション(画像リスト)を含むデータ モジュールを作成し、コレクション内の画像を使用する複数のアプリケーションにそのモジュールを追加することも可能です。
3 つ目に短い例を挙げます。アイコンを表示する多数のコントロールを実行時に動的に作成する場合には、多数のグラフィカル データを重複して持つことになります。TImageList を使用すると、各コントロールで保持するのは、描画するアイコンの TImageList コレクション内でのインデックスだけでよくなります。
このように、共通の TImageList コレクションを使用すると、グラフィカル データに関して 'コードの再利用' のようなことを行うことができます。
画像リストの使用方法
FireMonkey プロジェクト内に新しい画像リストを作成するための一般的な手順を解説したビデオや画像リスト エディタを使用して画像リストを編集する方法を解説したビデオが公開されています。
TImageList の使い方の詳細な手順は、次のようになります。
- データ モジュールを作成し、プロジェクトのモジュール リストの中でメイン フォームより前の位置に置きます。
- データ モジュールのユニット名を、メイン モジュールの uses セクションに追加します。
- [ツール パレット]を使用して、データ モジュールに TImageList オブジェクトを配置します。
- TImageList オブジェクトをダブルクリックします。
空の画像リスト エディタ(FireMonkey)が開きます。 - [追加]ボタンをクリックします。
- [イメージの追加]ダイアログ ボックスで、使用したいグラフィック ファイルを探して選択し、[開く]をクリックします。
- 開いた画像すべてのプレビューが[画像のリスト]ペインに表示されます。画像の順序は、マウスでドラッグして変更することができます。
- メモ: TImageList コンポーネント自体には、全体としての幅や高さのプロパティはありません。各画像は、その画像を表示するコントロールの形に合わせて拡大/縮小されます。[画像のリスト]ペインのサイズを変更すると、それに合わせて画像のサイズが変更されることがわかります。
- メモ: どのようなサイズの画像でも追加することができますが、TImageList は、さまざまな GUI 要素(ボタン、メニュー項目、リストなど)に表示できる小さい多数の画像を保持することを意図して作られています。デフォルトでは、画像は変更なしで格納されます。そして必要に応じて拡大/縮小されます。そのため、サイズの大きい画像や写真を読み込んだ場合、24×24 のボタンには期待したとおりに表示されないかもしれませんし、モバイル デバイス上のアプリケーションではシステム リソースが足りなくてハングする可能性があります。
- [OK]をクリックして画像リスト エディタを閉じます。
- [ツール パレット]から、TGlyph グラフィカル コントロールを追加します。TGlyph の機能は TImage と似ていますが、グラフィカル データは含みません。
- [オブジェクト インスペクタ]で、グリフ コントロールの Images プロパティを探します。Images のリストには、プロジェクト内で見えているすべての TImageList コレクションが表示されます。
- 使用したい TImageList コレクションを選択したら、ImageIndex プロパティを探します。ImageIndex は、画像リスト内における画像のインデックス(番号)です。ImageIndex のリストには、選択した TImageList コレクションに含まれるすべての画像が表示されます。表示したい画像を選択するか、その画像の整数のインデックス(ImageIndex)を入力します。指定した ImageIndex の画像が存在しない場合には、画像は選択されません。
TGlyph は、TControl を継承しており、コントロールを作成するためのスタイルで使用できます。TGlyph は、画像リストの使用をサポートしている多くのコントロールに含まれています。
TGlyph コントロールには AutoHide プロパティがあります。AutoHide は、TGlyph コントロール自体で Visible プロパティの値を管理するかどうかを示します。
- アプリケーションの実行時に AutoHide が
True
であれば、コントロールにグラフィックが含まれている(BitmapExists がTrue
である)場合に Visible がTrue
になります。それ以外の場合には Visible はFalse
になります。アプリケーションのコードで Visible に新しい値を設定しようとしても、その値は無視されます。Visible の値が Align プロパティに影響するのは、Align がNone
以外の場合です。その場合、Visible がTrue
であれば、コントロール用の空の領域に、空の目に見えるコントロールが描画されます。Visible がFalse
であれば、空の領域には空の目に見えないコントロールが描画されません。 - デザイン モードのときに AutoHide が
True
であれば、Visible は常にTrue
になり、変更できません。
多くのグラフィカル コントロール(ボタン、メニュー、リストなど)やアクションに、Images および ImageIndex という新しいプロパティが表示されます。ただし、要素のリストを持つコンポーネントの場合は、[オブジェクト インスペクタ]に Images プロパティしか表示されません。一方、そのリストの要素に対しては、[オブジェクト インスペクタ]に ImageIndex プロパティしか表示されません。リスト コントロールのすべての要素の Images プロパティの値は、リスト コントロールの Images の値と同じです。たとえば、メニューの Images プロパティに設定された同じ Images コレクションを、すべてのメニュー項目が常に使用することになります。
Images や ImageIndex プロパティを持つコントロールの中には、イメージを参照することのできる、Bitmap プロパティを持つものもあります。Images や ImageIndex プロパティが既存のイメージを指している場合、このイメージは描画されます。ImageIndex が既存のイメージを指していない場合、コントロールは Bitmap が参照するイメージを描画します。Bitmap から参照されているイメージが使用されてない場合、ただ無視されるのみで、データの紛失を避けるために自動的に削除されることはない点に留意してください。たとえば、ImageIndex を存在していないイメージを参照するように設定したとします。このイメージ参照は、Bitmap プロパティを編集することで、手動で削除することができます。
TImageList の最上位の構造
一般に、FireMonkey アプリケーションでは、画像を描画するのに必要なサイズを把握していません。このサイズは、具体的なコントロールや、コントロールのスタイルや、シーンの縮尺(この縮尺は 1、1.5、2 だけとは限りません)によって異なり、これらはさらに、コンピュータやモバイル デバイスによって異なります。つまり、望ましい境界に合わせて画像が拡大/縮小されることがよくあります。そのため、私たちは TMultiResBitmap クラスを導入しました。多重解像度ビットマップには、同じ画像をさまざまなサイズに最適化したものが複数格納されます。これによって、画像を拡大/縮小したときの質の低下を最小限にできます。さらに次の段階として、私たちは TImageList 画像リストを導入しました。TImageList オブジェクトには、TMultiResBitmap 画像のコレクションが格納されます。そのそれぞれの多重解像度ビットマップは、同じ画像を元にサイズを変えたものの集合です。
画像リスト エディタの要素の意味と機能を理解するには、TImageList コンポーネントの構造を確認する必要があります。
それぞれの TImageList コンポーネントには、次の 2 つの画像コレクションが含まれています。
- Source コレクション: ソースであるグラフィカル データを含みます。このデータを使って、Destination コレクション内の画像が作成されます。
- Destination コレクション: TImageList コレクションの画像として描画する画像を形成するのに必要なデータを含みます。
提供されている ImageList サンプルの[構造]ビューには、次の図のように、ImageList1
の構造が表示されます。
この図を見ると、Source コレクション内のそれぞれの画像には以下が含まれていることがわかります。
- 大文字/小文字を区別しない(各 TImageList コレクション内で)一意の名前。たとえば、画像
0
の名前はStop
、画像1
の名前はTImageList
、画像2
の名前はfolder
です。 - MultiResBitmap プロパティ。この MultiResBitmap コレクション内の各要素は、同じ画像を特定のサイズ(縮尺)に変更したものを表します。たとえば、画像
folder
の MultiResBitmap コレクションには、縮尺1.0
、2.0
、1.5
、3.0
、16.0
用の 5 つのビットマップが含まれています。
この図から、Destination コレクション内の各要素には、複数のレイヤ(Layers コレクションにまとめられています)を持たせることができることもわかります。たとえば、画像 3
は 2 つのレイヤから構成されています。レイヤ 0
には folder
多重解像度ビットマップが、レイヤ 1
には delete
多重解像度ビットマップが含まれています。各レイヤには、ソースの多重解像度ビットマップの名前と、このビットマップ(縮尺は常に 1
)内のあるセクションの座標とが含まれます。ビットマップのこのセクションを描画して画像が生成されます。画像を生成する際、FireMonkey はまず 0
レイヤの多重解像度ビットマップを描画し、その画像の上に 1
レイヤの多重解像度ビットマップを描画し、その画像の上に 2
レイヤの多重解像度ビットマップを描画するというように、順に描画を行います。これについては、「[選択した画像]ペイン」のセクションで詳しく説明します。レイヤ内の多重解像度ビットマップに複数の縮尺のビットマップが含まれている場合、最も適した縮尺のビットマップが使われます。この縮尺は、必要な既製画像のサイズとシーンの縮尺に応じて選択されます。
TImageList の構造を理解していると、TImageList インスタンス全体の一部分を実行時に作成することができます。ImageList サンプルには、そのようなコードの一例が含まれています。
IDE で TImageList コレクションを編集する場合、[構造]ビューと[オブジェクト インスペクタ]を使用して行うことができます。ただし、専用の画像リスト エディタも提供されています。TImageList コレクションの内容を管理するには、こちらの方がずっと便利です。
VCL の TImageList 実装との違い
VCL の Vcl.Controls.TImageList 画像リストは、固定された同じサイズの画像のコレクションです。そのため、縮尺の異なる画像をアプリケーションで描画しようとしたときに問題が発生します。VCL アプリケーションで縮尺の異なる画像を描画するには、複数の TImageList オブジェクトを作成する必要があります。各 TImageList オブジェクトには、特定のサイズの画像だけを集めたコレクションを含めます。そのすべての TImageList オブジェクトの中で、同じ画像は同じ番号(インデックス)でなければなりません。VCL アプリケーションでは、それらの縮尺の画像しか描画できません。VCL アプリケーションで実行時に画像を変更するのは困難です。
画像リスト エディタの使用方法
画像リスト エディタを使用して画像リストを作成および編集する方法を解説したビデオが公開されています。このビデオでは、画像リスト エディタの高度で重要な機能を実演しています。
画像リスト エディタのプロパティについては、ImageList サンプルで説明しています。これは画像リストの使い方の例となっています。ImageListDemo.dproj プロジェクトを読み込み、[プロジェクト マネージャ]で UnitDataModule.dfm モジュールをダブルクリックします。フォーム デザイナで ImageList1 アイコンをダブルクリックします。画像リスト エディタが開き、ImageList1 コンポーネントが表示されます。画像リスト エディタには、[画像のリスト]、[画像のソース]、[選択した画像]という 3 つのペインがあります。
[画像のリスト]ペイン
[画像のリスト]ペインには、画像のリストが横に並んで表示されます。各画像は、Destination コレクション内の対応する画像のプレビューです。これらの画像は、この画像コレクションを使用するコントロールの ImageIndex プロパティに表示されます。
[画像のリスト]ペインでマウスを使って画像をドラッグすると、ImageList1 画像コレクション内での画像の順序を変更することができます。
[追加]ボタンを使って、ファイルから画像を追加することができます。[追加]をクリックすると[イメージの追加]ダイアログ ボックスが開き、そこで画像ファイルを探して選択することができます。追加する画像のサイズが指定した[幅]および[高さ]の倍数であれば、その画像を複数の画像に分割するかどうかを尋ねる確認画面が画像リスト エディタから表示されます。これはツールバーのビットマップのときに便利です。これらは通常、順に並んだ複数の小さい画像で構成され、1 つの大きいビットマップとして格納されているためです。追加した画像は、[画像のリスト]ペインのプレビュー リスト内で強調表示され、[選択した画像]ペイン内に表示されます。画像が Source コレクションに追加され、[画像のソース]ペインに表示されることに注意してください。
[エクスポート]ボタンを使用すると、Destination コレクションのすべての画像を 1 つのグラフィック ファイルに保存することができます。このファイルに格納される画像は、[画像のリスト]ペインのすべての画像を含む部分から構成されます。すべての部分は、[幅]と[高さ]で指定されたサイズと同じサイズです。
[削除]ボタンでは、選択した画像を Destination コレクションから削除することができます (ただし、[削除]によって Source コレクションから対応する画像が削除されることはありません)。
(イメージの新規作成)ツールバー ボタン(ペインの左上端にある)は、読み込み済みのイメージ(レイヤを 1 つ持っている)を追加します。[画像のソース]ペインにフォーカスがある場合には、選択されている画像が追加されます。 それ以外の場合には、[選択した画像]ペインで選択されているレイヤの画像が追加されます。 追加された画像は、[画像のリスト]ペイン内で強調表示されます。
[画像のソース]ペイン
[画像のソース]ペイン(上の画像リスト エディタの図を参照)には、現在の TImageList 画像リストの Source コレクションに含まれるすべての画像が表示されます。このソース画像は、Destination コレクションの画像([画像のリスト]ペインに表示されるもの)を作成するために使われます。
既に述べたように、[画像のリスト]ペインで[削除]をクリックすると、選択した画像が Destination コレクションから削除されます。 ただし、[削除]をクリックしても、[画像のソース]ペインに表示された Source コレクションから対応する画像が削除されることはありません。 Source コレクションから画像を削除するには、ツールバーの[画像のソースから選択した項目を削除]ボタン()を使用してください。
ツールバーの と の矢印ボタンを使って、ソース画像を上下に移動し、順序を変更することができます。 これは、使い勝手を考慮して提供されている機能です。
(ソースの新規作成)ツールバー ボタンをクリックすると、新しいイメージを、Source コレクションに追加することができます。このボタンをクリックすると、[新しいソース項目名]を入力する画面が表示され、MultiResBitmap エディタがアクティブになります。
MultiResBitmap エディタの使用方法
MultiResBitmap エディタの典型的な使用方法を解説したビデオが公開されています。
MultiResBitmap エディタを使用して、異なる縮尺用に最適化された複数の画像を追加することができます。
実行時にアプリケーションによって、最も適切な縮尺の画像が選択されます。
既に述べたように、TImageList コンポーネントは多数の小さい画像のリストを格納するためのものです。TImageList コンポーネント内の画像は、コントロールやメニューやリストなどのアイコンとして使われると想定されています。
サイズの大きい画像を含んだファイルしかない場合には、追加時に MultiResBitmap エディタで画像のサイズをカスタマイズすることができます。ファイルから大きい画像を追加する前に、SizeKind のコントロールをクリックし、表示されたリストから[カスタム サイズ]を選択します。[幅]と[高さ]の編集ボックスが(まだ表示されていない場合には)表示されます。この[幅]と[高さ]によって、Scale = 1
の画像の Width プロパティと Height プロパティが決まります。たとえば、Scale = 1.5
用に画像を読み込むと、指定した[幅]と[高さ]が 1.5
倍されます。特定の縮尺の画像を読み込むには、その縮尺のエントリの ボタンをクリックします。すべての縮尺用に画像を読み込むには、MultiResBitmap エディタのツールバーの[ファイルからすべてに入力]ボタン()をクリックします。
[カスタム サイズ]オプションを指定しても、ソース ファイル内の画像は変更されません。[カスタム サイズ]を指定すると、.fmx ファイルまたは .dfm ファイルに格納される画像のサイズが変わるだけです。
MultiResBitmap エディタが使用する設計時情報には、透明色、SizeKind、幅、高さ、ソース画像が含まれるファイルの名前などがあります。これらのプロパティは可視性が published ではなく、設計時にのみ使用されます。多重解像度ビットマップの編集が終わったら、ツールバーの[設計時の情報をクリアし閉じる]ボタン()をクリックして、これらのプロパティを .fmx ファイルまたは .dfm ファイルから削除することができます。MultiResBitmap エディタの をクリックすると、MultiResBitmap エディタで編集中の多重解像度ビットマップだけの設計時情報がクリアされます。画像リスト エディタの をクリックすると、現在の画像リストに含まれるすべての画像の設計時情報がクリアされます。
[選択した画像]ペイン
[選択した画像]ペイン(上の 画像リスト エディタの図を参照)には、[画像のリスト]ペインで選択した画像についての詳細情報が表示されます。既に述べたように、[画像のリスト]には、Destination コレクションに含まれる画像のプレビューが表示されます。
TImageList の構造のセクションで説明しましたが(上の [構造]ビューの図を参照)、Destination コレクション内の各画像には複数のレイヤが含まれている可能性があります。各レイヤには、Source コレクション内の 1 つの画像の名前と、それに対応する多重解像度ビットマップが表示されます。また、各レイヤには、この多重解像度ビットマップ内のセクションの座標(使用するビットマップの縮尺に関係なく常に縮尺 1 で)も含まれます。多重解像度ビットマップのこのセクションだけが既製画像に描画されます。
通常は、各画像に 1 つのレイヤしか含まれません。ただし、場合によって複数のレイヤから複合画像を作成したいこともあります。たとえば、'取り消し線' や '拡大鏡' の画像を含む別レイヤを使用すると便利な場合があります。このレイヤを別の画像を含むレイヤの上に置いて使用します。たとえば、このレイヤの画像を 'フォルダ' や '封筒' の画像を含むレイヤの上に描画することができます。すると、2 つのレイヤが 1 つになって、'取り消し線の付いたフォルダ' や '拡大鏡で覗いた封筒' の画像ができます。
画像を生成する際、FireMonkey はまず 0 レイヤの多重解像度ビットマップを描画し、その画像の上に 1 レイヤの多重解像度ビットマップを描画し、その複合画像の上に 2 レイヤの多重解像度ビットマップを描画するというように、順に描画を行います。レイヤで指定された名前の画像が存在しない場合や、指定されたセクションが画像の外部にある場合、レイヤは何も描画せず、例外も発生しません。レイヤ内の多重解像度ビットマップに複数の縮尺のビットマップが含まれている場合、最も適した縮尺のビットマップが使われます。この縮尺は、必要な既製画像のサイズとシーンの縮尺に応じて選択されます。
ImageList サンプルの画像を使ってこれらの機能を紹介しましょう。[画像のリスト]ペイン(上の「画像リスト エディタの使用方法」を参照)で、ImageIndex = 3
の画像を選択します。これが '取り消し線の付いたフォルダ' です。[選択した画像]ペインには、次の 2 つのレイヤが表示されます。
これと、[構造]ビューに表示された ImageIndex = 3
の Destination 画像とを比較してください。
この図を見ると、folder
画像が 0
レイヤに、delete
画像が 1
レイヤに含まれていることがわかります。[画像のリスト]ペインには、この 2 つのレイヤから作成された、次の '取り消し線の付いたフォルダ' の Destination 画像が表示されます。
1
レイヤの delete
画像が、0
レイヤの folder
画像の上に描画されています。
レイヤ ペインの[左]、[上]、[幅]、[高さ]の編集ボックスでは、レイヤに格納される多重解像度ビットマップのセクションの座標を指定します。「取り消し線の付いたフォルダの[選択した画像]ペイン」の図では、この座標は delete
画像で (0,0,16,16)
になっています。delete
画像の[左]を 4
に、[上]を 4
に設定してみましょう。[適用]をクリックします。すると、[構造]ビューで、delete
画像のセクションの座標が (4,4,20,20)
と表示されます。[画像のリスト]ペインでは、'取り消し線の付いたフォルダ' の Destination 画像が変化します。'取り消し線' の画像が次のように左上に移動しています。
多重解像度ビットマップのセクションの座標の使い方を確認するために、'取り消し線' の画像をダブルクリックします。MultiResBitmap エディタが開きます。
この図は、'取り消し線' 画像のどのセクションをレイヤに描画するかを示しています。'表示セクション' を示す選択箇所の長方形をクリックします。選択箇所の長方形のサイズを変更してみてください。選択箇所の長方形を移動すると、それに応じて、[選択した画像]ペインのレイヤでは長方形の[左]、[上]、[幅]、[高さ]のプロパティが変化し、[画像のリスト]ペインではプレビュー画像が変化します。
[選択した画像]ペインのツールバー ボタンの機能は、見てすぐ分かるものばかりです。 -- 新しいレイヤを[選択した画像]ペインに追加します。 -- 選択したレイヤを[選択した画像]ペインから削除します。 および の矢印を、選択したレイヤを上下に移動させ、レイヤの順番を変更することができます。前のレイヤで描画された画像の上に数の大きいレイヤの画像が描画されるため、この機能が役立つ場合があります。
TImageList の基底クラス
画像リスト クラスの基本機能の情報を知っていると、画像リストを使ったカスタム コンポーネントの開発時に役立ちます。
System.ImageList ユニット
RAD Studio では、XE8 バージョンから新しい System.ImageList RTL ユニットを提供しています。System.ImageList には、画像リストの、デバイスに依存しない最も基本的な機能を実装する、FireMonkey と VCL に共通のコードが含まれています。 System.ImageList には、画像リスト内の画像とそれを使用するコンポーネント(コントロールやメニュー項目など)との相互作用をサポートするコードが含まれています。
System.ImageList ユニットでは、TBaseImageList クラスと TImageLink クラスが定義されています。
TBaseImageList クラス
TBaseImageList クラスは、イメージ リストと、そのイメージ リストのイメージを使用するすべての GUI コンポーネントの間のやり取りを処理する、メソッドやプロパティを提供します。 RAD Studio XE8 バージョンより、FMX.ImgList.TCustomImageList と Vcl.ImgList.TCustomImageList クラスは、この TBaseImageList クラスを継承しています。
TImageLink クラス
TBaseImageList 画像リスト オブジェクトは、TImageLink オブジェクトを使用して、画像リスト オブジェクト内で生じた変更についての通知を、画像リスト オブジェクトを使用しているコンポーネントに送信します。
ImageIndex プロパティや Images プロパティが変化すると、TImageLink オブジェクトは仮想メソッド Change を呼び出します。TBaseImageList がどのように変化した場合でも、Images が変化することを覚えておいてください。
画像リスト内の変化に対してコンポーネントで適切な反応をするには、コンポーネントに TImageLink の下位クラスのインスタンスを含んでおく必要があります。この TImageLink の下位クラスには正しい Images プロパティおよび ImageIndex プロパティが必要で、さらに Change メソッドをオーバーライドするか OnChange イベント ハンドラを定義する必要があります。
TBaseImageList には Links という配列プロパティがあり、そこには TImageLink のインスタンスが含まれます。TImageLink.Images プロパティにコンポーネントを設定すると、その画像リストがこの Links 配列に追加されます。 Images または ImageIndex が変化すると、TBaseImageList の下位オブジェクトは、Links 配列から画像リンク オブジェクトを順に取り出し、そのすべての(または一部だけの)画像リンク(コンポーネント)の Change を呼び出します。
TImageLink.IgnoreIndex プロパティ
IgnoreIndex プロパティが True
であれば、TBaseImageList 画像リストで画像や画像の順序が変化しても、Change は実行されません。通常は、あるインデックスの画像が変化すると通知が届きます。しかし、IgnoreIndex を True
に設定すると、その通知が送られなくなります。このプロパティは、アプリケーションで編集ウィンドウに画像リスト全体を表示する必要がある場合などに使用できます。
TImageLink.IgnoreImages プロパティ
IgnoreImages プロパティが True
の場合、Images 値が変更されても、Change は実行されません。このプロパティは、System.ImageList からクラスを継承している、VCL クラス群と互換性を持たせるために使用されます。
FMX.ImgList ユニット
FMX.ImgList ユニットでは、FireMonkey 画像リストを実装する基本的な FireMonkey クラスが定義されています。
FMX.ImgList.TCustomImageList クラス
FMX.ImgList.TCustomImageList コンポーネントでは、FireMonkey 画像リストの中核機能を実装しています。もともと、FMX.ImgList.TImageList と FMX.ImgList.TCustomImageList クラスとの違いは、一部のプロパティを Published と再宣言していることだけです。FMX.ImgList.TImageList では、Source、Destination、OnChanged、OnChange のプロパティが Published になっています。
TCustomImageList.Dormant プロパティ
Dormant プロパティは、Source コレクション内のすべての多重解像度ビットマップの Dormant プロパティが True
の場合に True
になります。
TCustomImageList.CacheSize プロパティ
CacheSize プロパティは、現在の TCustomImageList オブジェクトの内部キャッシュに格納できる最大画像数を示します。
それぞれの画像を描画するには、かなりリソースを消費する手順が必要です。いくつものコントロールに同じ画像を表示するたびに複数のレイヤを拡大/縮小して描画することを避けるには、生成した画像を内部キャッシュ内の配列に格納し、その既製画像をキャッシュ配列から取り出して使用します。新しく生成された画像はこのキャッシュ配列の末尾に追加され、すべての既存画像が配列の先頭に向けてずらされます。キャッシュ配列に格納する画像数が CacheSize に指定した画像数を超えると、最も古い画像がキャッシュから削除されます。CacheSize の数が増えるほど処理速度はもちろん向上しますが、メモリ使用量も増えます。キャッシュ配列に格納する最大画像数のデフォルト値は 8 です。最小数は 1 です。
TCustomImageList.ClearCache メソッド
ClearCache は、Index
の数値で指定された画像を、内部キャッシュ配列から削除します。キャッシュでは、サイズの異なる複数の画像が同じ数値に格納されていることがあります。Index
が -1
の場合(デフォルト)、すべての画像がキャッシュから削除されます(キャッシュがクリアされます)。通常、TImageList オブジェクトが変化すると、ClearCache は自動的に呼び出されます。
TCustomImageList.BitmapExists メソッド
BitmapExists は、指定された数値の画像が存在し、実際のビットマップを含んでいる場合に、True
を返します。つまり、既存のソース画像の Name を含み、かつ画像の長方形と少しでも交差する描画対象の長方形を持つレイヤを、指定された画像が少なくとも 1 つ含んでいる場合です。
画像が存在しない場合は、画像リスト エディタには空の破線の長方形が表示されます。上の画像リスト エディタの図で、[画像のリスト]ペインの 9
番の画像を見てください。描画対象の長方形にグラフィカル データが含まれているけれども、そのグラフィックがどれも透明である([透明色]を参照)場合、BitmapExists メソッドは True
を返しますが、このレイヤにはグラフィックは描画されません。BitmapExists は、TGlyph クラスで AutoHide が True
の場合に Visible プロパティを設定するために使われます。
TCustomImageList.Bitmap メソッド
Bitmap メソッドは、キャッシュに格納された TBitmap オブジェクトのうち、Index
で指定された画像に対応する、指定されたサイズ Size
のものを返します。
Index
で指定された画像でサイズが Size
のものがキャッシュ内に存在しなければ、Bitmap は適切な画像を作成してキャッシュに追加することを試みます。Index
の画像が Source コレクション内に存在しない場合や、指定された Size
サイズのビットマップを Bitmap が作成できない、あるいはキャッシュに追加できない場合、Bitmap は nil
を返します。
キャッシュ内の画像は TCustomImageList 内部の機能で作成および破棄されるため、返された TBitmap オブジェクト(キャッシュに格納されたもの)を明示的に破棄したり、そのオブジェクトの参照を保存してはなりません。
TCustomImageList リスト内の画像を単に描画する必要があるだけなら、Draw メソッドを使用してください。取得した TBitmap オブジェクトを保存して後で使用する必要が本当にあるなら、別の TBitmap オブジェクトを作成し、Assign を使用して、取得した TBitmap オブジェクトのコピーを作成してください。
TCustomImageList.BitmapItemByName メソッド
BitmapItemByName メソッドは、指定された Name
の画像が Source コレクション内に見つかれば True
を返します。
BitmapItemByName が True
を返す場合、Item
には縮尺 1 に最も適したビットマップが、Size
にはそのビットマップのサイズが返されます。BitmapItemByName が False
を返す場合、Item
と Size
は変化しません。
TCustomImageList.UpdateImmediately メソッド
UpdateImmediately メソッドは、この画像リストを使用しているすべてのコンポーネントの再描画を即座に開始します。
画像が変更された場合、その画像を使用しているすべてのコンポーネントは、画像の更新が必要であるという通知を受け取るはずです。 その通知は、画像リストの変更中に何度も再描画を行うことがないよう、デフォルトでは変更が終わった後ですぐに送信されます。その変更の間に複数回変更があった場合でも、送信される通知は 1 つだけで、コントロールも 1 度しか再描画されません。
TCustomImageList.Source プロパティ
Source プロパティは TSourceCollection コレクションの参照を保持し、そこにはソースの多重解像度ビットマップである TCustomSourceItem が含まれます。Source コレクションは CreateSource メソッドで作成されます。このメソッドをオーバーライドすると、独自の型のコレクションを作成することができます。TSourceCollection ソース コレクションの重要なメソッドに、IndexOf があります。これは、指定された Name
に対応するソース コレクション内の画像のインデックスを返します。画像が見つからなければ nil
を返します。
TCustomSourceItem クラス
TCustomSourceItem クラスは、TSourceCollection ソース コレクションの要素を定義したものです。それぞれの TCustomSourceItem オブジェクトには、Source コレクションに含まれる画像項目の大文字/小文字を区別しない一意の Name と、同じ形だけれども縮尺が異なる画像のセットを保持する多重解像度ビットマップが含まれる MultiResBitmap プロパティとがあります。
TCustomImageList.Destination プロパティ
Destination プロパティは TDestinationCollection コレクションの参照を保持し、そこには TCustomDestinationItem 項目が含まれます。Destination 項目には、画像を描画する準備に必要な情報が含まれます。Destination コレクションは CreateDestination で作成されます。
TCustomDestinationItem クラス
TCustomDestinationItem オブジェクトは TDestinationCollection コレクションの要素です。それぞれの TCustomDestinationItem 要素には、TLayer 要素のコレクションである Layers が含まれます。Layers は CreateLayers で作成されます。LayersCount 関数は、空でないレイヤの数を返します。TLayers.Count プロパティ(レイヤ コレクション内のレイヤの総数を保持)との違いに注意してください。
TLayers クラス
TLayers コレクションには TLayer レイヤが含まれます。既製画像の生成時には、目に見えるすべてのレイヤの最も適切な縮尺のビットマップが、0 レイヤから最後の Count レイヤまで順に描画されます。
TLayer クラス
TLayer レイヤには、ソース画像についての情報が含まれます。この情報は、既製画像の描画時に使用されます。
TLayer.MultiResBitmap プロパティ
MultiResBitmap プロパティは、Source コレクションの多重解像度ビットマップを保持します。
TLayer.Name プロパティ
Name プロパティは、Source コレクションの画像の、大文字/小文字を区別しない名前を保持します。BitmapItemByName 関数は、Source コレクション内の多重解像度ビットマップの中で名前が Name のものを返します。
TLayer.SourceRect プロパティ
SourceRect プロパティは、この多重解像度ビットマップ内のセクションの座標(使用するビットマップの縮尺に関係なく常に縮尺 1)を保持します。多重解像度ビットマップのこのセクションだけが既製画像に描画されます。
FMX.TImageList コード サンプル
「FMX.ImageList サンプル」でプログラミングされている機能を解説したビデオが公開されています。
ここでは、画像リストの使い方の例である FMX.ImageList サンプルから、画像リストの標準的な操作のプログラミングに使用する典型的なコードを取り上げて説明します。
ImageListDemo.dproj プロジェクトを開きます。方法は FMX.ImageList サンプルを参照してください。UnitMain モジュールのフォーム デザイナを開き、[Bitmap and Image]タブを開きます。このタブには、画像リストの画像を使ったコントロールがいくつか含まれています。ここでは、[Add New Source]ボタンおよび[Update Text]ボタンと、[None]タブの[None]キャプションの上に ImageList1
画像リスト内の次または前の画像を描画する右矢印および左矢印のボタンについて、その機能を実装するコードを説明します。右矢印と左矢印のボタンを処理するコードを見ると、カスタム コントロールの表面に画像リスト内の画像を描画する方法がわかります。
コントロールの処理をするコードをコード エディタで開くには、フォーム デザイナでコントロールをダブルクリックしてください。
[Add New Source]ボタンのイベント ハンドラ
フォーム デザイナで[Add New Source]ボタンをダブルクリックします。コード エディタに ActnAddSourceExecute メソッドの実装が表示されます。ActnAddSourceExecute は、[Add New Source]ボタンの OnClick イベント ハンドラです。ActnAddSourceExecute では AddSourceToItem(9)
を呼び出しています。ActnAddSourceExecute の主要部分は、内部的に呼び出されている
DrawPicture(Canvas: TCanvas; R: TRectF; Scale: Single)
というメソッドです。このメソッドは、指定された Canvas
に画像を描画しているだけです。AddSourceToItem(9)
の他の部分のコードでは、DrawPicture
を呼び出すためのパラメータの準備をしています。AddSourceToItem(9)
メソッドでは、指定されたパラメータ値 9
を、ImageList1
に追加する新しい画像の ImageIndex
に割り当てます。つまり、ImageIndex=9
となります。AddSourceToItem
は、まず、Destination コレクション内の ImageIndex=9
の画像に少なくとも 1 つの Layer があるかどうかを確認します。なければ 0 Layer を作成します。0 Layer に画像が含まれていれば、その画像を SourceName
変数に格納します。
SourceName := Layer.Name;
そして BitmapItemByName を次のように呼び出し、
MainDataModule.ImageList1.BitmapItemByName(SourceName, Item, Size)
指定した名前 SourceName
に対応する要素を Source コレクションから取得します。これが成功した場合には、画像は既に追加されていると見なし、何も処理を行いません。成功しなければ、Source コレクションに新しい画像を追加します。Layer が存在してそこに SourceName
という名前が含まれている場合には、追加する画像に SourceName
を割り当てます。それ以外の場合にはデフォルトの名前を使用します。
次に、縮尺の異なる複数の画像を、繰り返し処理で作成します。新しい要素を TMultiResBitmap に追加します。デフォルトの縮尺を取得します。
S := Item.Scale;
S
と Layer の SourceRect を使用して、Source 画像のサイズを計算します。
Size.cx := Round(Layer.SourceRect.Width * S);
その後、透明色で画像を塗りつぶします。
Item.Bitmap.Clear(TAlphaColorRec.Null);
そして画像を描画します。
DrawPicture(Item.Bitmap.Canvas, R, S);
-
- メモ: フォーム デザイナで UnitDataModule ユニットを開き、
ImageList1
の画像をダブルクリックしてください。[画像のリスト]ペインを見ると、ImageIndex=9
の画像が空であることがわかります。
- メモ: フォーム デザイナで UnitDataModule ユニットを開き、
つまり、現在の実装では、[Add New Source]をクリックすると、ImageIndex=9
の新しい画像が ImageList1
画像リストに追加されます。
[Add New Source]ボタンの機能を確認する手順:
- FMX.ImageList サンプルの使い方を読みます。
- [Demo of images]アプリケーションで、[Bitmap and Image]タブを選択します。
- 右矢印ボタンを何度かクリックします。
ImageList1
画像リストの画像が、[None]タブの[None]キャプションの上に順に描画されます。 - 右矢印ボタンを 9 度目にクリックすると、
Label1
ラベルのテキストが次のようになります。
-
-
Images: 'ImageList1': Counts 11
ImageIndex: 9; Width 16; Height 16
- そして、[None]キャプションには空の画像が描画されます(画像が描画されません)。
-
- [Bitmap and Image]タブで[Add New Source]をクリックします。
ImageIndex=9
の画像がImageList1
に追加されます。 - 追加された画像が[None]キャプションの上に表示されます。
[Update Text]ボタンのイベント ハンドラ
フォーム デザイナで[Update Text]ボタンをダブルクリックします。コード エディタに ActnUpdateTextExecute メソッドの実装が表示されます。ActnUpdateTextExecute は、[Update Text]ボタンの OnClick イベント ハンドラです。
描画するテキストは、FNumber
整数フィールドを FNumber.ToString
で文字列表現に変換したものです。[Update Text]をクリックするたびに、Inc(FNumber)
でこのフィールドの値が増やされます。
ActnUpdateTextExecute は以下を呼び出します。
DrawTextOnLayer(8,FNumber.ToString)
この最初のパラメータ 8
は、変更対象の画像リスト ImageList1
の Destination コレクションに含まれる画像のインデックスを指定しています。
-
- メモ: フォーム デザイナで UnitDataModule ユニットを開き、
ImageList1
の画像をダブルクリックしてください。[画像のリスト]ペインでImageIndex=8
の画像をクリックします。[選択した画像]ペインを見ると、この画像に 2 つの Layers が含まれていることがわかります。0
レイヤは空で、1
レイヤにはmail
という Name で特定される TMultiResBitmap が含まれ、封筒の画像が表示されています。
- メモ: フォーム デザイナで UnitDataModule ユニットを開き、
2 番目のパラメータ FNumber.ToString
では、描画するテキストを指定しています。
DrawTextOnLayer
は DrawPicture
と似ています。DrawTextOnLayer
は、まず、Destination コレクション内の ImageIndex=8
の画像に少なくとも 1 つの Layer があるかどうかを確認します。次に、最上位の Layer に画像が含まれているかを確認し、その画像を SourceName
変数に格納します。
SourceName := Layer.Name;
そして BitmapItemByName を次のように呼び出し、
MainDataModule.ImageList1.BitmapItemByName(SourceName, Item, Size)
指定した名前 SourceName
に対応する要素を Source コレクションから取得します。
BitmapItemByName で SourceName
という名前に対応する要素を取得できなければ、新しい Source 要素を追加します。
NewSource := MainDataModule.ImageList1.Source.Add;
そこに SourceName
という名前を割り当てます。
NewSource.Name := SourceName;
Item
を取得します。
Item := NewSource.MultiResBitmap.ItemByScale(1, False, True);
Item
が存在しなければ、新しい項目を MultiResBitmap コレクションに追加し、
Item := NewSource.MultiResBitmap.Add;
サイズを設定します。
Item.Bitmap.SetSize(Size.cx, Size.cy);
これは、以下で設定されたものです。
Size.cx := Round(Layer.SourceRect.Rect.Width);
Size.cy := Round(Layer.SourceRect.Rect.Height);
これが終わった後、または BitmapItemByName が成功した場合にはその後で、DrawTextOnLayer
は、取得した画像の上に Text
(FNumber.ToString
と同じ)を描画します。
Item.Bitmap.Canvas.FillText(
TRectF.Create(1,0,Size.cx - 1,Size.cy div 2),
Text, False, 1, [], TTextAlign.Center, TTextAlign.Center);
つまり、現在の実装では、[Update Text]をクリックすると、ImageList1
画像リストの ImageIndex=8
の画像の上に数値が描画されます。新しくクリックするたびに、描画される番号が 1
だけ増えます。
[Update Text]ボタンの機能を確認する手順:
- [Demo of images]アプリケーションで、[Bitmap and Image]タブを選択します。
- 右矢印ボタンを何度かクリックします。
ImageList1
画像リストの画像が、[None]タブの[None]キャプションの上とGlyph1
コントロールに、順に描画されます。 - 右矢印ボタンを 8 度目にクリックすると、
ImageIndex=8
の封筒の画像が描画されます。 - 次の図では、[None]キャプションと
Glyph1
コントロールの画像に、4
という文字列が描画されています。
コントロール内での画像リストの使用
ここでは、画像リスト内の画像を独自のコントロールの表面に描画する方法を説明します。同様の方法で、画像リスト内の画像を使用する独自のコントロールを作成することもできます。
このサンプルでは、ImageList1
画像リスト内の画像を、4 番目のタブ コントロールの表面の[None]キャプションの上に描画します。その様子を確認するには、右矢印または左矢印のボタンを何度かクリックします。ImageList1
画像リストの画像が、[None]キャプションの上に順に描画されます。
画像描画を行う方法
フォームのコンストラクタ TMainForm.Create
で、TImageLink 型の FImageLink
というフィールドを作成します。その後、次のように
FImageLink.Images := MainDataModule.ImageList1;
ImageList1
画像リストをこのフィールドに代入します。それから、OnChange
イベントに OnImagesChange
イベント ハンドラを代入します。ImageList1
画像リストを使用しているコントロールに画像を再描画する必要が生じると、OnImagesChange
が実行されます。OnImagesChange
は次のようになっています。
... TabItem4.Repaint; ...
画像の描画は、TabItem4
タブ コントロールの OnPaint イベントの TabItem4Paint
イベント ハンドラで行われます。
procedure TMainForm.TabItem4Paint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
R: TRectF;
begin
R := TRectF.Create(ARect.Right - 22, 4, ARect.Right - 6, 20);
MainDataModule.ImageList1.Draw(TabItem4.Canvas, R,
Glyph1.ImageIndex);
end;
この R
は、TabItem4
タブ コントロール内の、画像が描画される長方形です。Draw は、Glyph1.ImageIndex
の画像を、指定された TabItem4.Canvas
キャンバス上の R
に描画します。
OnChange
イベント ハンドラを作成する代わりに、TImageLink
の下位クラスを作成し、その中で Change メソッドをオーバーライドすることもできます。
-
- メモ: 作成した
FImageLink
フィールドをデストラクタで解放することを忘れないでください。そのときには DisposeOf メソッドを使用してください。
- メモ: 作成した
コントロールがアクションの使用をサポートしている場合には、おそらくそのコントロールでは IGlyph インターフェイスをサポートしています。その場合には、TGlyphImageLink オブジェクトを作成する方がよいでしょう。このオブジェクトは ImagesChanged メソッドを呼び出すため、OnChange
のイベント ハンドラを割り当てる必要がありません。
コントロールの ImageIndex および Images のプロパティは正しい値でなければなりません。通常、プロパティは次のような形で使われます。
property ImageIndex: TImageIndex read GetImageIndex write SetImageIndex
stored ImageIndexStored;
…
function TMyControl.GetImageIndex: TImageIndex;
begin
Result := FImageLink.ImageIndex;
end;
procedure TGlyph.SetImageIndex(const Value: TImageIndex);
begin
FImageLink.ImageIndex := Value;
end;
関連項目
- 画像リスト エディタ(FireMonkey)
- 画像リスト エディタを使用して画像リストを作成し、編集する方法を解説したビデオ(画像リスト エディタの一部の高度な機能(些細なものではない)が解説されています)