domingo, 30 de dezembro de 2012

Android Google Maps v2

Olá povo,

No último dia 03/12/2012  a Google disponibilizou a versão 2.0 da API do Google Maps para Android que trouxe diversas melhorias em relação a versão anterior. O problema é que tudo mudou completamente :) Desde a geração da chave de acesso até as classes envolvidas. A parte boa disso tudo, é que essa API funciona desde a versão 2.2 do Android e trouxe melhorias significativas \o/
Nesse post vou mostrar como dar os primeiros passos nessa nova API.

Gerando a chave de acesso

Quem já fez algum exemplo sabe que precisamos de uma chave de acesso para utilizar o serviço de mapas. Para ter acesso aos mais diversos serviços do Google, precisamos criar um projeto no site do Google APIs Console. Uma vez que o projeto foi criado, acesse a opção Services e habilite o serviço Google Maps Android API v2. Uma vez habilitado o serviço, acesse a opção API access depois clique no botão Create new Android key, no popup que for exibido clique em Create.
Será gerada uma chave no padrão SHA-1 que usaremos no nosso código similar a essa: AIzbSyCyoMobvh72ZrSv4xQddOLzDOlaLqCcILU

Biblioteca dos serviços do Google Play
A segunda etapa é baixar o pacote de serviços do Google Play. Abra o Android SDK Manager e baixe o Google Play services.
Quando terminar o download, será criado o seguinte diretório [android-sdk/extras/google/google_play_services/libproject] que contém as classes necessárias para usar a nova API. Precisamos importar esse projeto para dentro do Eclipse, pois nosso projeto irá referencia-lo.
Acesse a opção File | New project... Em seguida, selecione Android | Android Project From Existing Code e então selecione o diretório citado anteriormente. O projeto será importado para o Eclipse e agora podemos começar nosso projeto de exemplo.

Projeto de exemplo
Crie um novo projeto Android e vamos começar a brincadeira :) A primeira coisa é referenciar o projeto  do Google Play Service dentro do nosso projeto. Para isso, clique com o botão direito sobre o projeto e selecione  Properties. Na janela que for exibida, selecione Android do lado esquerdo. Clique no botão Add... e selecione google-play-services_lib e então clique em Ok.
Estamos prontos pra começar, deixe o AndroidManifest.xml como abaixo.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android=
  "http://schemas.android.com/apk/res/android"
  package="ngvl.testegmaps_v2"
  android:versionCode="1"
  android:versionName="1.0" >

  <uses-sdk
    android:minSdkVersion="9"
    android:targetSdkVersion="15" />

  <uses-permission android:name=
    "android.permission.INTERNET" />
  <uses-permission android:name=
    "android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name=
    "android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name=
    "com.google.android.providers.gsf.permission.READ_GSERVICES" />

  <uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />

  <application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
      android:name="ngvl.testegmaps_v2.MainActivity"
      android:label="@string/app_name" >
      <intent-filter>
        <action android:name=
          "android.intent.action.MAIN" />
        <category android:name=
          "android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    <meta-data android:name=
      "com.google.android.maps.v2.API_KEY"
      android:value="suaApiKeyAqui!!!!!!" />
  </application>
</manifest>

Vamos comentar alguns detalhes do arquivo. Começamos utilizando as permissões de INTERNET e WRITE_EXTERNAL_STORAGE para podermos baixar os mapas e fazer cache dos mesmos no cartão de memória. Um requisito dessa nova API é que o aparelho deve suportar OpenGL que declaramos logo em seguida. A última configuração importante que fizemos aqui é a tag meta-data que declaramos a chave do Google Maps que geramos na primeira etapa desse post.
Agora deixe o arquivo de layout conforme abaixo.
<fragment
  xmlns:android=
    "http://schemas.android.com/apk/res/android"
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:name=
    "com.google.android.gms.maps.SupportMapFragment" />
Aqui temos uma grande mudança: a classe MapView e MapActivity não são mais utilizadas. Em seu lugar entrou a classe MapFragment, mas como quis deixar a compatibilidade com versões anteriores do Android, usei a SupportMapFragment. Isso nos permite utilizar mapas em fragments, o que só era possível anteriormente através de APIs de terceiros, uma vez que tínhamos que herdar de MapActivity. Outro detalhe é que o nome do pacote também mudou, agora é com.google.android.gms.maps.
Por fim, vamos ao código da Activity.

