日程Fit|「いつ空いてますか?」の往復はもう不要。候補日を選んでURLを送るだけ|登録不要|今すぐ無料で使う →

.NET MAUI(Android)Google MapsクラスタリングでカスタムInfoWindowを表示する方法|ClusterManagerにMarkerCollectionが無い問題と回避策

.NET MAUI(Android)で Google Maps のクラスタリングを導入すると、次にやりたくなるのが「ピンをタップしたときのカスタム InfoWindow 表示」です。ところが、Xamarin では当たり前に触れた ClusterManager の MarkerCollection が、MAUI で使っているパッケージでは見当たらず、同じ書き方ができないケースがあります。この記事では原因を整理し、現場で取り得る回避策を具体的にまとめます。

日程Fit。無料・登録不要。「いつ空いてる?」を、ひとつのリンクで。リンクを送って、○△×でかんたん日程調整。無料で日程を作る。
目次

.NET MAUI(Android)のクラスタリングで「カスタム InfoWindow」にハマる理由

クラスタリングは、近い位置にある多数のピン(マーカー)をまとめて表示し、ズーム操作に応じて「まとまり」を分解・統合する仕組みです。Google Maps のクラスタリングを扱う場合、Android では Google Maps Android SDK と、その上に乗る Maps Utils(クラスタリング支援) の概念が関わってきます。

ここで問題になるのが「InfoWindow(吹き出し)」です。InfoWindow はマーカーをタップしたときに出るポップアップですが、実務では次のような要件がよく出ます。

  • 店舗アイコンをタップしたら、店舗名・営業時間・ステータス・写真などを表示したい
  • クラスタ(まとまり)をタップしたら、「◯件あります」など別の表示にしたい
  • 表示内容をデータモデルに合わせて柔軟に変えたい

一方で、InfoWindow は Android 側の仕様として「自由度が高そうで、実は制約が強い」UI です。さらに .NET MAUI では、ネイティブ API を どのパッケージ(ラッパー)経由で触っているか によって、書けるコードが大きく変わります。

前提整理:InfoWindow は「実ビュー」ではない

Android の Google Maps における InfoWindow は、画面上に普通の View が乗るのではなく、一度描画(スナップショット)された見た目として表示されます。これにより、次のような特性が出ます。

  • InfoWindow 内のボタンやリンクを直接タップしても、基本的に個別クリックは取れない(クリックは「InfoWindow 全体」扱い)
  • 画像を非同期ロードしても、そのままでは表示が更新されない(再表示や invalidate 的な工夫が必要)
  • カスタム表示は InfoWindowAdapter(IInfoWindowAdapter)で行う

つまり「レイアウトを自由に組めば完璧」というより、仕様を理解した上で、最適な UI(InfoWindow / BottomSheet / 画面遷移)を選ぶのが重要です。

症状:Xamarin の書き方が MAUI で通用しない

質問の状況を噛み砕くと、困っているのは次の一点です。

  • Xamarin では clustermanager.MarkerCollection.SetOnInfoWindowAdapter(…) のように、クラスタ配下のマーカー群に対して InfoWindowAdapter を設定できた
  • .NET MAUI(Android)で利用している GoogleMapsUtils.Android.Maui (1.0.2) の ClusterManager には MarkerCollection が公開されていない
  • そのため、Xamarin と同じ実装方針が取れず、カスタム InfoWindow 表示の入り口が塞がっている

原因:MAUI そのものではなく「利用しているラッパーの API 不足」

ここを誤解しやすいのですが、今回の論点は「MAUI だからできない」ではありません。多くのケースで本質は次のどちらかです。

  • そもそも使っているパッケージが、ネイティブ側の API を 十分に公開していない
  • 公開はしているが、ドキュメントやサンプルが少なく、到達経路が見えない

今回のように ClusterManager に MarkerCollection が無い場合、Xamarin 時代の手順(MarkerCollection に対して adapter をセット)が再現できません。これはアプリ側で頑張っても「プロパティが存在しない」という壁なので、基本的には ライブラリ側の対応範囲の問題です。

まず確認したいこと:自分が触っているのは「どの ClusterManager」か

