«

»

dic 28 2011

Usando MapViews: Desde el principio

No hay duda que una de las mejores aplicaciones que podemos disfrutar en Android es Google Maps, y sigue mejorando con cada actualización. Pero los desarrolladores también podemos aprovechar un poco los mapas en nuestras aplicaciones. Como en AndroCode no descansamos ni por Navidad, hoy veremos cómo usar el MapView para mostrar mapas y personalizarlo colocando elementos a nuestro antojo.

Para saber qué es lo que pretendemos hacer, voy a intentar explicar cómo conseguir una funcionalidad parecida a la de mi aplicación SeviBus, que podéis ver abajo. De hecho sacaré los ejemplos del código de ésta.  En resumen, mostrar un mapa, colocarle elementos, e interactuar con ellos. Ante todo, tenéis la documentación oficial sobre este tema aquí.

captura

Creando el MapView

Empecemos por el principio. El MapView no es un elemento propio de Andoid, pertenece a una librería externa de Google. No vamos a tener problema para distribuir nuestras aplicaciones ya que esta librería viene incluída en la mayoría de dispositivos, pero sí vamos a necesitar usar un SDK especial para desarrollar. Así que en las propiedades de nuestro proyecto, bajo la categoría Android debemos seleccionar como “build target” uno de los llamados Google APIs correspondiente a la versión sobre la que queramos construir nuestro proyecto. En la imagen de abajo vemos cómo sería con el SDK de Android Honeycomb 3.2 (uso ésta por compatibilidad con ActionBarSherlock, pero no trataré este tema por ahora).

Si no os aparecen las Google APIs es que no las tenéis instaladas. Se pueden descargar desde el SDK Manager, hay una para cada versión del SDK.

Pero aún no estamos listos. Para usar el mapa necesitamos una clave de Google Maps, que dependerá de la firma de nuestra aplicación. Aquí nos encontramos un problema, ya que no es la misma firma la que usamos (automágicamente) cuando ejecutamos nuestra aplicación desde Eclipse en modo debug, que la que usamos cuando exportamos la aplicación final para subirla al Market o lo que hagamos con ella. Por tanto, deberemos cambiar manualemente esta clave dependiendo si compilamos la app para debug o para producción. Para ambos casos debemos obtener la huella MD5 de la firma digital, y registrarla aquí. El proceso viene explicado en esta página. (Cuidado si tienes el JDK7, mira los comentarios)

Una vez tenemos nuestra clave de Google Maps, ya podemos colocar el mapa en la aplicación. Para ello debemos colocar en el layout un objeto como éste, poniendo la clave que hemos generado en la propiedad apiKey

<com.google.android.maps.MapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:clickable="true"
        android:apiKey="API Key"
    />

Además, debemos asegurarnos de que nuestra actividad extiende a MapActivity, e implementa el método isRouteDisplayed(), que simplemente devolverá false.

@Override
protected boolean isRouteDisplayed() {
	return false;
}

No debemos olvidarnos de nuestro querido AndroidManifest.xml. Debemos decirle que queremos usar la librería de mapas, además de declarar el permiso de Internet. Opcionalmente debemos declarar permiso para obtener la localización precisa (fine) o aproximada (coarse) si queremos marcar la posición del usuario como veremos más adelante.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<application ...>
	...
	<uses-library android:name="com.google.android.maps" />
</application>

Llegados a este punto podemos abrir la aplicación y ver que el mapa aparece donde debería. Aunque de momento hace más bien poco, sigamos con lo nuestro.

Ajustando el mapa

Ya tenemos nuestro mapa funcionando, pero queremos ajustar algunos parámetros porque los que vienen por defecto no nos sirven.
Para empezar nos creamos unas variables en nuestra clase para acceder a ellas sin problema, de tipo MapView y MapController:

private MapView mapView;
private MapController myMapController;

Y las inicializamos en el método onCreate()

protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mapa);

		mapView = (MapView) findViewById(R.id.mapa_mapview);
		myMapController = mapView.getController();
}

¿Y qué hacemos con todo esto? Pues vamos por partes. El objeto MapController se encarga de controlar el zoom y la posición del mapa. El MapView se encarga de mostrar el mapa y sus elementos. Tenéis la documentación de ambos aquí y aquí respectivamente.

Por defecto el mapa se muestra en modo callejero. Si queremos mostrar la vista de satélite tenemos que llamar al método

mapView.setSatellite(true);

o pasarle false si queremos desactivarlo.

Tenemos más métodos como setTraffic para activar los datos de tráfico, y más que podéis ver en la documentación para dejar vuestro mapa como queráis, o permitir ajustarlo dinámicamente.

Pero el mapa se muestra gobalmente. Vamos a centrarlo en un punto concreto y aplicarle zoom para mostrar la zona que queremos. Esto lo hacemos usando el MapController.

GeoPoint centro = new GeoPoint((int)(37.380882 * 1E6), (int)(-5.986958 * 1E6));
myMapController.setCenter(centro);
myMapController.setZoom(14);

¿Qué hemos hecho? Primero hemos creado un objeto GeoPoint, que sirve para representar un punto geográfico mediante su latitud y longitud. Yo he usado el punto de latitud 37.380882 grados y de longitud -5.986958 grados, que corresponden a una zona más o menos centrada de Sevilla. Podéis usar herramientas como ésta para obtener las coordenadas geográficas de cualquier punto. Pero ojo, el GeoPoint usa microgrados enteros, es por eso que multiplico los valores por 10^6, de forma que el resultado es por ejemplo 37380882 microgrados para la longitud. Esto se hace así porque para el procesador es más fácil trabajar con enteros que con números decimales.
Luego establezco el centro del mapa en ese punto que he creado. Y después establezco un zoom de nivel 14. El nivel de zoom puede variar entre 1 y 21 inclusive, y tenemos varios métodos para cambiarlo, aumentarlo y disminuirlo de forma inmediata o animada. Como digo, todos esos métodos están en la documentación.

Estos valores son meros ejemplos, vosotros quizá queráis centrar el zoom en la posición del usuario, o ajustar el zoom dinámicamente estableciendo una extensión de zona. Pero eso queda fuera del propósito de este tutorial.

Marcar Posiciones

Lo siguiente es marcar posiciones en el mapa para que el usuario interactúe con ellas. En el caso de SeviBus son paradas de autobús, pero en el vuestro pueden ser cosas distintas. En general, veamos cómo colocamos objetos en el mapa.

Los objetos en el mapa se muestran mediante capas, llamadas Overlays. Para acceder a ellas usamos el método getOverlays() de MapView, que devuelve una lista de Overlay que serán pintados. Así que en nuestra clase vamos a poner otra propiedad más,

private List<Overlay> mOverlays;

y la inicializamos en el onCreate también con

mOverlays = mapView.getOverlays();

De esta forma todo objeto Overlay que añadamos a la lista se pintará en el mapa. ¿Pero qué objetos son esos? Normalmente serán subclases de Overlay. Veamos dos casos.

Aquí está usted

Algo muy típico es mostrar la posición del usuario en el mapa. En el MapView es realmente sencillo, ya que disponemos de un Overlay específico para esto que se encarga de hacer todo el trabajo sucio. Así que básicamente vamos a crear el Overlay para la localización, configurarlo, y pintarlo en el mapa.

Este Overlay del que os hablo es MyLocationOverlay, que extiende a Overlay. Para usarlo, igual que antes, lo declaramos como propiedad de nuestra clase.

private MyLocationOverlay mOverlayLocation;

Ahora en el onCreate, tras todo lo anterior, creamos el Overlay en sí. El constructor requiere un Context y un MapView. Sencillo:

mOverlayLocation = new MyLocationOverlay(this, mapView);

Pero no vale con eso. Como dije, hay que configurarlo y mostrarlo. Este Overlay nos proporciona 2 utilidades: posición y orientación. En la primera captura se muestran ambas, la posición el punto azul con su radio de exactitud, y la orientación la brújula de la esquina señalando al norte. La primera se habilita con enableMyLocation() y la segunda con enableCompass(). Vamos a activar las dos, que no nos cobran por ello

mOverlayLocation.enableMyLocation();
mOverlayLocation.enableCompass();

Important!

Ojo: como nuestro objetivo final será hacer una aplicación de calidad, estos 2 métodos deberíamos llamarlos en el onResume, y llamar a sus antagónicos en el onPause, de forma que la aplicación deje de mirar los sensores de orientación y posición mientras el usuario está con otra cosa.