package ngvl.testegmaps_v2;

import android.os.Bundle;
import android.support.v4.app.*;

import com.google.android.gms.common.*;
import com.google.android.gms.maps.*;
import com.google.android.gms.maps.model.*;

public class MainActivity extends FragmentActivity {

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
 
    SupportMapFragment fragment =
     (SupportMapFragment)getSupportFragmentManager()
       .findFragmentById(R.id.map);
    GoogleMap map = fragment.getMap();

    LatLng latLng = new LatLng(-23.561706,-46.655981);
    map.addMarker(new MarkerOptions()
      .position(latLng)
      .icon(BitmapDescriptorFactory.fromResource(
        R.drawable.ic_launcher))
      .title("Av. Paulista")
      .snippet("São Paulo"));
  
    configuraPosicao(map, latLng);
  }

  private void configuraPosicao(
    GoogleMap map, LatLng latLng) {

    map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
    map.animateCamera(
     CameraUpdateFactory.newLatLngZoom(latLng, 17.0f));
  }
}
Nossa classe herda de FragmentActivity da API de compatibilidade do Google que falei aqui. Em seguida, obtemos a referência para o MapFragment, e a partir dele obtemos a instância do GoogleMaps (que substitui o MapView). A classe GeoPoint foi substituída pela classe LatLng que representa uma coordenada geográfica. Com essa informação, adicionamos um ponto no mapa, passando uma imagem que servirá de marcador no mapa, um título e um resumo, que serão exibidos ao clicar na imagem no mapa. O método configuraPosicao, define o tipo de mapa para satélite e o zoom para 17 (os valores variam de 2 a 21, sendo 21 mais próximo possível).
Se rodarmos nossa aplicação, em um aparelho 2.3 ficará similar a figura abaixo:
Uma das novidades dessa versão da API do GoogleMaps é poder visualizar o mapa em relevo, mostrando, por exemplo, os prédios do local selecionado no mapa. Modifique o método configuraPosicao para ficar como abaixo:
private void configuraPosicao(
  GoogleMap map, LatLng latLng) {
  map.moveCamera(
   CameraUpdateFactory.newLatLngZoom(latLng, 15));
  map.animateCamera(
   CameraUpdateFactory.zoomTo(10), 2000, null);

  CameraPosition cameraPosition = 
    new CameraPosition.Builder()
      .target(latLng)   
      .zoom(17)     
      .bearing(90)
      .tilt(45)
      .build();
  map.animateCamera(
    CameraUpdateFactory.newCameraPosition(
      cameraPosition));
}
Notem que não usamos o modo satélite aqui (não ficou muito bom quando usei :) mas posicionamos a câmera de uma maneira diferente. Além da Latitude/Longitude e do Zoom, setamos o bearing (rotação do mapa em 90 graus) e o tilt (inclinação em 45 graus). O resultado pode ser visto abaixo.
Bem pessoal, espero que tenham gostado e qualquer dúvida, deixem seus comentários.
Mais detalhes em: https://developers.google.com/maps/documentation/android/

 4br4ç05,
nglauber

44 comentários:

Unknown disse...

Glauber,
Em minhas tentativas sempre é retornado o erro:
01-14 19:19:49.426: E/AndroidRuntime(555): java.lang.NoClassDefFoundError: com.google.android.gms.R$styleable
01-14 19:19:49.426: E/AndroidRuntime(555): at com.google.android.gms.maps.GoogleMapOptions.createFromAttributes(Unknown Source)

Já adicionei a lib do Google Play Services, mas o erro persiste!

Tem alguma idéia do por quê?

Nelson Glauber disse...

Oi Felipe,

Você está tentando rodar no emulador? Se sim, a API só funciona no dispositivo real (que eu saiba).

4br4ç05,
nglauber

Unknown disse...

Glauber, descobri o problema. Em minhas tentativas estava utilizando o .jar do google-services na qual (pelo o que eu entendi) não continha a R.class do projeto.