同じ “ClusterManager” という名前でも、以下のような事情で別物になりえます。

  • 公式の Maps Utils をそのままバインドしているのか
  • MAUI 向けに独自ラップされ、API が省略されているのか
  • Generics の扱い、イベントの露出、Renderer の差し替え口があるか
観点Xamarin 時代に多い構成.NET MAUI + サードパーティ構成で起きがち影響
クラスタリングの APIMaps Utils の API を比較的そのまま利用必要最低限だけ公開(省略あり)MarkerCollection / Renderer などが触れない
InfoWindow 設定箇所MarkerCollection に adapter を設定できる設定口が無い/Map 側しか触れないマーカー種別ごとの出し分けが難しい
拡張ポイントDefaultClusterRenderer の差し替えがしやすいRenderer が internal 扱いなどで差し替え不能marker.Tag 付与などの定石が使えない

この表の通り、どこまでラップされているかで取れる戦略が変わります。今回のケースは「MarkerCollection が見えない」ため、Xamarin の定石が使えない側です。

解決策の方向性は大きく 2 つ

今回の状況に対して、現実的な打ち手は次の 2 つに集約されます。

  • 解決策1:利用しているパッケージ(GoogleMapsUtils.Android.Maui)に機能追加・不具合として依頼する
  • 解決策2:より API が揃っている別パッケージ(例:Xamarin.Google.Maps.Utils など)へ切り替える

以降では、それぞれを「実務で動かす」前提で、手順と注意点を掘り下げます。

解決策1:プラグイン側に Issue を起票して対応を依頼する

MarkerCollection が存在しない問題は、アプリコードの工夫でどうこうできる範囲を超えています。したがって、正攻法は 当該プラグインの公式リポジトリに Issue を起票し、API を公開してもらうことです。

Issue 起票時に入れると解決が早い情報

  • 利用バージョン(例:GoogleMapsUtils.Android.Maui 1.0.2)
  • .NET / MAUI / Android のバージョン、ターゲット API レベル
  • 期待する API(MarkerCollection / ClusterMarkerCollection を公開してほしい、あるいは SetOnInfoWindowAdapter 相当のメソッドが欲しい)
  • 再現手順(最小コード、最小プロジェクト)
  • Xamarin で動いていたコード例(比較として)

要求仕様を「API の形」で具体化する

Issue を出す際に有効なのは、「やりたいこと」だけでなく「どういう API があれば実装できるか」を提示することです。例えば次のような形です。

  • ClusterManager に MarkerCollection を公開する
  • ClusterManager に ClusterMarkerCollection(クラスタ側)も公開する
  • あるいは、MarkerCollection を直接触らずに済む SetInfoWindowAdapter のヘルパーを提供する

プラグイン作者側が「どこを公開すべきか」を判断しやすくなり、修正も早くなりやすいです。

社内・商用で急ぐなら「フォークして自前で追加」も選択肢

もし待てない場合、リポジトリが公開されている前提ですが、プラグインをフォークして MarkerCollection を公開する PR を作り、社内ビルドで一時的に運用する手もあります。

ただし、これはチーム運用の負債にもなるため、次の観点で判断しましょう。

  • アプリのリリース期限が近いか
  • クラスタリング+InfoWindow が必須機能か
  • 今後も Maps 周りで改修が続くか
  • Google Play services / AndroidX の追従が必要か

解決策2:より API が揃っている別パッケージへ切り替える

スレッド内で提示されている案として、「追従が遅い/必要な API が揃っていない」場合は、別のパッケージへ切り替えるのが現実的、という判断があります。代表例として挙がっているのが Xamarin.Google.Maps.Utils 系です。

切り替えの狙い

切り替えによって得たいのは、主に次の 2 点です。

  • MarkerCollection にアクセスできる(Xamarin でやっていた adapter 設定が復活する可能性)
  • Renderer やイベントなど、Maps Utils の拡張ポイントを使える

切り替え前に知っておくべき注意点

一方で、Xamarin 系の資産を MAUI 側で使う場合、依存関係が絡みます。特に Android は次がポイントです。

  • Google Play services(Maps 関連)の依存整合
  • AndroidX 互換の状況
  • 既存の地図コントロール(Microsoft.Maui.Maps / 別プラグイン)との相性