¿Qué falta? Mostrarlo en pantalla. Si recordáis lo que dije antes, para mostrarlo tenemos que añadirlo a la lista de Overlays. Muy fácil:

mOverlays.add(mOverlayLocation);

Y listo. ¿Veis qué fácil? Ah, no olvidéis llamar al método postInvalidate() del MapView después de hacer cualquier cambio para que se actualice correctamente el mapa.

Con lo que llevamos de código tenemos más o menos esto:

public class MiMapActivity extends MapActivity{

	private MapView mapView;
	private MapController myMapController;
	private List<Overlay> mOverlays;
	private MyLocationOverlay mOverlayLocation;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mapa);

		mapView = (MapView) findViewById(R.id.mapa_mapview);
		myMapController = mapView.getController();
		mOverlays = mapView.getOverlays();
		mOverlayLocation = new MyLocationOverlay(this, mapView);

		GeoPoint centro = new GeoPoint((int)(37.380882 * 1E6), (int)(-5.986958 * 1E6));
		myMapController.setCenter(centro);
		myMapController.setZoom(14);

		// Sed buenos y meted esto en el onResume, y pararlos en el onPause
		mOverlayLocation.enableMyLocation();
		mOverlayLocation.enableCompass();

		mOverlays.add(mOverlayLocation);

		mapView.postInvalidate()
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
}

¿De momento bien, verdad?

Conquistando el mundo… digo, el mapa

Ahora vamos con la parte que yo creo es la que puede resultar más complicada. Mostrar nuestros propios objetos en el mapa. Como decía, en el caso de SeviBus son paradas, en el vuestro serán otras cosas. Por tanto, la forma de hacerlo dependerá, voy a intentar explicarlo de forma que se pueda aplicar a cualquier contexto, pero los ejemplos los daré conforme a mi caso.

Como siempre, un poco de teoría primero. Antes hemos añadido al mapa un Overlay especial que nos dejaba la API, pero ahora queremos añadir nuestros propios elementos. ¿Cómo lo hacemos? ¿Creamos un Overlay por cada objeto que queramos pintar? No no, tranquilidad. Aún nos queda un as bajo la manga, el ItemizedOverlay. Ésta es otra subclase de Overlay que podemos añadir al mapa, y está formada por un conjunto de OverlayItems. ¿Lioso? Para nada, veámoslo por partes.

Primero nos creamos una subclase de ItemizedOverlay compuesta de OverlayItems. El constructor recibe un parámetro (pero yo le voy a pasar 2, más adelante veréis por qué). Además debemos implementar otros 2 métodos abstractos de la clase.

public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
	private List<OverlayItem> mOverlays;
	private Context mContext;

	public MyItemizedOverlay(Drawable defaultMarker,Context context) {
		super(boundCenter(defaultMarker));
		mOverlays =new ArrayList<OverlayItem>();
		mContext = context;
	}

 @Override
	protected OverlayItem createItem(int index) {
		return mOverlays.get(index);
	}

	@Override
	public int size() {
		return mOverlays.size();
	}

}

Veamos, hemos definido una lista de OverlayItems, que será la lista de objetos que vamos a pintar en el mapa, y una referencia al contexto que vamos a necesitar más adelante.
El constructor recibe un Drawable que será la imagen usada para marcar los objetos,  además del contexto. Lo primero que hace es llamar al constructor de la superclase pasándole el Drawable y luego inicializa la List y el Context.

Como veis, el Drawable no se le pasa directamente, sino a través del método boundCenter(). Éste y el método boundCenterBottom() se usan para que la imagen aparezca centrada en el punto geográfico, o que se centre en su base.

Nos vendría muy bien también un método para añadir los Overlays.

public void addOverlay(OverlayItem overlay) {
		mOverlays.add(overlay);
		populate();
	}
}

Warning!

Cuidado con el populate(): Normalmente no debería haber problemas, pero si vamos a añadir muchos elementos del tirón deberíamos añadirlos todos primero y luego llamar a populate, en vez de usar este método.


Ahora tenemos que añadir los puntos que queremos marcar. Aquí es donde hay más diferencias según la aplicación. Mi contexto es el siguiente:
Desde la pantalla del mapa quiero poder elegir una lista de puntos (líneas de autobús) para dibujar. Esto requiere que mi ItemizedOverlay permita añadir puntos desde fuera, pero de forma abstracta. Yo le paso los objetos con la información de las paradas y la clase MyItemizedOverlay se encarga de leer los puntos geográficos donde tiene que pintar. Digo esto para que entendáis mi propósito, y adaptéis la explicación a vuestras necesidades.

Así que voy a crear un método más en MyItemizedOverlay para añadir paradas al mapa. En mi caso recibe un objeto de tipo Entity, propio de la librería DataFramework que ya explicamos en AndroCode. Vosotros le pasaréis lo que veáis conveniente.

public void addParada(Entity parada) {
// código particular
	int lat = (int)(parada.getDouble("latitud") * 1E6);
	int lon = (int)(parada.getDouble("longitud") * 1E6);
	String numero = parada.getString("numero");
	String nombre = parada.getString("nombre");
	// código general
 GeoPoint point = new GeoPoint(lat, lon);
	OverlayItem overlayitem = new OverlayItem(point, "Parada nº "+numero, nombre);
	addOverlay(overlayitem);
}

Para no perdernos, os digo lo que el método hace. Primero obtiene la latitud y longitud de la parada almacenada en la base de datos, usando DataFramework. Luego coge el nombre y número de la parada (en lo que veremos en este tutorial no será de mucha utilidad). Lo importante: crea un GeoPoint con esas coordenadas, crea un OverlayItem usando el GeoPoint y dos Strings que harán de título y subtítulo. Por último añade el nuevo OverlayItem a la lista usando el método que creamos antes.

Y ya casi estamos. Nos falta crear e incluir la capa en nuestro mapa. En mi caso lo haría de esta forma:

MyItemizedOverlay markers = new MyItemizedOverlay(getResources().getDrawable(R.drawable.marker), this);
for(Entity parada : mParadas){ // Una lista de paradas que tengo en mi Activity
    markers.addAllParadas(paradas);
}
mOverlays.add(markers);
mapView.postInvalidate();

Espero que se entienda a pesar de las peculiaridades de mi código. Si no entendéis algo u os queda alguna duda siempre podéis preguntar e intentaré resolverla.

Capturando pulsaciones

Bien, llegados a este punto tenemos nuestro mapa mostrado, ajustado, y con nuestros elementos colocados. Pero falta algo que suele ser muy útil, y es realizar una acción cuando pulsamos algún elemento del mapa. Realmente es muy sencillo, por ahora veremos la forma más simple.

Y tan simple como implementar el método onTap(int) de nuestro ItemizedOverlay, que recibe el índice correspondiente al elemento que se haya pulsado. Con él podemos, por ejemplo, acceder al objeto de nuestra propia lista, tal que así:

@Override
protected final boolean onTap(int index) {
	Toast.makeText(mContext, “Se ha pulsado el elemento “+index,Toast.LENGTH_SHORT).show();
	// Código específico mío
	mContext.startActivity(new IntentParada(mContext, mParadas.get(index).getId()));
	return true;
}

La funcionalidad que llevará este método depende de vuestra aplicación, en mi caso lanza una actividad para abrir la parada que se ha pulsado.

Y con esto tenemos el mapa interactivo listo. En una próxima entrega, cuando saque tiempo, os contaré cómo mostrar un globo como el de la aplicación de Google Maps cuando pulsamos en un elemento, tal como hace en realidad SeviBus. Y quizá más cosas, sentiros libres de pedir.

Espero que os sea útil la explicación. He intentado contárloslo de forma general para que podáis adaptar mis propuestas a vuestro proyecto. Seguro que la mía no es la única forma de hacer esto, pero ojalá os sirva. Si veis que algo no está demasiado claro, os habéis quedado con alguna duda, o simplemente pensáis que podría mejorar algo, no dudéis en dejar constancia en los comentarios y veré qué puedo hacer.

¡Hasta la próxima, androides!

Important!

Actualización: Por si alguien quiere ver más claramente todo el código de ejemplo que pongo aquí, recientemente he liberado el código de mi aplicación SeviBus. Podéis encontrarlo aquí en GitHub