Resolvi isso importanto o projeto google-play-services_lib e nas propriedades do meu projeto marquei ele como Library, tirando a necessidade do .jar! Assim consegui acessar a R.class do projeto :)

Obrigado! (:

Nelson Glauber disse...

Oi Felipe,

Que bom que resolveu. Não segue os passos do tutorial é no que dá :)

4br4ç05,
nglauber

Unknown disse...

Segui os passos e tive problema em emular com o AVD.
Troquei para emular no dispositivo real e funcionou direitinho.

Valeu! :D

Unknown disse...

utilizei a API 8 e estou testando em um tablet ele entra na aplicaçao mais nao aparece o mapa, o log cat da um erro assim

01-18 16:42:12.341: E/Google Maps Android API(2176): Google Maps application is missing.


oque pode ser ?

Nelson Glauber disse...

oi Jhonny,

Pelo que diz a mensagem, o aparelho não tem a aplicação Google Maps instalada.

Outra pessoa já teve o mesmo problema.
http://stackoverflow.com/questions/13727439/new-google-maps-v2-not-displayed-on-android-2-2

4br4ç05,
nglauber

Unknown disse...

consegui mostrar a imagem do mapa, mas eu tive que usar a Key for browser apps e nao a Key for Android apps, esta certo isso, ou tem coisa errada ?

Nelson Glauber disse...

Oi johnny,

Teoricamente não deveria funcionar com a chave de Browser, mas... :)

4br4ç05,
nglauber

Unknown disse...

agora esta funcionando certo, muito obrigado pela sua ajuda, sempre estou aprendendo com o seu blog e recomendando aos outros programadores novos como eu.

Unknown disse...

Boas pessoal,

Estou a utilizar Android Google Maps v2 na minha aplicação e queria colocar vários botões nas infowindows dos markers. O problema é que pelos vistos não é possível interagir com componentes dentro da infoWindow, visto que o que nos é mostrado é uma imagem. Gostaria de saber se alguém já teve de fazer os mesmo e se sabe se alguma alternativa válida.

Obrigado pela ajuda ;)

Nelson Glauber disse...

Oi João,

Acho que é isso que você está precisando.

https://developers.google.com/maps/documentation/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter

4br4ç05,
nglauber

Ricardo disse...

Nelson, por gentileza, por o que vi nos post anteriores esta API só funciona num emulador real, gostaria de saber se logo esta api irá funcionar no emulador ? e o por que não funciona no emulador ? ...

vlw

Nelson Glauber disse...

Oi Ricardo,

A API só funciona no dispositivo real (pelo que testei). Ela não funciona no emulador porque necessita do Google Play instalado (que o emulador não tem).

4br4ç05,
nglauber

Unknown disse...

Glauber, tem como adicionar mais de um addMarker no mesmo mapa? Obrigado.

Nelson Glauber disse...

Oi Leandro,

Teoricamente seria só chamar o addMarker novamente com outros valores.

4br4ç05,
nglauber

Leonam disse...

Olá Nelson,

Primeiramente parabéns pelo blog e obrigado pelo post esclarecedor. Dúvida, testei no meu Galaxy S 2.3.3 e funcionou bem, mas não apareceu o title e nem o snippet acima da imagem que inseri, a imagem apareceu sozinha!

O que pode estar acontecendo??

Nelson Glauber disse...

Oi Leonam,

Para o balão aparecer você deve clicar na imagem.

4br4ç05,
nglauber

Marcus Pimenta disse...

Olá Glauber,

Primeiramente gostaria de parabenizar pelo Blog, seus posts tem me ajudado bastante.

Tenho um aplicativo que utiliza a versão antiga do Google Maps, através do seu post comecei a migra para a versão 2.0. Estou querendo colocar uma configuração para o usuário escolher se ele quer armazenar em cache o mapa só quando estiver conectado pela rede wifi ou redes móveis. Já procurei sobre isso, mas não encontrei nada.

Você sabe como posso fazer essa configuração?

Desde já agradeço a atenção.

Marcus Pimenta

Michaeloc disse...

