The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | page.title=Hello, MapView |
| 2 | parent.title=Hello, Views |
| 3 | parent.link=index.html |
| 4 | @jd:body |
| 5 | |
Dirk Dougherty | e1d9b55 | 2009-04-21 20:39:18 -0700 | [diff] [blame] | 6 | <div class="special"> |
| 7 | <p>This tutorial requires that you have the Google Maps external library |
Dirk Dougherty | f11d7d5 | 2009-08-05 19:04:18 -0700 | [diff] [blame^] | 8 | installed in your SDK environment. By default the Android SDK includes the |
Dirk Dougherty | e1d9b55 | 2009-04-21 20:39:18 -0700 | [diff] [blame] | 9 | Google APIs add-on, which in turn includes the Maps external library. If you |
| 10 | don't have the Google APIs SDK add-on, you can download it from this |
| 11 | location:</p> |
| 12 | |
| 13 | <p style="margin-left:2em;"><a |
| 14 | href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p> |
| 15 | |
| 16 | <p>The Google APIs add-on requires Android 1.5 SDK or later release. After |
| 17 | installing the add-on in your SDK, set your project properties to use the build |
| 18 | target called "Google APIs Add-on". See the instructions for setting a build |
| 19 | target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in |
| 20 | Eclipse with ADT</a> or <a |
| 21 | href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>, |
| 22 | as appropriate for your environment. </p> |
| 23 | |
| 24 | <p>You will also need to use the android tool to set up an AVD that uses the |
| 25 | Google APIs deployment target. See <a |
| 26 | href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> for |
| 27 | more information. Once you have set up your environment, you will be able to |
| 28 | build and run the project described in this tutorial</a></p> |
| 29 | |
| 30 | </div> |
| 31 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 32 | <p>A MapView allows you to create your own map-viewing Activity. |
| 33 | First, we'll create a simple Activity that can view and navigate a map. Then we will add some overlay items.</p> |
| 34 | |
| 35 | <ol> |
| 36 | <li>Start a new project/Activity called HelloMapView. |
| 37 | |
| 38 | <li>Because we're using the Google Maps library, |
| 39 | which is not a part of the standard Android library, we need to |
| 40 | declare it in the Android Manifest. Open the AndroidManifest.xml |
| 41 | file and add the following as a child of the <code><application></code> element: |
| 42 | |
| 43 | <pre><uses-library android:name="com.google.android.maps" /></pre> |
| 44 | </li> |
| 45 | <li>We also need access to the internet in order to retrieve the Google Maps tiles, |
| 46 | so the application must request the {@link android.Manifest.permission#INTERNET INTERNET} permissions. |
| 47 | In the manifest file, add the following as a child of the <code><manifest></code> element: |
| 48 | <pre><uses-permission android:name="android.permission.INTERNET" /></pre> |
| 49 | </li> |
| 50 | <li>Now open the main layout file for your project. Define a layout with a com.google.android.maps.MapView |
| 51 | inside a android.widget.RelativeLayout: |
| 52 | |
| 53 | <pre> |
| 54 | <?xml version="1.0" encoding="utf-8"?> |
| 55 | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 56 | android:id="@+id/mainlayout" |
| 57 | android:orientation="vertical" |
| 58 | android:layout_width="fill_parent" |
| 59 | android:layout_height="fill_parent" > |
| 60 | |
| 61 | <com.google.android.maps.MapView |
| 62 | android:id="@+id/mapview" |
| 63 | android:layout_width="fill_parent" |
| 64 | android:layout_height="fill_parent" |
| 65 | android:clickable="true" |
| 66 | android:apiKey="<em>Your Maps API Key</em>" |
| 67 | /> |
| 68 | |
| 69 | </RelativeLayout> |
| 70 | </pre> |
| 71 | <p>The <code>clickable</code> attribute defines whether you want to allow user-interaction with the map. |
| 72 | In this case, we set it "true" so that the user can navigate.</p> |
| 73 | |
| 74 | <p>The <code>apiKey</code> attribute holds the Google Maps API Key that proves your application and signer |
| 75 | certificate has been registered with the Google Maps service. Because MapView uses Google Maps data, this key is required |
| 76 | in order to receive the map data, even while you are developing. Registration is free and it only takes a couple |
| 77 | minutes to register your certificate and receive a Maps API Key. For instructions on getting a key, read |
Dirk Dougherty | 392d29a | 2009-05-01 13:01:24 -0700 | [diff] [blame] | 78 | <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API Key</a>. |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 79 | (For the purpose of this tutorial, you should register with the fingerprint of the SDK debug certificate.) |
| 80 | Once you've acquired the Maps API Key, insert it for the <code>apiKey</code> value.</p></li> |
| 81 | |
| 82 | <li>Now open the HelloMapView.java file. For this Activity, we're going to extend the special sub-class of |
| 83 | Activity called MapActivity, so change the class declaration to extend |
| 84 | MapActicity, instead of Activity:</p> |
| 85 | |
| 86 | <pre>public class HelloMapView extends MapActivity {</pre> |
| 87 | |
| 88 | <li>The <code>isRouteDisplayed()</code> method is required, so add it inside the class: |
| 89 | <pre> |
| 90 | @Override |
| 91 | protected boolean isRouteDisplayed() { |
| 92 | return false; |
| 93 | } |
| 94 | </pre> |
| 95 | <p>You can actually run this now, but all it does is allow you to pan around the map.</p> |
| 96 | <p>Android provides a handy {@link android.widget.ZoomControls} widget for zooming in and out of a View. |
| 97 | MapView can automatically hook one for us by requesting it with the <code>getZoomControls()</code> |
| 98 | method. Let's do this.</p> |
| 99 | |
| 100 | <li>Go back to the layout file. We need a new ViewGroup element, in which we'll |
| 101 | place the ZoomControls. Just below the MapView element (but inside the RelativeLayout), add this element: |
| 102 | <pre> |
| 103 | <LinearLayout |
| 104 | android:id="@+id/zoomview" |
| 105 | android:layout_width="wrap_content" |
| 106 | android:layout_height="wrap_content" |
| 107 | android:layout_alignBottom="@id/mapview" |
| 108 | android:layout_centerHorizontal="true" |
| 109 | /></pre> |
| 110 | |
| 111 | <p>It doesn't really matter what kind of ViewGroup we use, because we just want a |
| 112 | container that we can position within our root RelativeLayout.</p> |
| 113 | |
| 114 | <p>The last two attributes are available only to an element that's a child of a |
| 115 | RelativeLayout. <code>layout_alignBottom</code> aligns the bottom of this element to the bottom of |
| 116 | the element identified with a resource tag (which must be a sibling to this element). |
| 117 | <code>layout_centerHorizontal</code> centers this on the horizontal plane.</p></li> |
| 118 | |
| 119 | <li>Now go back to the HelloMapView class. We'll now retrieve the ZoomControls object from |
| 120 | the MapView and add it to our new layout element. First, at the top of the HelloMapView, |
| 121 | instantiate handles for the MapView and LinearLayout, plus a ZoomControl object: |
| 122 | <pre> |
| 123 | LinearLayout linearLayout; |
| 124 | MapView mapView; |
| 125 | ZoomControls mZoom;</pre></li> |
| 126 | |
| 127 | <li>Then initialize each of these in <code>onCreate()</code>. We'll capture the LinearLayout and |
| 128 | MapView through their layout resources. Then get the ZoomControls from the MapView:: |
| 129 | <pre> |
| 130 | linearLayout = (LinearLayout) findViewById(R.id.zoomview); |
| 131 | mapView = (MapView) findViewById(R.id.mapview); |
| 132 | mZoom = (ZoomControls) mapView.getZoomControls();</pre> |
| 133 | |
| 134 | <p>By using the ZoomControls object provided by MapView, we don't have to do any of the work |
| 135 | required to actually perform the zoom operations. The ZoomControls widget that MapView |
| 136 | returns for us is already hooked into the MapView and works as soon as we add it to the |
| 137 | layout. The controls will appear whenever the user touches the map, then dissapear after |
| 138 | a few moments of inactivity.</p></li> |
| 139 | |
| 140 | <li>Now just plug our ZoomControls into the LinearLayout we added: |
| 141 | |
| 142 | <pre>linearLayout.addView(mZoom);</pre></li> |
| 143 | |
| 144 | <li>Run it.</li> |
| 145 | </ol> |
| 146 | |
| 147 | <hr/> |
| 148 | |
| 149 | <p>So, we now have full interaction controls. All well and good, but what we really want our map |
| 150 | for is custom markers and layovers. Let's add some Overlay |
| 151 | objects to our map. To do this, we're going to |
| 152 | implement the ItemizedOverlay |
| 153 | class, which can manage a whole set of Overlay items for us.</p> |
| 154 | |
| 155 | <ol> |
| 156 | <li>Create a new Java class named HelloItemizedOverlay that implements ItemizedOverlay. |
| 157 | |
| 158 | <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and select New > Class. Fill-in |
| 159 | the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter |
| 160 | <em>com.google.android.maps.ItemizedOverlay</em>. Click the checkbox for <em>Constructors from |
| 161 | superclass</em>. Click Finish.</p></li> |
| 162 | |
| 163 | <li> First thing, we need an OverlayItem ArrayList, in which we'll put each of the OverlayItem |
| 164 | objects we want on our map. Add this at the top of the HelloItemizedOverlay class: |
| 165 | |
| 166 | <pre>private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();</pre></li> |
| 167 | |
| 168 | <li>All the constructor does is define the default marker to be used on each of the OverlayItems. |
| 169 | In order for the Drawable to actually get drawn, it must have its bounds defined. And we want the |
| 170 | center-point at the bottom of the image to be the point at which it's attached to the map |
| 171 | coordinates. We handle all this with the boundCenterBottom() method. Wrap this around our |
| 172 | defaultMarker, so the super constructor call looks like this: |
| 173 | |
| 174 | <pre>super(boundCenterBottom(defaultMarker));</pre></li> |
| 175 | |
| 176 | <li>In order to add new OverlayItems to our ArrayList, we need a new public method. We'll handle |
| 177 | this with the following method: |
| 178 | |
| 179 | <pre> |
| 180 | public void addOverlay(OverlayItem overlay) { |
| 181 | mOverlays.add(overlay); |
| 182 | populate(); |
| 183 | }</pre> |
| 184 | |
| 185 | <p>Each time we add a new OverlayItem, we must call <code>populate()</code>, which will read each of out |
| 186 | OverlayItems and prepare them to be drawn.</p></li> |
| 187 | |
| 188 | <li>In order for the <code>populate()</code> method to read each OverlayItem, it will make a request to |
| 189 | <code>createItem(int)</code>. We must define this method to properly read from our ArrayList. Replace the |
| 190 | existing contents of the createItem method with a <code>get()</code> call to our ArrayList: |
| 191 | |
| 192 | <pre> |
| 193 | @Override |
| 194 | protected OverlayItem createItem(int i) { |
| 195 | return mOverlays.get(i); |
| 196 | } |
| 197 | </pre></li> |
| 198 | |
| 199 | <li>We're also required to override the <code>size()</code> method. Replace the existing contents of the |
| 200 | method with a size request to our ArrayList: |
| 201 | |
| 202 | <pre>return mOverlays.size();</pre></li> |
| 203 | </ol> |
| 204 | |
| 205 | |
| 206 | <p>That's it for the HelloItemizedOverlay class. We're now ready to use it.</p> |
| 207 | |
| 208 | <hr/> |
| 209 | <p>Go back to the HelloMapView |
| 210 | class. We'll start by creating one OverlayItem, adding to an instance of our HelloItemizedOverlay, |
| 211 | and then adding this to the MapView.</p> |
| 212 | |
| 213 | <img src="images/androidmarker.png" align="right" /> |
| 214 | <p>First, we need the image that we'll use for our map overlay. Here, we'll use the Android on the |
| 215 | right as our marker. Drag this image (or your own) to the res/drawable/ directory of your project workspace.</p> |
| 216 | |
| 217 | <p>Now we're ready to work in the HelloMapView:</p> |
| 218 | |
| 219 | <ol> |
| 220 | <li>First we need some more types. Add the following at the top of the HelloMapView class: |
| 221 | |
| 222 | <pre> |
| 223 | List<Overlay> mapOverlays; |
| 224 | Drawable drawable; |
| 225 | HelloItemizedOverlay itemizedOverlay;</pre></li> |
| 226 | |
| 227 | <li>Now pick up where we left off in the <code>onCreate()</code> method. Instantiate the |
| 228 | new fields: |
| 229 | |
| 230 | <pre> |
| 231 | mapOverlays = mapView.getOverlays(); |
| 232 | drawable = this.getResources().getDrawable(R.drawable.androidmarker); |
| 233 | itemizedoverlay = new HelloItemizedOverlay(drawable);</pre> |
| 234 | |
| 235 | <p>All overlay elements on a map are held by the MapView, so when we want to add some, we must |
| 236 | first retrieve the List with <code>getOverlays()</code> methods. We instantiate the Drawable, which will |
| 237 | be used as our map marker, by using our Context resources to get the Drawable we placed in |
| 238 | the res/drawable/ directory (androidmarker.png). Our HelloItemizedOverlay takes the Drawable in order to set the |
| 239 | default marker.</p></li> |
| 240 | |
| 241 | <li>Now let's make our first OverlayItem by creating a GeoPoint |
| 242 | that defines our map coordinates, then pass it to a new OverlayItem: |
| 243 | |
| 244 | <pre> |
| 245 | GeoPoint point = new GeoPoint(19240000,-99120000); |
| 246 | OverlayItem overlayitem = new OverlayItem(point, "", "");</pre> |
| 247 | |
| 248 | <p>GeoPoint coordinates are based in microdegrees (degrees * 1e6). The OverlayItem takes this |
| 249 | GeoPoint and two strings. Here, we won't concern ourselves with the strings, which can display |
| 250 | text when we click our marker, because we haven't yet written the click handler for the OverlayItem.</p></li> |
| 251 | |
| 252 | <li>All that's left is for us to add this OverlayItem to our collection in the HelloItemizedOverlay, |
| 253 | and add this to the List of Overlay objects retrieved from the MapView: |
| 254 | |
| 255 | <pre> |
| 256 | itemizedoverlay.addOverlay(overlayitem); |
| 257 | mapOverlays.add(itemizedoverlay);</pre></li> |
| 258 | |
| 259 | <li>Run it!</li> |
| 260 | </ol> |
| 261 | |
| 262 | <p>We've sent our droid to Mexico City. Hola, Mundo!</p> |
| 263 | <p>You should see the following:</p> |
| 264 | <img src="images/hello-mapview.png" width="150px" /> |
| 265 | |
| 266 | <p>Because we created our ItemizedOverlay class with an ArrayList, we can continue adding new |
| 267 | OverlayItems. Try adding another one. Before the <code>addOverlay()</code> method is called, add these lines:</p> |
| 268 | <pre> |
| 269 | GeoPoint point2 = new GeoPoint(35410000, 139460000); |
| 270 | OverlayItem overlayitem2 = new OverlayItem(point2, "", ""); |
| 271 | </pre> |
| 272 | <p>Run it again... We've sent a new droid to Tokyo. Sekai, konichiwa!</p> |
| 273 | |