Every time a client wants a map in his application, he wants an infowindow for sure to show the details of map markers. And by infowindow, they assume the native iPhone like infobubble. Now, while creating a map for Sencha Touch, the default infowindow of Google doesn’t really look good in mobile device. So here is a piece of example showing how to improvise the infobubble in Sencha Touch.
Here is few simple steps to achieve this. Lets get started!
There is a InfoBubble similar to iPhone map bubble available online. You can download it from here: http://google-maps-utility-library-v3.googlecode.com/svn-history/r292/trunk/infobubble/src/infobubble.js
Now, lets create a Sencha application and structure the app like below, following the MVC structure. We have maintained a set of locations in a store just for the sake of the example.
Controller
Views
Model
Store
Style
1. style.css
view/Map.js
Ext.define("Mapinfobubble.view.Map", { extend: 'Ext.Container', xtype: 'infomap', requires: ['Ext.Map', 'Ext.TitleBar'], config: { layout: 'fit', items: [{ docked: 'top', xtype: 'titlebar', title: 'Map with InfoBubble' }, { xtype: 'map', name: 'infoMap' } ] } });
model/Place.js
Ext.define('Mapinfobubble.model.Place', { extend: 'Ext.data.Model', config: { fields: ['description', 'name', 'latitude', 'longitude'] } });
store/Places.js
Ext.define('Mapinfobubble.store.Places', { extend: 'Ext.data.Store', config: { model: 'mapinfobuble.model.Place', proxy: { type: 'ajax', url: 'app/data/MapData.json', reader: { type: 'json', rootProperty: 'places' } } } });
controller/App.js
Ext.define('Mapinfobubble.controller.App', { extend: 'Ext.app.Controller', config: { refs: { // Containers map: 'map[name="infoMap"]', infoMap: 'infomap', details: 'details', main: 'main', // Button backToMap: 'button[name="backBtnToMap"]' }, control: { map: { maprender: function (thisOb, map, eOpts) { var me = this; me.map = map; Ext.Function.defer(me.loadMarker, 2000, me); } }, backToMap: { tap: function () { this.getMain().animateActiveItem(this.getInfoMap(), { type: 'slide', direction: 'right' }); } } } }, launch: function () { Ext.getStore('Places').load(); }, /* * Function responsible for all the marker and infobubble handling **/ loadMarker: function () { var me = this, latlngbounds = new google.maps.LatLngBounds(), position, marker, map = me.map, store = Ext.getStore('Places'), ib = new InfoBubble({ hideCloseButton: true, disableAutoPan: true, maxHeight: 110 }); store.each(function (rec) { position = new google.maps.LatLng(rec.get('latitude'), rec.get('longitude')); marker = new google.maps.Marker({ position: position, map: map, data: rec }); /* * Showing InfoBubble **/ (function (data, selfMarker) { google.maps.event.addListener(selfMarker, 'mousedown', function (event) { ib.record = { places: data }; ib.setContent([ '<div class="infobox">', '<div class="content">', data.get('description'), '</div>', '<img src="resources/images/arrow.png">', '</div>' ].join('')); /* * center the map on the marker position **/ map.setCenter(selfMarker.position); ib.open(map, this); google.maps.event.addListener(map, 'mousedown', function () { ib.close(); }); /* * Tap on InfoBubble handled here **/ google.maps.event.addDomListener(ib.bubble_, 'click', function (e) { if (!me.getDetails()) { me.getMain().add({ xtype: 'details' }); } me.getMain().animateActiveItem(me.getDetails(), { type: 'slide' }); me.getDetails().setData(ib.record.places.data); }); }); }(rec, marker)); latlngbounds.extend(position); map.fitBounds(latlngbounds); }, me); } });
css/style.css
/******************************* Map ****************************************/ .infobox{ height: 50px; width: 200px; margin-left: -100px; background-image: url("../images/infobox.png"); background-size: 100% 100%; opacity: 0.8; display: -webkit-box; cursor: pointer !important; } .infobox .content{ margin: 15px 0px 0px 12px; color: white; font-size: 12px; width: 75%; } .infobox img{ margin-top:11px; } /***************************** Map ENDS ************************************/