Olá...Glauber. Não estou conseguindo rodar o exemplo. Fiz todos os passos descritos no tutorial. Estou tentando rodar em um tablet samsung GT-P3110. Não estou conseguindo acompanhar o log. Estou utilizando wi-fi. O aplicativo abre, mas não mostra o mapa, apenas os botões de zoom (+/-).

Nelson Glauber disse...

Oi Fundamentos da Computação,

Isso normalmente acontece quando você não coloca a permissão de Internet ou a API Key no AndroidManifest.xml.

4br4ç05,
nglauber

Michaeloc disse...

Ok..Glauber. Obrigado pela atenção. O meu problema estava na falta de atenção na hora de obter a key. Anteriormente havia dito que estava com problema em converter LatLng para GeoPoint. Fiz o que vc indicou, o aplicativo está rodando entretanto não mostra a rota...Para transformar de GeoPoint para LatLng posso passar o GeoPoint.getLatitudeE6 para o construtor de LatLng?

Nelson Glauber disse...

Oi Fundamentos de Computação,

É exatamente isso.

4br4ç05,
nglauber

Unknown disse...

Pra galera que não está conseguindo usar a v2 no emulador basta seguir esses passos:

1. Crie um novo AVD (não use Google Apis como target);
2. Inicie o emulador criado;
3. Baixe e instale os apks com.android.vending-1.apk (Google Play Store) e com.google.android.gms-1.apk (Google Play Services) pela linha de comando usando o ADB (adb install caminho_apk/nome.apk).
4. Verifique se seu projeto marca como target a Google APIs. Se for o caso desmarque e use uma api diferente.
5. Rode o projeto e seja feliz.

Comigo funcionou!
Fonte: nemanjakovacevic.net/blog/2012/12/how-to-make-android-google-maps-v2-work-in-android-emulator/

Unknown disse...

Glauber, como faço para publicar minha aplicação pois, quando debugo o código o mapa funciona, mas quando coloco-a no play store o mapa não aparece?

Nelson Glauber disse...

Oi Dene,

Você deve usar o API console para gerar uma chave com a assinatura do Google Play.

4br4ç05,
nglauber

Jose Wilton Barbosa Procopio disse...

ola Glauber blz tava vendo seu post aki e tentei rodar ele aki num emulador so q ele da um erro no seguinte trecho

map.addMarker(new MarkerOptions()
.position(latLng)
.icon(BitmapDescriptorFactory.fromResource(
R.drawable.ic_launcher))
.title("Av. Paulista")
.snippet("São Paulo"));

mais precisamente nessa parte aki

.icon(BitmapDescriptorFactory.fromResource(
R.drawable.ic_launcher))

Unknown disse...

Fala Glauber tudo bom? Primeiramente queria agradecer seus artigos sobre android, muito bons mesmo. Sempre que tenho alguma dúvida passo por aqui hehe. Porém ao tentar fazer rodar essa nova versão da api para o maps do android, estou encontrando algumas dificuldades em relação ao funcionamento da mesma.

Segui todos seus passos:
1-Gerei a key
2-importei o projeto google-play-services_lib
3-no meu novo projeto android fui em properts e add o google-play-services_lib pra dentro dele.

Só que, no meu mainActivity está aparendo erros nas importações, e em alguns recursos como: SupportMapFragment, GoogleMap, LatLng, MarkerOptions, BitmapDescriptorFactory e CameraUpdateFactory. Acredito eu que seja por causa das importações estarem com erro.
O que pode ser Glauber?

obs: o meu novo projeto que fiz no android é na versão 4.0.3, e o google-play-services_lib é na versão 2.3.3 seria esse meu erro? ou não tem nada haver?

Aguardo resposta.

Obrigado, Abraços.

Nelson Glauber disse...

Oi Wyllyan,

Tenta remover o android-support-v4.jar da libs do teu projeto. Acho que o projeto do Google Play já tem essa biblioteca, aí pode dar problema de duplicidade.

4br4ç05,
nglauber

Wesley O. Gado disse...

Cara, parabéns! De tantos tutoriais que pesquisei, na minha opinião, o melhor, bem explicado e sem firula!

Fábio Lima disse...

