【Android】Google MapのinfoWindowにウェブ上の画像を表示する

# 1.プロジェクト作成
新規プロジェクトを作成します。
Google Maps Activityを選択し、プロジェクト名を入力して進みます。
自動的に生成されるres/values/google_maps_api.xmlのYOUR_KEY_HEREの部分を、取得してある自身のAPIキーで置き換えておきます。

```xml:google_maps_api.xml
<resources>
<!-- 略 -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string>
</resources>

```
この時点で一度プログラムを起動し、地図がちゃんと表示される事を確認します。

#2.レイアウトファイルを記述する
infoWindow用のレイアウトを作成します。今回はLinearLayoutにImageViewが一つあるだけのシンプルなものにします。

```xml:info_window_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
```

#3.InfoWindowAdapterの記述
MapsActivity.javaのonMapReadyメソッドにて、InfoWindowAdapterを実装します。

```java:MapsActivity.java
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
//　　　　　〜略〜
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
            Marker lastMarker;
            View infoWindow;

            @Override
            public View getInfoWindow(Marker marker) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
//              今回はこちらに処理を記述
                return null;
            }
        });
```
infoWindowAdapterはgetInfoWindow()メソッドとgetInfoContents()メソッドの両方をオーバーライドする必要があり、かつどちらかに処理を記述しもう片方はnullを返す必要があります。今回はgetInfoContents()に処理を記述します。また、処理の都合上必要な変数lastMarkerとinfoWindowをinfoWindowAdapterのクラス内で宣言しておきます。

#4.getInfoContents()メソッドの記述

```java
            @Override
            public View getInfoContents(Marker marker) {
                if(lastMarker==null || !lastMarker.equals(marker)){
                    lastMarker = marker;
                    infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null);
                    ImageView img = infoWindow.findViewById(R.id.img);
                    new DownloadImageTask(img,marker).execute(imgUrl);
//                      ↑このあと実装
                }
                return infoWindow;
            }
```
最後まで実装してから細かく処理を追えばわかることですが、マーカーをクリックしてinfoWindowを表示させようとするとgetInfoWindow()メソッドは合計2回呼ばれることになります。1回目で画像をダウンロードする処理が走り、2回目はreturn infoWindow;だけ実行され、無限ループにならないようにしています。

#5.画像のダウンロード処理のクラスを記述

```java
private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{
        ImageView img = null;
        Marker marker = null;

        DownloadImageTask(ImageView img, Marker marker){
            this.img = img;
            this.marker = marker;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bmp = null;
            try{
                String urlStr = strings[0];
                URL url = new URL(urlStr);
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setRequestMethod("GET");
                con.connect();
                int resp = con.getResponseCode();
                switch (resp){
                    case HttpURLConnection.HTTP_OK:
                        InputStream is = con.getInputStream();
                        bmp = BitmapFactory.decodeStream(is);
                        is.close();
                        break;
                    default:
                        break;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bmp){
            img.setImageBitmap(bmp);
            marker.showInfoWindow();
        }
    }
```

画像のダウンロード処理のクラスであるDownladImageTaskを記述します。画像のダウンロードは非同期で行う必要があるのでAsyncTaskを継承しています。画像のURLは、execute()で呼び出すときにStringで受け渡し、doInbackground()のstrings[0]で受け取ります。画像のダウンロードが完了したあとで呼ばれるonPostExecute()でImageViewに画像をセットしており、そのあとmarker.showInfoWindow()を呼ぶことでinfoWindowに画像が表示されます。

# 6.MapsActivity.javaの全体
マーカーはデフォルトの位置であるシドニー、画像は、ウェブ版のGoogle mapでシドニーで検索したときに出たもののurlを指定しています。

```java:MapsActivity.java
package com.example.test;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        final String imgUrl = "https://lh5.googleusercontent.com/p/AF1QipNUrZvzjGohRsYfuwIpCS2MjYdAq_3xruYM5imS=w408-h271-k-no";

        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
        mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
            Marker lastMarker;
            View infoWindow;

            @Override
            public View getInfoWindow(Marker marker) {
                return null;
            }

            @Override
            public View getInfoContents(Marker marker) {
                if(lastMarker==null || !lastMarker.equals(marker)){
                    lastMarker = marker;
                    infoWindow = getLayoutInflater().inflate(R.layout.info_window_layout,null);
                    ImageView img = infoWindow.findViewById(R.id.img);
                    new DownloadImageTask(img,marker).execute(imgUrl);
                }
                return infoWindow;
            }
        });
    }

    private class DownloadImageTask extends AsyncTask<String,Void, Bitmap>{
        ImageView img = null;
        Marker marker = null;

        DownloadImageTask(ImageView img, Marker marker){
            this.img = img;
            this.marker = marker;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            Bitmap bmp = null;
            try{
                String urlStr = strings[0];
                URL url = new URL(urlStr);
                HttpURLConnection con = (HttpURLConnection)url.openConnection();
                con.setRequestMethod("GET");
                con.connect();
                int resp = con.getResponseCode();
                switch (resp){
                    case HttpURLConnection.HTTP_OK:
                        InputStream is = con.getInputStream();
                        bmp = BitmapFactory.decodeStream(is);
                        is.close();
                        break;
                    default:
                        break;
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bmp){
            img.setImageBitmap(bmp);
            marker.showInfoWindow();
        }
    }
}

```

#参考
https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter
https://stackoverflow.com/questions/36335004/how-to-get-image-in-infowindow-on-google-maps-with-picasso-android