実務では、いきなり本体に組み込むより、最小構成の検証プロジェクトを作って「地図表示→クラスタリング→タップ→InfoWindow 表示」まで通るか確認してから移植するのが安全です。

選択肢工数安定性将来性向いているケース注意点
プラグインに Issue 起票作者対応次第対応されれば高い時間に余裕がある、OSS に貢献したい修正がいつ入るか読めない
プラグインをフォークして自前修正中〜大短期は高い運用負債になりやすいリリースが迫っている、機能が必須アップデート追従が必要
別パッケージへ切り替え実績次第選定次第必要 API が揃うことが最優先依存関係の整合が課題
InfoWindow を捨てて別 UI(BottomSheet 等)高い高い情報量が多い、操作が複雑UI 設計の見直しが必要

(補助策)MarkerCollection が触れない時に検討できる設計変更

ここからは「本筋はプラグイン対応 or 切り替え」だと理解したうえで、現場でよく採用される補助策を紹介します。状況によっては、MarkerCollection に触れなくても目的を達成できることがあります。

補助策A:InfoWindow ではなく BottomSheet / ポップアップに寄せる

InfoWindow は見た目は便利ですが、制約が多く、UI がリッチになるほど苦しくなります。たとえば次の条件に当てはまるなら、InfoWindow を捨てて BottomSheet などに寄せた方が結果的に堅牢です。

  • 写真・複数行・ボタンなど、情報量が多い
  • 「お気に入り」「経路」など、InfoWindow 内で操作させたい
  • 非同期で表示内容が変わる(在庫、混雑度、到着予測など)

地図上のタップは「どのピンが選ばれたか」を決めるだけにして、詳細は別 UI に任せる設計は、MAUI との相性も良いです。

補助策B:Map 全体に InfoWindowAdapter を設定して出し分ける

理屈の上では、InfoWindowAdapter はマーカー単位ではなく Map に対して設定する手もあります。MarkerCollection に対して adapter を付けられない場合でも、Map に adapter を設定できるなら、次のような設計が可能です。

  • すべてのマーカー(クラスタ含む)に共通の InfoWindowAdapter を設定
  • marker.Title / marker.Snippet / marker.Tag などを見て内容を切り替える

ただし、クラスタリングライブラリが内部でマーカーを生成するため、marker.Tag を自由にセットできるかや、マーカー生成のタイミングを制御できるかが鍵になります。プラグインが Renderer 差し替えを提供していないと、この補助策は成立しにくいです。

参考として、Android 側で一般的な InfoWindowAdapter の形は次のようになります。

using Android.Content;
using Android.Views;
using Android.Widget;
using Android.Gms.Maps;
using Android.Gms.Maps.Model;

public class CustomInfoWindowAdapter : Java.Lang.Object, GoogleMap.IInfoWindowAdapter
{
private readonly LayoutInflater _inflater;


public CustomInfoWindowAdapter(Context context)
{
    _inflater = LayoutInflater.From(context);
}

// 枠ごと差し替える場合は GetInfoWindow を返す
public View GetInfoWindow(Marker marker) => null;

// 中身だけ差し替える場合は GetInfoContents を返す
public View GetInfoContents(Marker marker)
{
    var view = _inflater.Inflate(Resource.Layout.info_window, null);

    view.FindViewById<TextView>(Resource.Id.title)!.Text = marker.Title ?? string.Empty;
    view.FindViewById<TextView>(Resource.Id.snippet)!.Text = marker.Snippet ?? string.Empty;

    return view;
}


}

この方式の利点は、MarkerCollection を触れなくても「表示」自体は組める可能性がある点です。一方で、クラスタリング側のマーカーにタイトルやタグが入っていないと、出し分けロジックを作れません。

補助策C:Renderer を差し替えて marker.Tag を付与する

Maps Utils の定石は、Renderer を差し替えて「クラスタアイテムのマーカーが生成された直後」に情報を埋め込み、InfoWindowAdapter はそれを参照して描画する設計です。

例えば、クラスタアイテムが描画されたタイミングで marker.Tag にモデルを詰めるイメージです。