O meu aparece erro no LogCat assim:
Failed to find provider info for com.google.settings e Failed to find provider info for com.google.android.gsf.gservices
E no emulador aparece a tela cinza. Detalhe: eu sei como emular o no android, é só instalar o com.android.vending-xxx.apk e com.google.android.gms-xxx.apk que funciona.
Minha chave está correta e foi gerada no mesmo computador.
Alguém pode ajudar?

Unknown disse...

Caras, depois de muitas tentativas consegui rodar o negócio, fiz tudo certo e estava sempre dando o erro "E/AndroidRuntime(555): java.lang.NoClassDefFoundError: com.google.android.gms.R$styleable", acho que o Samsumg N8000 (Android 4.1.2 - API 16) não tem essa lib, pois só parou de dar erro quando eu resolvi, enfim, Exportar o apk e instalar nele para ver o que aconteceria, aí ele rodou de 1ª e agora está rodando via Eclipse também. Uma coisa que eu curti na V2 foi a Chave genérica (a mesma para DEBUG e Release), não precisa mais ficar trocando ela. Agora vou converter minha app da V1 para a nova, inclusive eu sempre quis desenhar retângulos no Mapa para "zonear ele" e parece que essa versão permite isso. Valeu pelas dicas.

Edwin Carlo Ribeiro Marinho disse...

Muito bom o post. Exemplo prontinho de como usar as coordenadas (latitude e longitude) para marcar um ponto no mapa.

Unknown disse...

Cara estou iniciando desenvolvimento em Android, minha duvida é a seguinte nesse exemplo você passou direto os valores de Latitude e Longitude e se eu quisesse pegar esses valores de forma automática no método LatLng ele aceitaria ?
Já fiz baseando em outros exemplos que achei porem nunca da certo rs!

Vlw
Abraços;

Ewerton Fontes.

Nelson Glauber disse...

OI Ewerton,

Você usaria parte do código desse post. E pegaria os dados do GPS do aparelho.
http://nglauber.blogspot.com.br/2011/10/google-maps-e-gps.html

4br4ç05,
nglauber

rdfreitas disse...

Uma duvida.
Você precisou do google service instalado para rodar o mapa v2 ??

Nelson Glauber disse...

oi rdfreitas,

Sim, precisa estar instalado. Por isso que só funciona no dispositivo real (até o momento).

4br4ç05,
nglauber

SOUZA disse...

OI Glauber parabéns pelo poster....o meu problema é que o meu aparelho não está carregando o mapa....instalou direitinho..mais quando abro ele não carrega o mapa...tenho instalando o serviso do google maps...

Nelson Glauber disse...

oi Souza,

Normalmente isso acontece quando você não coloca a chave corretamente no AndroidManifest.xml ou quando a permissão de internet não foi adicionada nesse mesmo arquivo.

4br4ç05,
nglauber

Muller disse...

Muito obrigado, funcionou perfeitamente !!!! Melhor tutorial encontrado !

Unknown disse...

ao rodar a aplicação num tablet(máquina virtual VMware), o mapa aparece todo preto, só com os botões de + e - e onome google à esquerda. Meu manifest está identico ao tutorial da google, todas as users permissions, permissions meta-data com a chave, está certo....
o que pode estar acontecendo?

Unknown disse...

só complementando o post:

1. Quando criar o projeto no Google APIs Console [https://code.google.com/apis/console/]. Acesse a opção API e marque como ON a opção Google Maps Android API v2. Uma vez habilitado, acesse a opção Credentials e clique em Create a new Key e depois em Android Key

2. Eu usei a google-play-services_lib no Android Studio 0.5.8. A única coisa que eu fiz foi adicionar essa linha:
compile 'com.google.android.gms:play-services:3.1.36'
no build.gradle, e só.

Nelson Glauber disse...

Valeu Muller!

Adriano, pra o mapa funcionar, o dispositivo tem que possuir o Google Play (aplicativo) e o Google Play Services instalado.

Valeu pela dica Frank, tô gostando muito do Android Studio.

4br4ç05,
nglauber

Unknown disse...

Parabéns pelo post Glauber, funcionou muito bem no MotoG que estou usando para testes, foi necessário apenas acrescentar a seguinte meta informação no Manifest:


A IDE (Eclipse - ADT)