[Android] Using layout as custom marker on Google Map API

Here is my first tutorial on Android development.
The purpose of this tutorial is to create custom marker for Google Maps using a layout containing a customizable TextView.

First thing to do, install and configure Google Maps API v2 to obtain an API key (to put in the file manifest.xml).
All the information is here:
http://developer.android.com/google/play-services/maps.html

And to get the api key is here:
https://code.google.com/apis/console/


We create the layout of the application by specifying a fragment referring to the Google Map API (com.google.android.gms.maps.SupportMapFragment).

layout/map_layout.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF" >
 
    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
 
</RelativeLayout>

We create a layout with a TextView (dynamic) and ImageView. It will be converted into bitmap to display in the map.

layout/custom_marker_layout.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
 
    <ImageView
        android:layout_width="55dp"
        android:layout_height="65dp"
        android:src="@drawable/custom_marker" />
 
    <TextView
        android:id="@+id/num_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="5dp"
        android:gravity="center"
        android:text="20"
        android:textColor="#ce8223"
        android:textSize="25dp"
        android:textStyle="bold" />
 
</RelativeLayout>

drawable/custom_marker.png


In CustomMapMarker class, we setup the map (tutorial here).
We get the layout custom_marker_layout with layout inflater service, we change our TextView and convert the view result into a bitmap (with createDrawableFromView method).

CustomMapMarker.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class CustomMapMarker extends FragmentActivity {
	private GoogleMap mMap;
	private Marker customMarker;
	private LatLng markerLatLng;
 
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_layout);
 
		markerLatLng = new LatLng(48.8567,2.3508);
 
		setUpMapIfNeeded();
	}
 
	private void setUpMapIfNeeded() {
		// Do a null check to confirm that we have not already instantiated the
		// map.
		if (mMap == null) {
			// Try to obtain the map from the SupportMapFragment.
			mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
			// Check if we were successful in obtaining the map.
			if (mMap != null) {
				setUpMap();
			}
		}
	}
 
	private void setUpMap() {
 
		View marker = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.custom_marker_layout, null);
		TextView numTxt = (TextView) marker.findViewById(R.id.num_txt);
		numTxt.setText("27");
 
		customMarker = mMap.addMarker(new MarkerOptions()
		.position(markerLatLng)
		.title("Title")
		.snippet("Description")
		.icon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(this, marker))));
 
		final View mapView = getSupportFragmentManager().findFragmentById(R.id.map).getView();
		if (mapView.getViewTreeObserver().isAlive()) {
			mapView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
				@SuppressLint("NewApi")
				// We check which build version we are using.
				@Override
				public void onGlobalLayout() {
					LatLngBounds bounds = new LatLngBounds.Builder().include(markerLatLng).build();
					if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
						mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
					} else {
						mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
					}
					mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
				}
			});
		}
	}
 
	// Convert a view to bitmap
	public static Bitmap createDrawableFromView(Context context, View view) {
		DisplayMetrics displayMetrics = new DisplayMetrics();
		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
		view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
		view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
		view.buildDrawingCache();
		Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
 
		Canvas canvas = new Canvas(bitmap);
		view.draw(canvas);
 
		return bitmap;
	}
}

Preview:


Google Maps Android API v2 useful links:
https://developers.google.com/maps/documentation/android/start
https://developers.google.com/maps/documentation/android/map
https://code.google.com/apis/console/
http://developer.android.com/google/play-services/index.html


Project sources on GitHub:
https://github.com/Nasc/CustomMapMarker