// ※実際の型名や名前空間は利用ライブラリにより異なります(概念例)
public class MyClusterRenderer : DefaultClusterRenderer<MyItem>
{
    public MyClusterRenderer(Context context, GoogleMap map, ClusterManager<MyItem> clusterManager)
        : base(context, map, clusterManager)
    {
    }


protected override void OnClusterItemRendered(MyItem item, Marker marker)
{
    base.OnClusterItemRendered(item, marker);

    // marker.Tag にモデルを保持して InfoWindowAdapter で参照する、という定石
    marker.Tag = item;
    marker.Title = item.Title;
    marker.Snippet = item.Subtitle;
}


}

ただし今回の前提では、GoogleMapsUtils.Android.Maui のラップが薄く、Renderer 差し替えや MarkerCollection が提供されていない可能性が高いです。つまり補助策B/C は「理論上は可能でも、プラグイン API が開いていないと実装できない」側になります。

実務でよくある落とし穴と対処

画像を入れたら更新されない

InfoWindow はスナップショット表示のため、画像の非同期ロードが完了しても表示が更新されないことがあります。一般的には次の対応を組み合わせます。

  • 画像ロード完了後に一度 InfoWindow を閉じて再表示する(hide → show)
  • キャッシュが効くようにし、InfoWindow 表示前に画像が揃う状態を作る

InfoWindow 内のボタンが押せない

基本的に InfoWindow 内部のビューイベントは取れません。必要なら UI を InfoWindow に詰め込まず、BottomSheet や別画面に寄せるのが安全です。どうしても必要な場合は、InfoWindow 全体クリックをトリガーにして「擬似的に操作」へ繋げます。

クラスタをタップした時の挙動が中途半端になる

クラスタは通常「タップでズームインして分解」が期待されます。ここに InfoWindow を混ぜると UX が崩れる場合があるため、次のような設計が無難です。

  • クラスタタップはズームに専念(InfoWindow を出さない)
  • アイテム(単体ピン)だけ InfoWindow や詳細 UI を出す

トラブルシューティング:最小チェックリスト

チェック項目確認ポイントよくある症状対処
利用しているクラスタリング実装パッケージ名とバージョンサンプル通りに進まないMaps Utils を直接使っているか、MAUI 用ラップかを切り分ける
MarkerCollection / Renderer の公開有無API が見えるかInfoWindow の設定口が無いIssue 起票/フォーク/別パッケージへ切り替えを検討
InfoWindow の責務表示だけか、操作も含むかクリックできない、更新されないBottomSheet など別 UI に移す
表示内容の参照元Title/Snippet/Tag を使えるか出し分けできないRenderer で Tag を埋めるか、設計を変更する
依存関係(Play services / AndroidX)ビルド・実機で動くか参照が競合する、実機で落ちる最小プロジェクトで検証し、依存整合を取ってから本体に移植

結局どれを選ぶべきか:現実的な判断基準

今回のポイントは明確で、MarkerCollection を使った Xamarin のやり方が「書けない」のは、MAUI の問題ではなく、利用中プラグインの API 不足です。したがって、短期・中期の現実解は次の通りです。

  • 短期で確実に進めたい:別パッケージへ切り替える(もしくはフォークで API を追加)
  • 中期で改善したい:プラグインに Issue を起票して公式対応を狙う(可能なら PR も出す)
  • UX を含めて最適化したい:InfoWindow 前提を見直し、BottomSheet 等へ寄せる

「クラスタ配下のマーカーにカスタム InfoWindow を出したい」場合、技術的には MarkerCollection や Renderer を触れることが重要です。そこが閉じているなら、戦い方(採用ライブラリ or UI 設計)を変えるのが最も費用対効果が高い選択になります。

まとめ:詰まりどころを“自分のコード”と“ライブラリの限界”で切り分ける

.NET MAUI(Android)で Google Maps のクラスタリングを使い、カスタム InfoWindow を出したいのに ClusterManager に MarkerCollection が無い——この状況は、アプリ側の工夫だけで解決しづらいタイプの問題です。まずは「今のプラグインが必要な拡張ポイントを公開しているか」を見極め、公開されていないなら プラグイン対応の依頼パッケージ切り替えに舵を切るのが近道です。あわせて、InfoWindow の制約を踏まえ、より堅牢な UI(BottomSheet 等)へ寄せる選択肢も持っておくと、地図機能全体の品質が安定します。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次