Acerca del autor

Rafa Vázquez

Estudio Ingeniería Informática del Software en Sevilla. Me considero geek sin dinero, amante y desarrollador novato de Android. He creado algunas aplicaciones como SeviBus, TicTacDroide, Kill Bieber y Traductor Hoygan, si es que se puede llamar aplicaciones a estas dos últimas ;) Ganas de aprender más y más no me faltan, e intentaré compartir mis experiencias con vosotros en la medida de lo posible.

  • Alexrs95

    He seguido los pasos para conseguir la huella MD5 de varios tutoriales, tal y como indican, y no la consigo obtener, la que me da es la huella SHA1, que es muy parecida a la MD5. Tengo el jre7. ¿Alguna solución? Gracias :)

    • http://about.me/sloy Sloy | Editor

      Ups perdón, había puesto uno de los enlaces al respecto mal. Ya está corregido. El proceso está explicado aquí http://code.google.com/intl/es/android/add-ons/google-apis/mapkey.html#getfingerprint
      Tanto para la firma de debug como la final. Espero que eso te te aclare las dudas ;)

      • Alexrs95

        Pongo aquí la solución que me ha dado Kix2902, por si alguien más se encuentra con el mismo problema. http://stackoverflow.com/questions/6305938/how-can-i-get-the-md5-fingerprint-from-javas-keytool-not-only-sha-1
        Gracias :)

      • William

        Hola…Sloy | Editor,, tu articulo esta bien bueno. mira sòlo que tengo un problema, que es el siguiente: Tengo SDK Linux, atrabajo con eclipse Helios, pero cuando creo un nuevo proyecto, por defecto ya biene con una actividad programada, pero cuando yo creo una clase nueva de nombre por ejemplo: MapView extends MapActivity, se me subraya de color rojo “MapActivity” mostrandome un error.

        • http://about.me/sloy Sloy | Editor

          Si no me dices el error no sé qué pasa. Supongo que será que no has importado la clase MapActivity

        • Ali Lopez

          Viejo eso pasa porque cuando uno hace los proyectos android tu lo haces para que tarabaje o con una version de android o con el core de android. cuando trabajad con el core de android is puedes usar cosas como el mapview y eso no te sale en rojo

    • KaloSX

      Hola, no he visto el tutorial que has seguido, pero si solo te da la huella SH1 es por que te falta agregar el identificador -v, con este te da todas las huellas. seria algo asi:

      keytool -v -list -keystore c:\ruta_del_debug_keystore

      espero te funcione.

  • Maki

    Hola!! enhorabuena y muchas gracias por el articulo me ha servido de mucha ayuda para lo que estaba haciendo, tengo una cuestión relacionada con los overlays que no he encontrado una solución aún y es que la respuesta del MapView al scroll y el zoom despues de pintar el overlay es muy muy lenta y el MapView va como a trompicones, supongo que te habrá pasado ¿lo has solucionado de alguna manera?
    Muchas gracias y a seguir así, espero con impaciencia la siguiente entrega del tutorial.
    Un cordial saludo.

    • http://about.me/sloy Sloy | Editor

      Mmm pues no, no me ha pasado. Una vez cargados los overlays el mapa se mueve fluido. ¿Lo has hecho como cuento en el tutorial?

  • alberto

    Hola , tengo una pequeña duda al poner
    ” super(boundCenter(defaultMarker), mapView);” en el constructor de la clase MyItemizedOverlay , me salta un error como que no esta definido. Me lo acepta si quito la variable mapView. Debo de quitarlo o puede que haya importado mal algo.

    Gracias de antemano y excelente tutorial!!

    • http://about.me/sloy Sloy | Editor

      Tienes razón, se me ha escapado porque yo en mi proyecto uso otra clase más que sí recibe el MapView.

      El constructor de ItemizedOverlay sólo recibe un drawable, como se ve en su documentación: http://code.google.com/intl/es/android/add-ons/google-apis/reference/com/google/android/maps/ItemizedOverlay.html

      Ahora lo corrijo en el código, sobre lo otro pretendo escribir otro post como continuación de éste cuando tenga tiempo.

      Gracias por avisar ;)

      • alberto

        Para eso estamos!! :D

        Tengo otra duda que es mas porque llevo un tiempo sin tocar java creo yo.

        Casi al final del tutorial pone que incluyamos el siguiente codigo :

        MyItemizedOverlay markers = new MyItemizedOverlay(getResources().getDrawable(R.drawable.marker), this);
        for(Entity parada : mParadas){ // Una lista de paradas que tengo en mi Activity
        markers.addAllParadas(paradas);
        }
        mOverlays.add(markers);
        mapView.postInvalidate();

        que supongo que tengo que incluir dentro del constructor de la clase principal. Y va añadiendo las distintas paradas. Pero claro en esa clase no tengo definido mParadas para que las vaya recorriendo y añadiendo las paradas.

        Mi duda (y creo que es bastante tonta) , es si debo crearme otra lista llamada mParadas, y cual seria la diferencia entre esta lista y la lista que ya tenemos que es mOverlays.

        Gracias!!

        • http://about.me/sloy Sloy | Editor

          Esa es la parte que digo que dependerá más de la aplicación de cada uno. En mi caso tengo una lista de paradas, mParadas, porque según la línea que el usuario escoja se cargan ahí unas paradas u otras (y luego van al mapa).

          Tú debes recorrer lo que sea que quieras poner en el mapa, y procesarlo según sea conveniente en tu ItemizedOverlay para colocar los marcadores.

          Si quieres que te cuente más específicamente lo que deberías hacer tendría que saber tu caso, qué datos tienes, qué quieres colocar y tal.

          • alberto

            Gracias!! , ya me salio(funciona y se muetra al menos una parada / objeto) , tarde en contestar porque en Sevilla en la etsii estábamos de exámenes :D

            Me falta conseguir mostrar varias capas a la vez.

            Si tengo alguna duda mas lo pondré por aquí.

          • http://about.me/sloy Sloy | Editor

            Me alegro ;)
            Yo también estudio en la ETSII, sé lo que es eso jeje

          • alberto

            Ya había leído por ahí algo. =)

            Supongo que también conocerás imaginática (la asociación), allí estoy siempre organizando las charlas y demás espero que el año que viene os podamos invitar :D

  • Pepe

    Hola,

    No se dibuja el bitmap para el marcador. ¿Qué puede ser?

    Saludos.

    • http://about.me/sloy Sloy | Editor

      Te has asegurado de pasar al constructor de ItemizedOverlay el drawable con el método boundCenter() o boundCenterButton()? Si le pasas el drawable directamente no se muestra la imagen

  • Pepe

    Hola,

    Sí, mira:

    public MyItemizedOverlay(Drawable defaultMarker,Context context) {
    super(boundCenter(defaultMarker));
    mOverlays =new ArrayList();
    mContext = context;
    }

    No se qué podrá ser…

    • http://about.me/sloy Sloy | Editor

      Hmm es extraño, asegúrate de que el resto de pasos los haces bien no sea que no pintes el ItemizedOverlay en el mapa o algo por el estilo.
      Y la imagen que uses de marcador, no sé si la librería da problemas con algo. Asegúrate que no usas imágenes grandes o que tengan algo raro.

      Pero sobre todo revisa los otros pasos, huele a que te está faltando algo.

  • Pepe

    Hola,

    Al final sí que se veía, lo que pasaba es que aparecía en un lugar que no esperaba, había puesto mal la latitud y la longitud.

    De todos modos, ¿no debería centrarse el mapa en el punto marcado? Contaba con ello y por eso pensé que no se estaba dibujando, pero lo que pasaba es que no se estaba centrando el mapa en el punto geográfico.

    Otra cosa, por si a alguien le ha pasado, tengo el eclipse guardado en el Dropbox para poder trabajar desde varios PCs con la misma información y me ha dado problemas con los mapas, en concreto, se dejaron de ver, mostrándose una rejilla en vez del mapa.

    La solución ha sido volver a generar la huella digital y pedir una nueva clave para los mapas.

    Enhorabuena por tu artículo, me sirvió de mucha ayuda.

    Saludos.

    • http://about.me/sloy Sloy | Editor

      Jajaja también puede pasar. Lo mejor para eso es alejar el mapa al máximo y te aseguras.

      El mapa no se centra automáticamente por defecto, podría no ser el comportamiento deseado ya que los marcadores podrían estar cambiando constantemente (por ejemplo el de localización). Queda a manos del programador. Es una de las cosas que quería explicar en otro tutorial, cómo centrar el mapa en una nube de puntos.

      Lo segundo que comentas, es debido a las firmas. Aunque se corra desde eclipse, en Android TODAS las aplicaciones deben ir firmadas. Así que el sdk cuando compila usa una firma de debug, que se genera automáticamente y como es lógico es diferente en ordenadores diferentes. Una solución (que no he probado, se me ocurre sobre la marcha) podría ser copiar la firma de un PC a otro para que tengan la misma. Si no tendrás que estar cambiando la clave del mapa cada vez que cambies de PC.

  • Fustigador

    Hola Sloy.

    Primero de todo, muchas gracias por tu artículo, me ha sido de mucha ayuda para una aplicación que estoy desarrollando. Aún así, no soy capaz de dibujar varios puntos sobre el mapa. Creo que en el fondo el problema es que no comprendo muy bien el funcionamiento de los Overlays.

    Quiero dibujar a personas en un mapa. La clase que contiene a la persona es un simple bean con un nombre, una latitud y una longitud (extiende OverlayItem). La clase ItemizedOverlay (excluyo importaciones):

    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
    public class PersonasItemizedOverlay extends ItemizedOverlay<OverlayItem> {

       
        private List<OverlayItem> mOverlays;
        private Context mContext;

        public PersonasItemizedOverlay(Drawable icon, Context context) {
            super(boundCenter(icon));
            mOverlays = new ArrayList<OverlayItem>();
            mContext = context;
        }

        @Override
        protected OverlayItem createItem(int index) {
           
            return mOverlays.get(index);
        }

        @Override
        public int size() {
           
            return mOverlays.size();
        }

       
        public void addOverlay(OverlayItem overlay) {
            mOverlays.add(overlay);
            populate();
        }

       
        public void addAllOverlays(ArrayList<Persona> azafatas) {

            for (int i = 0; i < azafatas.size(); i++) {
                addAzafata(azafatas.get(i));
            }
            populate();

        }

        public void addPersona(Persona persona) {

            int lat = (int) (persona.getLat());
            int lon = (int) (persona.getLon());
            String nombre = persona.getNombre();

            GeoPoint point = new GeoPoint(lat, lon);
            OverlayItem itempersona = new OverlayItem(point, nombre, null);
            addOverlay(itempersona);
        }

    }

    La clase MapActivity (excluyo importaciones, algunas declaraciones, y la clase interna Mapoverlay:

    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
    public class MostrarMapa extends MapActivity {

       
       
        private List<OverlayItem> moverlays;
       
       

       
        @Override
        protected void onCreate(Bundle icicle) {
            // TODO Auto-generated method stub
            super.onCreate(icicle);
            setContentView(R.layout.maplayout);
            mapView = (MapView) findViewById(R.id.mapa);
            mapView.setBuiltInZoomControls(true);
            mapView.setSatellite(true);
           
            mc = mapView.getController();
            mc.setZoom(23);
            gp1=new GeoPoint((int)(39.960543572003 * 1E6), (int)(-4.8331904411*1E6));
            gp2=new GeoPoint((int) (39.960543574003 * 1E6), (int)(-4.8331906411*1E6));
            gp3=new GeoPoint((int) (39.960543572003 * 1E6), (int)(-4.8331906711*1E6));
            ArrayList <Persona> listapersona=new ArrayList <Persona>();
            PersonaItemizedOverlay persoverlays = new PersonaItemizedOverlay(
                    getResources().getDrawable(R.drawable.ic_launcher), this);
            persona perso1=new Persona(gp1, "Pepe", null);
            Persona perso2=new Persona(gp2, "Juan", null);
            Persona perso3=new Persona(gp3, "Ana", null);
            listapersona.add(perso1);
            listapersona.add(perso2);
            listapersona.add(perso3);
            persoverlays.addAllOverlays(listapersona);
            //Creo que el fallo está aquí
                    moverlays.addAll(listapersona);
           
            mc.animateTo(gp1);
           
           
            mapView.invalidate();
        }

    La verdad, me lío un poco con todo el rollo de Overlays, ItemizedOverlays, etc.

    Si me lo explicas para tontos (como yo) tienes una borrachera pagada :P

    Muchísimas gracias!

    • Fustigador

      Vale, ya conseguí poner los puntos, pero fue más una cuestión de ensayo y error…

      • Roberto

        Puedes poner el codigo o ayudarme estoy realizando pero nop tengo la mas minima idea de como hacer para a lo que me meuva de posicion se capture una nueva posicion y se muestre junto con el primer punto con el que fuistes localizadoo

    • http://about.me/sloy Sloy | Editor

      Perdona, he estado ocupado durante el día. Me alegra que hayas conseguido que funcione. Aún así voy a ignorar el último comentario e intentar explicarlo bien. Porque es cierto que el tema es bastante confuso, siento no haber sido más claro en el artículo.

      A ver, Overlay es el tipo genérico de las capas que se pintan sobre un mapa. Un Overlay es una capa, podríamos decir. Un ItemizedOverlay es un tipo especial de Overlay, o sea de capa, que podemos configurar para mostrar un conjunto de elementos, o items, en una misma capa.

      Cuando construimos nuestro propio ItemizedOverlay tratamos de facilitar el manejo de esta clase adaptándolo a nuestro problema, veo que eso tú lo has hecho bien (si no se me ha pasado nada).

      A la hora de insertar esto en el mapa el proceso es el siguiente:
      1) Obtenemos la lista de Overlays del MapView.
      2) Creamos un objeto de tipo MyItemizedOverlay y le metemos los elementos que queramos pintar.
      3) Añadimos ese MyItemizedOverlay a la lista de Overlays obtenida en el paso 1.
      4) Invalidamos el mapa para que pinte todo. Automáticamente detectará el ItemizedOverlay que hemos insertado.

      En tu caso el paso 1 no veo que lo hagas, ese podría ser el primer fallo (te debe saltar un NullPointerException, no?).
      En segundo lugar el paso 2 parece que lo haces bien, pero me parece que te confundes en el paso 3. Estás añadiendo a la lista de Overlays tu lista de Personas, pero lo que tienes que añadir es el ItemizedOverlay. En tu caso persoverlays.

      (En la explicación hablo de MyItemizedOverlay como forma genérica, en tu caso hablamos de PersonaItemizedOverlay).

      Espero que te quede claro y te funcione bien con eso. Si no o te queda alguna duda dímelo, y haré lo que pueda. Mis dotes de profesor no están muy finas jeje.

      Un saludo y me alegra que te sirva el tutorial ;)

      • Fustigador

        No es que tus dotes de profesor no sean muy finas, es que yo soy un poco (bastante) ceporro :)

        En primer lugar estás en lo cierto, me saltaba un NullPointerException por que no cogía la lista de Overlays del MapView,eso lo solucioné pronto, lo que no me quedaba claro era qué añadir a la lista de Overlays, yo pensaba “Si quiero pintar personas, tendré que pasarle objetos Persona”, pero ahora gracias a tu explicación veo que no es así, dado que, si lo he entendido bien, lo que hay que pasarle es la capa (ItemizedOverlay) que quieres mostrar, incluyéndose en esa capa, esta vez sí, los objetos concretos que se quieren mostrar, Persona en mi caso, y que un objeto Persona, como tal, no puede pintarse en un mapa sin antes meterlo en un ItemizedOverlay.

        También me chocaba el hecho de que en ningún momento nosotros llamamos al método draw(). Me parece un poco “magia” :P

        Aún a riesgo de ser pesado te pediría, por favor, que me corrigieras si estoy equivocado.

        Y de nuevo, muchísimas gracias por tu ayuda, así da gusto!

        Un saludo.

        • http://about.me/sloy Sloy | Editor

          Sí, creo que ya lo has pillado :)
          Quizá sea un poco magia, pero es así. Es el mapa el que se encarga de todo, sólo hay que ponerle la lista de capas y él ya hace el resto.

          Tan fácil que puede ser confuso jajaja
          Espero que te vaya bien con eso, si tienes algún otro problema aquí estoy ;)

    • Squallv

      Hola, podrias poner el codigo completo de las clases?, me seria de mucha ayuda. Gracias ^.^

  • kenneth

    hola, como puedo hacer para que el mapa me ejecute un archivo KML con una ruta entre dos puntos?
    gracias

    • http://about.me/sloy Sloy | Editor

      Eso se aleja un poco de las funcionalidades de la API, personalmente nunca he hecho nada así.
      Para tratar con el archivo KML quizá haya alguna librería para Java, nunca he tratado con ellos.
      Para dibujar rutas como digo no hay nada en la API, tendrías que hacerlo tú. Quizá te sirva esto http://stackoverflow.com/questions/4408671/android-draw-route-on-a-mapview-with-twoo-poi-s

      Suerte, cuéntanos lo que consigas ;)

  • Emanuel

    Hola, primero agradecerte por el artículo que me ha sido de gran utilidad para la realización de mi aplicación. Pero quería hacer una pregunta acerca de si es posible eliminar un grupo concreto de Overlays del mapa.
    He intentado usar el método removeAll sobre la lista de overlays pasando como parámetro aquella lista de Overlays que quiero eliminar. Pero por mucho que lo intento no consigue que los quite del mapa. Sólo he sido capaz con el método clear, pero no me es de interés porque tengo tres tipos distintos de overlays que son mostrados en función de la selección de un grupo de checkBox y eliminados si alguno de estos está deseleccionado.

    Muchas gracias de antemano.

    • http://about.me/sloy Sloy | Editor

      Me alegra que te haya sido útil, gracias.

      A ver si te he entendido bien. Tienes diferentes grupos de objetos que quieres mostrar en el mapa según los checkboxes.

      Si es ese el caso, creo que lo más sencillo es que crees diferentes instancias de tu clase propia de ItemizedOverlay. De esa forma cuando el usuario cambia un checkbox añades o eliminas (métodos add() y remove() de List) cada ItemizedOverlay directamente sobre la lista de Overlays del mapa (mapView.getOverlays())

      Si te he entendido mal voy a necesitar que me lo expliques mejor :P

  • Dvb

    Hola muy buen tuto, en el eclipse no tengo problemas pero no me hago con las instrucciones del keytool para mi keystore, solo se llama map.
    Como seria la linea de comandos? porque siempre me da error.
    Hay que poner map.keystore porquue ni así. Será un fallo tonto porque no sale mucha cosa en google aunque si muchas preguntas sin respuesta. Gracias de antemano.

    • http://about.me/sloy Sloy | Editor

      Sería de ayuda si dices qué error te da, porque puedes estar haciendo mal mil cosas diferentes.

      Tal como dice en la página de Google, el comando debe ser
      keytool -list -alias alias_name -keystore my-release-key.keystore sustituyendo los valores en cada parámetro. El keystore debe llevar .keystore si el tuyo lo lleva, si no no. No es más que el nombre completo del archivo.

      • Dvb

        Gracias por la rapidez de la respuesta.
        Si te digo lo que era…
        Ayer formatee el ordenador y no me di cuenta que habia cambiado el nombre de la unidad del disco duro secundario y cuando lo buscaba por f:\aplis\map era d:\aplis\map.
        Gracias de todas formas por asegurarme la linea de comandos.

  • slimer

    Hola, te felicito por el articulo, muy bueno la verdad.

    Llevo dias intentando que al actualizar la posicion actual se borre el marcador anterior y se quede solo el nuevo.
    Si hago un clear perfecto, los borra todos y pone solo el nuevo, el problema es que tengo otros marcadores que no quiero que se borren, entonces el clear no me vale y no consigo borrar solo ese.

    • http://about.me/sloy Sloy | Editor

      No entiendo del todo tu pregunta, pero si tienes diferentes tipos de marcadores puedes agruparlos en Overlays diferentes, y limpiar sólo el que te interese.

  • kenneth

    hola, estupendo tutorial, pero tengo dos preguntas, la primera es como puedo modificar algunos metodos de la libreria MyLocationOverlay, ya que necesesito, si doy, cambiar un par de cosas de su comportamiento, y tambien queria saber como hacer para pintar esa ruta que tienes tu con dos lineas verdes, ya que solo supe pintarla con una linea gruesa.
    gracias

    • http://about.me/sloy Sloy | Editor

      MyLocationOverlay es una clase más como cualquier otra, si quieres modificarle algo puedes extenderla y sobreescribir lo que necesites. Si quieres el código fuente está más complicado, no está disponible.

      Las líneas verdes son la capa de tráfico de Google Maps que se puede activar en el MapController

      • kenneth

        perfecto!!
        muchas gracias

  • violeta

    Hola!, solo quería agradecerte por la explicación, me ha ayudado ha entender un poco mejor los mapsview. Un saludo!

    • http://about.me/sloy Sloy | Editor

      Fantástico, me alegra saberlo.
      Saludos! :)

  • sergio

    ayudaa, como hago para que cuando ponga el dedo sobre uno de los markers me envie a otra pantalla? xfas T_T

    • http://about.me/sloy Sloy | Editor

      Pues es precisamente lo que está explicado en la última parte.

      • Sergio

        Gracias, una consulta mas, lineas arriba trabajas con entity utilizando dataframework. Y que pasaria si trabajas con una base de datos MySql, en ese caso como sería? Porfas me tiene dando vueltas ese tema

        Gracias

        • http://about.me/sloy Sloy | Editor

          Como digo eso es cosa de la implementación de cada uno. Yo como ejemplo he usado dataframework porque es lo que uso en mi aplicación. Pero perfectamente se puede usar otro sistema.

          Supongo que te refieres a usar sqlite de Android. Hay muchos tutoriales y explicaciones al respecto, eso se escapa del objetivo de éste.
          Un saludo

          • Sergio

            Hola de nuevo, disculpa que te este molestando, pero cuando hago en onTap siempre me devuelve posicion 0 sea cual sea el overlay. Te pongo mi codigo, estoy trabajando con web services y una bd MySql

            public class MyItemizedOverlay extends ItemizedOverlay {
            private List mOverlays;
            private Context mContext;

            public MyItemizedOverlay(Drawable Marker, Context context){
            super(boundCenter(Marker));
            mOverlays = new ArrayList();
            mContext = context;
            }

            @Override
            protected OverlayItem createItem(int index) {
            return mOverlays.get(index);
            }

            @Override
            public int size() {
            // TODO Auto-generated method stub
            return mOverlays.size();
            }

            public void addOverlay(OverlayItem overlay){
            mOverlays.add(overlay);
            populate();

            }

            public void addRestaurante(int posicion){
            int i = posicion;
            HttpClient httpClient = new DefaultHttpClient();

            HttpGet del =
            new HttpGet(“http://192.168.42.190:80/ServicioWebRest/Api/Localizaciones/”);

            del.setHeader(“content-type” , “application/json”);

            try{
            HttpResponse resp = httpClient.execute(del);
            String respStr = EntityUtils.toString(resp.getEntity());

            JSONArray respJSON = new JSONArray(respStr);
            JSONObject obj = respJSON.getJSONObject(i);
            int latitud = (int)(obj.getDouble(“Latitud”)*1E6);
            int longitud = (int)(obj.getDouble(“Longitud”)*1E6);
            GeoPoint point = new GeoPoint(latitud, longitud);
            OverlayItem overlayitem = new OverlayItem(point, null, null);
            addOverlay(overlayitem);
            }
            catch(Exception e){

            }
            }

            @Override
            protected final boolean onTap(int index) {
            Toast.makeText(mContext, “Se ha pulsado el elemento” +index,Toast.LENGTH_SHORT).show();
            return true;
            }

            }

          • http://about.me/sloy Sloy | Editor

            Pues no sé la verdad, quizá sea porque creas el OverlayItem con título y snippet null. No sé cómo se llama internamente al método onTap, así que no sé cómo hace para distinguir el elemento pulsado.
            Es la única diferencia que veo a vote pronto, si no te funciona intenta construirte el mapa desde más simple y añadirle lo que necesites hasta que veas lo que falla.

            En cuanto a la base de datos, si usas webservice es independiente del sistema de persistencia remoto. Pero quizá deberías separar ese consumo de recursos de la lógica y hacer esas llamadas a la api desde la actividad del mapa. Y jamás jamás jamás hacer uso de la red en el hilo principal (en versiones actuales de Android lanza un error al intentarlo). Usa un AsynkTask o un hilo.

          • Sergio

            Cuando te refieres a que tengamos cuidado con el populate a que te refieres? podrias darnos un ejemplo para ilustrar la situacion?

            Gracias

          • http://about.me/sloy Sloy | Editor

            Porque el método populate procesa los nuevos objetos añadidos al mapa. La idea es añadir todos los necesarios y después llamar a populate, pero si son pocos los elementos que se añaden tampoco pasa nada.

            Por ejemplo en mi caso con SeviBus haciendo pruebas intenté añadir las 1000 paradas que hay del tirón llamando a populate() cada vez que añade una, y el móvil se bloqueaba durante unos minutos porque es mucho procesamiento. En cambio si añado todas llamando a populate() únicamente al acabar la respuesta es casi inmediata.

  • David

    Buenas! Otro de la ETSII por aqui intentando acabar con el PFC que me está matando :)

    Justo ahora mismo estoy enganchado en mi aplicacion en mostrar la burbuja de informacion y me vendria estupendamente ese otro post que prometes. He visto en sevibus como lo tienes y es justo lo que quiero, que al pinchar en el overlay salga el globo con una minima informacion y si pinchas en el globo que te lleve a otro activity con mucha mas info.

    ¿Me puedes echar un cable con el tema?

    Muchas gracias y genial post, se entiende todo perfecto!

    • http://about.me/sloy Sloy | Editor

      Recordaba haberte contestado el comentario, pero ahora no veo la respuesta. No sé si no la llegué a publicar o si es ha habido algún problema posterior. En cualquier caso, el post explicando eso está casi listo y a punto de ser publicado.
      Sé que 2 meses son mucho tiempo, pero si sigues interesado y necesitas más información contáctame por correo electrónico y te resolveré las dudas que tengas.

      Perdona y gracias.

      • David

        Muchas gracias, ya conseguí hacerlo tirar pero sin abrir ningun otro activity, solo puse la info que queria en el globo y a correr :)

        La pena es que no pude entregar ya el proyecto porque mi tutor no me aceptó la documentacion, asi que para diciembre

        Gracias por el interes y un saludo

  • Pingback: OSMDROID – Introducción a OpenStreetMap en Android (OSM Parte I) | Androcode()

  • http://ninguno Laar

    una duda, para hacer lo mismo que estas haciendo pero cogerlo desde SQL Server? que solucion me darias? graicas

    • http://about.me/sloy Sloy | Editor

      Pues sólo cambia la fuente de datos. El tutorial es abstracto respecto a eso, es otra historia. En mi caso particular uso una base de datos local mediante la librería DataFramework, pero es aplicable a cualquier otra fuente de datos.

      Yo no tengo experiencia con bases de datos remotas en Android por lo que no te puedo decir cómo hacerlo. Echa un vistazo a la librería OrmLite que la uso de forma local para otros proyectos y por lo que sé es compatible con SQL Server. Siento no poder ayudarte más con ello.

  • http://www.melodescargas.com Angel

    Buenas.
    Me ha interesado mucho tu proyecto.
    Me lo he descargado y tengo problemillas a la hora de ejecutarlo.

    ¿Me podrías hechar una mano?

    • http://about.me/sloy Sloy | Editor

      Olvidé actualizar la información del proyecto para indicar cómo hacerlo funcionar. Perdona pero la limpieza de código y claridad de instrucciones no es prioridad por ahora.

      Te digo que para funcionar, además de los jar incluídos, necesita las librerías externas (importadas en Eclipse como proyectos y seleccionadas como librerías) ActionBarSherlock 4, ViewPager Indicator y mapviewballoons. Están configuradas en el archivo project.properties, pero esa configuración es mía y deberás cambiarla correctamente en Eclipse.

      Si no te queda claro o necesitas algo más contáctame por email y te ayudaré en lo que pueda. Tienes mi dirección en mi perfil del blog.

  • Edu

    Hola, muy interante tu artículo. Muchas gracias por la aportación.
    Yo estoy desarrollando una aplicación que muestra una serie de puntos y le estoy añadiendo paginación, es decir, quiero que muestre los primeros N puntos de la lista y cuando le de a paginar que me muestre los N siguientes. El problema es que una vez pintados los primeros, no sé como hacer para que se “borren”.
    ¿Se te ocurre algo?
    Gracias y un saludo!

  • Duritz

    Gracias por tu artículo, muy bueno.
    El caso es que me funciona perfectamente en mi terminal, pero cuando subo mi aplicación al market (google play) me saca las marcas de las capas, pero el mapa está vacío, solo las cuadrículas.
    Alguna idea?

    • Duritz

      Vale, me contesto yo solo…

      Para generar la API key para el market
      deben primero generar un keyStore personal,
      (Omitir el siguiente parrafo si ya poseen un keyStore)
      ——
      Le dan clic derecho a su proyecto y exportar, de ahi en la ventana que les aparecera seleccionan Export Android Application, dan siguiente, seleccionan el proyecto, y le dan crear un nuevo KeyStore, seleccionan la ruta donde crearan su keystore, y le dan un password, luego de eso siguiente, a continuacion tendran que crear un ALIAS con “otro password”, llenan los demás datos y listo.
      ——

      Cuando ya tengan su KeyStore, entonces desde la linea de comandos (CMD) ponen lo siguiente:

      keytool.exe -list -alias [ElNombreDelAliasQueUsaron] -keystore “[LaUbicacionDeSuKeyStore]” -storepass [ElPasswordDeSuKeyStore] -keypass [ElPasswordDeSuAlias]

      ejm:
      keytool.exe -list -alias miAlias -keystore “c:/Keys/miKeyStore.keystore” -storepass miPassword123 -keypass miAliasPassword321

      Luego copian la huella digital generada y la ingresan en
      http://code.google.com/android/maps-api-signup.html
      y ya tendran la API Key que deben usar para publicar en el market.

      • http://about.me/sloy Sloy | Editor

        Exacto, siento no haber podido responder antes.
        Es lo que digo en el párrafo donde hablo de la clave. Hay que generar una para la firma de debug, y otra para la firma de publicación, la que se usa cuando se compila la aplicación mediante “Export Android Application”. Por lo que necesitarás 2 claves distintas, una para debug y otra para producción.

  • sergi

    Hola;

    Estava provando tu articulo, que esta muy bien explicado, para introducir el manejo de mapa a una aplicación que estoy haciendo y cuando pruebo de cargar la aplació me sale un error i no funciona.

    El error és: java.lang.NoClassDefFoundError

    saben porque no funciona?

    Muchas gracias!

    • sergi

      ya lo solucioné! Muchas gracias!

  • http://www.laideafeliz.es David

    Hola,

    He creado un ejemplo en el que me muestra el donde estoy cogiendo las coordenadas via gps, redes moviles, wifi,… pero no me termina de gustar la posicion.

    Asi que habia pensado que me gustaria poder indicar yo la posicion en el mapa. Es decir, una vez que ya tengo puesto el icono del marcador del “donde estoy” poder hacer zoom en el mapa y hacer clic donde yo se que realmente estoy, y que se redibujase el icono del marcador donde yo hice clic y por tanto ese fuese el valor de la longitud y latitud nuevos, para tratarlos posteriormente (enviar a una bd del telefono, guardar en un xml,….)

    Gracias

    Saludos

    • http://about.me/sloy Sloy | Editor

      Buenas,
      en primer lugar decirte que para mostrar tu posición tienes el MyLocationOverlay que se encarga de toda la gestión de gps y pintar en el mapa por ti, y si necesitas las coordenadas las puedes obtener con su método getMyLocation().

      Dicho esto, para lo que propones tú pienso que lo mejor es detectar las pulsaciones en el mapa y obtener las coordenadas del punto que pulsas. Con eso ya pintas en el mapa lo que necesites usando lo que comento en el tutorial. En mi aplicación yo también obtengo las coordenadas de la pulsación para otro fin pero igual el método te sirve para ver lo que te digo: https://github.com/Sloy/SeviBus/blob/master/src/com/sloy/sevibus/ui/MapaActivity.java#L120

      Un saludo y suerte ;)

  • Pingback: MapViews 2: Globos de información | Androcode()

  • Francisco

    Buenas!!,

    primero darte las gracias por el tiempo empleado en compartir tus conocimientos.
    Y ahora la pregunta.
    Una vez pintados los puntos de interes que queramos,¿ se pueden mostrar por ejemplo solo los mas cercanos al usuario?.

    Muchas gracias por tu ayuda.

  • Francisco

    Muchas gracias por tu ayuda!!!!!

  • ronald

    Muy bueno tu tutorial, Gracias por compartirlo, lo voy a poner en practica. Saludos desde Nicaragua

  • Maxi

    Muy bueno el tutorial, me sirvió muchísimo. Ahora quiero preguntarte como hacer unir con una linea de color los puntos donde definimos nuestros objetos pero yendo por la calle. te agradecería la explicación.

    Muchas Gracias Maxi

  • Jhon Jairo Suarez

    Muchas gracias desde Colombia,

    Muy buen tutorial, lo revisare muy detenidamente para poder aprender lo mas que pueda.

    Gracias de nuevo.

  • Pingback: Nueva versión v2 de la API de Google Maps | Androcode()

  • Link4past

    Hola a todos ^^, primeramente mi enhorabuena a sloy por su magnifico tuto. Os cuento un poco mi caso: estoy desarrollando una app android como parte de mi PFC y estoy atascado en la parte de dibujar los puntos en el mapa, y esto puede deberse a que me confundo un poco con el tema de los overlays y demas. A continuacion dejo mi codigo, si alguien me puediera echar un cable le estaria eternamente agradecido ya que ando un poco desesperado con este tema.

    Aqui es donde hago la conexion a la base de datos y creo los overlays

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    public class Consmapas extends ItemizedOverlay<OverlayItem> {
        private List<OverlayItem> mOverlays;
        private Context mContext;
        public String tip = "3";
        public JSONArray negocios = hacerConexion(tip);
       
        public JSONArray hacerConexion (String tipo){
            String result = "";
            JSONArray jarray = null;
             
             // NameValuePair : Es una clase simple que encapsula el nombre del
             // atributo y el valor que contiene.
             // Creamos una lista de 3 atributos
             ArrayList<NameValuePair> categoria = new ArrayList<NameValuePair>();
             // Añadimos los elementos a la lista convertidos a Strings
             categoria.add(new BasicNameValuePair("tipo", tipo));
             
             //http post
             InputStream is = null;
             try{
                 HttpClient httpclient = new DefaultHttpClient();
                 HttpPost httppost = new HttpPost(
                         "http://localhost/android/obteneranuncio.php");
                 httppost.setEntity(new UrlEncodedFormEntity(categoria));
                 HttpResponse response = httpclient.execute(httppost);
                 HttpEntity entity = response.getEntity();
                 is = entity.getContent();
             }catch(Exception e){
                 Log.e("miError", "Error in http connection "+e);
             }
             
          // convert response to string
             try{
                 BufferedReader reader = new BufferedReader(
                         new InputStreamReader(is,"iso-8859-1"),8);
                 StringBuilder sb = new StringBuilder();
                 String line = null;
                 while ((line = reader.readLine()) != null) {
                     sb.append(line + "\n");
                 }
                 is.close();

                 result = sb.toString();
             } catch(Exception e) {
                 Log.e("miError", "Error converting result "+e.toString());
             }  
             
           //parse json data
             try{
                 jarray = new JSONArray(result);
                 
             }catch(JSONException e){
                 Log.e("miError", "Error parsing data "+e.toString());
             }
             return jarray;
         }
       
     
        public Consmapas(Drawable defaultMarker,Context context) {
            super(boundCenter(defaultMarker));
            mOverlays =new ArrayList<OverlayItem>();
            mContext = context;
        }
     
        @Override
        protected OverlayItem createItem(int index) {
            return mOverlays.get(index);
        }
     
        @Override
        public int size() {
            return mOverlays.size();
        }
       
        public void addOverlay(OverlayItem overlay) {
            mOverlays.add(overlay);
            populate();
        }
       
        public void addNegocio(JSONArray negocios){
            try{
                for(int i=0 ; i<negocios.length() ; i++) {
           
                 JSONObject ob = negocios.getJSONObject(i);
                 String titulo = ob.getString("title");
                 String cat = ob.getString("catid");
                 String coord = ob.getString("extra_fields_search");
                 String latitud = coord.substring(0, 9);
                 int lat = (Integer.parseInt(latitud)*1000000);
                 String longitud = coord.substring(10, 19);
                 int lon = (Integer.parseInt(longitud)*1000000);
                 GeoPoint point = new GeoPoint(lat, lon);
                 OverlayItem overlayitem = new OverlayItem(point, titulo, "Categoria: "+cat);
                 addOverlay(overlayitem);
                }    
            }catch(Exception e){
                Log.e("miError", "Error parsing data "+e.toString());
            }
        }
       
        public void addAllNegocios(JSONArray negocios){
            for(int o=0; o<negocios.length();o++){
                addNegocio(negocios);
            }
            populate();
        }
       
     
    }

    Y aqui es donde esta el main desde el que llamo a la otra clase

    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
    public class MainActivity extends MapActivity {
       
        private MapView mapView;
        private MapController myMapController;
        private List<Overlay> mOverlays;
        private MyLocationOverlay mOverlayLocation;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
     
            mapView = (MapView) findViewById(R.id.mapview);
            myMapController = mapView.getController();
            mOverlays = mapView.getOverlays();
            mOverlayLocation = new MyLocationOverlay(this, mapView);
     
            GeoPoint centro = new GeoPoint((int)(37.380882 * 1E6), (int)(-5.986958 * 1E6));
            myMapController.setCenter(centro);
            myMapController.setZoom(14);
     
            // Sed buenos y meted esto en el onResume, y pararlos en el onPause
            mOverlayLocation.enableMyLocation();
            mOverlayLocation.enableCompass();
     
            mOverlays.add(mOverlayLocation);
           
            Consmapas markers = new Consmapas(getResources().getDrawable(R.drawable.marcador_google_maps), this);
            mOverlays.add(markers);
     
            mapView.postInvalidate();
        }
       
        @Override
        protected boolean isRouteDisplayed() {
        return false;
        }  

    }

    Un saludo y gracias de antemano.

  • Jaime

    Hola, anteriormente habia hecho una aplicacion, que ademas de dibujar puntos en el mapa, trazaba lineas, emulando recorridos.

    Ademas provecho de agradecer el tutorial, pues es bastante bueno, ahora mi consulta es que he tratado de incorporarle un menu, similar al siguiente http://stackoverflow.com/questions/8586945/layout-animation-androidfacebook… pero no he podido lograr la integracion con el mapview. si establezco el mapView antes de los elementos layout, la aplicacion compila pero se cae al momento de ejecutarla… si ubico el mapview despues, no se logra ver.
    Bueno, la verdad no se si sera posible tratar un mapView como un LinearLayout.

    • Squallv

      como has logrado dibujar una linea entre 2 puntos?

      • Jaime

        public void draw(Canvas canvas, MapView mapview, boolean shadow){
        super.draw(canvas, mapview, shadow);

        Paint mPaint = new Paint();
        mPaint.setDither(true);

        mPaint.setColor(Color.parseColor(HexColor));

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(4);

        Point p1 = new Point();
        Point p2 = new Point();

        projection = mapview.getProjection();
        projection.toPixels(this.GeoPointInicio, p1);
        projection.toPixels(this.GeoPointFin, p2);

        Path path = new Path();
        path.moveTo(p2.x, p2.y);
        path.lineTo(p1.x,p1.y);

        canvas.drawPath(path, mPaint);
        }
        Lo anterior dibujara una linea entre dos puntos, asi con un ciclo es posible colocar en un OverlayItem los trazos

    • http://about.me/sloy Sloy

      El MapView es muy puñetero en cuanto te sales de lo tradicional, mapactivity y listo. Se puede encapsular en fragments haciendo chapuzas y tal.
      Pero te recomiendo que directamente pases a la nueva versión de la librería que trae MapFragment y facilita mucho el trabajo con mapas en layouts complejos, además de las otras muchas mejoras. Aquí publicamos la noticia: http://androcode.es/2012/12/nueva-version-v2-de-la-api-de-google-maps/ y el tutorial está en camino ;)

  • andro

    Hola, Sloy, estoy trabajando con mapas en Android y hay una cosa que no consigo que me salga. En mi aplicación, al mostrar la ubicación actual en el mapa aparece un marcador y encima de éste el texto “Estás aquí” dentro de un rectágulo, para lo cual utilizo el método draw() del Overlay. Lo que quiero hacer es que al guardar la ubicación en la base de datos SQLite (tras rellenar unos campos de texto y pulsar el botón “Guardar”), se sustituya el texto anterior por el nombre que yo le he dado a la ubicación. ¿Podrías darme alguna solución? Gracias.

    • http://about.me/sloy Sloy

      Hola.
      No estoy seguro de lo que pretendes hacer, pero para obtener la posición del usuario sólo tienes que llamar al método getMyLocation() de MyLocationOverlay. Con eso ya haces lo que necesites, guardarlo en una base de datos o lo que sea.

      Un saludo.

      • andro

        La posición del usuario la obtengo correctamente, pero quiero que al guardar la ubicación, cambie el texto del mapa y se actualice con el nombre que yo le he dado.

        • http://about.me/sloy Sloy

          Pues dependerá de cómo lo dibujes en primer lugar. Hay muchas formas de mostrar Views sobre el mapa o incluso dibujar directamente sobre él que por lo que dices del método draw() parece que es lo que haces tú. Dependerá de cómo lo hayas implementado, tienes que hacerlo de forma que puedas borrar lo que dibujado y dibujarlo en otra posición.

          • andro

            ¿Se puede borrar lo que se dibuja en el mapa y después volver a pintar otro texto en la misma posición? ¿Con qué método se podría hacer?

          • http://about.me/sloy Sloy

            Nunca he manipulado el mapa de esa forma porque no lo he necesitado así que no te lo puedo explicar, pero te recomiendo que eches un ojo al libro de Reto Meier, Professional Android (también válidos el 2 y 4) Application Development. Tiene un capítulo sobre Mapas que explica bastante bien lo referente a dibujar “a mano” cosas en el mapa.

            Espero que te sirva.

          • andro

            Ok. Gracias por la ayuda, espero encontrar una solución.

  • David

    Hola, me ha gustado mucho tu tutorial, oye una pregunta como le puedo hacer para agregar una barra de menú como la que se muestra en tu aplicación ya que al utilizar los mapas de google me ocupa toda la pantalla y no me deja utilizar otro elemento espero me puedas ayudar

  • Darwin

    Disculpen, alguien me podría ayudar como marcar una ruta entre dos marcadores, osea digamos ver el camino hacia donde ir de mi posición actual a la posición de un marcador fijado en el mapa, digamos en el presente ejemplo una parada de bus, y posteriormente calcular la distancia que existe entre esos dos puntos. Gracias

  • Roberto

    Hola muy interesante tu app estoy haciendo algo parecido pero lo ubico que necesito es que al momento de moverme una distancia “x” se quede marcado con un icono o un punto donde estuve y asi mismo si me muevo a otra distancia que se refleje el punto anterior en el que estuve… si me podrian echar una mano les quedaria aghradecidos gracias

    • http://about.me/sloy Sloy

      Deberías usar el LocationManager para que te notifique de cambios en la ubicación cada x metros, y entonces pintarla en el mapa.

  • Daniel

    Lo primero de todo, muchas gracias por este articulo. Tengo una pequeña duda y es que me muestra el mapa pero no me deja aplicarle zoom ni moverlo. Tengo declarada la variable MapController() por lo que no se a que se debe este error.

    • Daniel

      Al final era una tonteria, se me habia olvidado escribir la sentencia clickeable=”true”.

  • Nacho

    Hola Sloy, excelente tuto, mi más sincera enhorabuena, después de mirar 3 o 4 tutoriales por internet, éste es el que definitivamente me ha aclarado los conceptos.
    Quería preguntarte un par de cosillas:
    1.Como incluir “bocadillos de información” (tipo cómic) al estilo Google Maps al clickar sobre un marcador. Comentas al final del tuto que lo incluirías en un próximo tutorial pero no sé si ya lo has hecho o podrías facilitarme la info de como hacerlo.
    2.Como traducir direcciones reales a coordenadas (latitud, longitud), es decir, para pintar puntos sobre el mapa la API de Android trabaja con coordenadas pero yo tengo que recibir una dirección (tanto sus coordenadas como la direccion física real o el código postal) y dibujar ese punto. Para ello tengo que traducir todos estos tipo de direcciones a coordenadas. He mirado por ahí que la clase GEOCODER de Location hace algo de esto, pero no sé muy bien como funciona.
    Si pudieras echarme un cable te lo agradecería.

    Saludos crack.

  • Nacho

    Perdón por la ignorancia, no había visto tu segundo tutorial acerca del punto 1 que te acabo de consultar. Por tanto, únicamente agradecería información sobre el punto 2 de mi consulta.

    Gracias.

    • http://about.me/sloy Sloy

      El uso de la clase es sencillo, el método getFromLocation() te devuelve una lista de direcciones que coinciden con las coordenadas que le pasas como parámetros. Es cuestión de hacer algunas pruebas para ver si te conviene coger la primera mismo, o filtrarlas de alguna forma.
      Aunque no la he usado, supongo que hará uso de red por lo que preferirás hacer las llamadas en otro hilo, quizás con un AsyncTask.
      La clase es https://developer.android.com/reference/android/location/Geocoder.html

  • Ivan

    Muy buenas he visto tu tutorial y es fantástico, gracias por aportar, pero mi pregunta es, ahora que tenemos que migrar a la API v2 las capas Overlay como psarían a ser en esta nueva API? porque es una locura.
    Gracias

    • http://about.me/sloy Sloy

      Hola,
      lo mejor es mantener un mapa relacionando los marcadores que añades al mapa con tus objetos del dominio, que haría de sustituto al MyItemizedOverlay de este tutorial.
      Echa un ojo a este hilo de StackOverflow en el que trata precisamente eso: http://stackoverflow.com/a/13714231/835787

  • http://www.facebook.com/izkarnsp Izkar Ns Pérez R

    Hola necesito ayuda realice todos lo pasos tal cual pero me aparece este mensaje:

    Missing styles. Is the correct theme chosen for this layout?
    Use the Theme combo box above the layout to choose a different layout, or fix the theme style references.

    Failed to find style ‘mapViewStyle’ in current theme

  • http://carlosgranadosm.wordpress.com carlosgranadosm

    Hola por favor alguien me explicas las posibles razones por la que no pinta el mapa, sale solo cuadros blanco sobre la actividad.

    Ya probe todo lo que comentaban aqui y nada.

  • yayi

    Hola ya lo probé al pie de la letra y no me aparece el mapa y nada me sale una pantalla negra

    • yayi

      me podrías proporcionar el código por favor aun nose si me fata alguna libreria

    • Carlos Segura

      creo que te falta crear tu propia api key

  • carlos98172738

    Hola amigo,,solamente kisiera sabe como puedo marcar una ruta a un punto especifico, por decir mi escuela kiero q sea el punto prdeterminado y mi localizacion el otro punto variable,,como le pudo hacer??

  • Sergio A A

    Hola amigo,muy buen tutorial, me gustaría saber si es posible agregar una capa .shp a este ejemplo?
    Gracias.

  • https://www.facebook.com/aj.degel AJ Degel

    Muchas gracias por el tutorial es posible fusionar esto con realidad aumentada?

  • martin

    Buenas tardes, estoy asiendo una aplicación para taxi y no se si me pueden ayudar pero estoy buscando en google maps de punto a punto ósea lo que quiero decir es que el taxista encuentra el punto del cliente y google maps le da la ruta de como llegar al punto del cliente.

    Espero que me puedan ayudar.

    Saludos….

    • Gaston de la cruz

      Hola que tal? estoy arrancando con un proyecto similar y quisiera saber si me podrían orientar un poco, ya que también estoy iniciando el proyecto con android studio, desde ya muchas gracias.

  • daniel gimenez

    Excelente amigo buen tutorial, actualmente quiero iniciar con un proyecto como el tuyo de SeiviBus, para implementarlo en mi ciudad en argentina para las rutas de buses y busetas y quisiera saber si puedo implementar algunas funciones de tu código, y como hago para hacerlo funcionar en android Studio. Agradezco su atención

  • Mario German Agudelo

    Hola gracias por compartir todo tu conocimiento, estoy haciendo un proyecto para tiendas y me gustaría que cuando se yo cree una nueva tienda no tenga que cargarla desde la aplicación sino solo al ingresar la longitud y latitud en una base de datos, por medio de la aplicación se actualice la información y el usuario pueda ver en el mapa la nueva tienda, tienes alguna idea de cómo se podría hacer. Agradezco tu ayuda.