Vous êtes sur la page 1sur 27

Aprenda LBS em Android na Prtica Parte 1

Introduo
Sistemas Baseados em Localizao (LBS Location Based System) esto cada vez mais comum, no s em aparelhos mveis e no ramo automotivo, a esfera de utilizao destes servios tambm aumenta gradativamente e, ao que tudo indica, essa curva de crescimento ainda tem muito flego. Segundo relatrio realizado recentemente pela JiWires, os usurios esto achando o fato de informarem seus dados para seus amigos cada vez mais comum, tambm, estas mesmas pessoas esto mais propcias a receberem anncios, cupons promocionais e outros, de estabelecimentos que estejam prximos a sua localizao. Esta mesma pesquisa indica que mapas o servio LBS com maior aceitao. E no so somente estes nmeros que indicam isso. visvel a crescente demanda de aparelhos com GPS. Todas as principais plataformas lderes de mercado tm uma alta sensibilidade a aplicativos LBS. E no s isso, a liderana isolada do GPS (Global Position System) pode estar ameaada, isso porque existem outras tecnologias chegando para competir neste nicho de mercado. E, competio, na maioria dos casos, gera melhorias nos sistemas estagnados. A importncia do conhecimento de programao deste tipo de aplicao parece estar bem clara para os programadores, analistas de sistemas e gerentes de projetos. Soma-se a isso o crescimento contnuo de outra plataforma, o Android, e tm-se uma demanda de mercado: conhecimento em criao de LBSs para smartphones com o sistema operacional do Google. Este artigo visa sanar justamente esta brecha. Para isso, vamos seguir o modelo do artigo Acessando aplicativos nativos de um Smartphone com a BlackBerry API, publicado na revista WebMobile, edio de nmero 30. Neste artigo criamos uma aplicao que mostrava os contatos do device, permitindo que a visualizao de um mapa 2D da localizao de cada um deles. Na prxima verso deste artigo, vamos dar um passo alm,

permitir tambm visualizao da rota de onde o usurio est at a localizao do seu contato. Bem, vamos a codificao.

Criar a tela de contatos


O primeiro passo para esta aplicao criar a interface grfica da pgina principal, que ir mostrar todos os contatos do dispositivo que informem o endereo. Logo, nosso primeiro objetivo, codificar a tela da Figura 1:

Figura 1: Primeira tela da aplicao.

Depois de criar o projeto WhereIsMyContact, o primeiro passo configurar as permisses da nossa aplicao. Isso porque estaremos lendo os contatos do dispositivo. Para isso, abra a edio do AndroidManifest.xml e inclua a linha sublinhada:
... </application> <uses-permission android:name="android.permission.READ_CONTACTS" /> </manifest>

Obs: Se o leitor iniciante no Android, por favor, leia os outros artigos sobre programao Android em http://issuu.com/ricardoogliari. Agora, vamos editar o arquivo main.xml para definir nossa interface grfica. Veja a Listagem 1:

Listagem 1: 01: <?xml version="1.0" encoding="utf-8"?> 02: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" 03 android:layout_width="fill_parent" 04: android:layout_height="fill_parent" > 05: <TextView 06: android:layout_width="fill_parent" 07: android:layout_height="wrap_content" 08: android:text="Escolha um dos contatos (com endereo):" /> 09: <Spinner android:layout_width="fill_parent" 10: android:layout_height="wrap_content" 11: android:id="@+id/spNomes" /> 12: <Button android:layout_width="80px" 13: android:layout_height="wrap_content" 14: android:id="@+id/btnGo" 15: android:text="Ver" 16: android:layout_gravity="right" 17: android:layout_marginTop="15px" /> 18: </LinearLayout>

Utilizamos o gerenciador de layout LinearLayout, para mostrar os componentes em uma seqncia linear e vertical (Linha 2). Nas linhas 3 e 4 definimos que ela ocupar toda a tela horizontalmente e verticalmente. Na linha 5 inserimos uma View de texto, do tipo TextView. Na linha 6 informamos que o componente ir ocupar toda a largura da tela, na linha subseqente definimos sua altura como igual a do seu contedo. Por fim, definimos seu texto na linha 8, dizendo para o usurio escolher um dos contatos mostrados na listagem. Sendo que, s sero apresentados os nomes que contenham endereo cadastrado. O Spinner criado na linha 9. Definimos sua largura como o tamanho da tela (linha 10). A altura igual ao tamanho do componente (linha 11). E tambm precisamos informar seu id (linha 12) . O Button criado na linha 12. Definimos sua largura como 80 pixeis. A altura igual ao tamanho vertical do componente (linha 13). Definimos seu id na linha 14. O texto Ver informado na linha 15. Na linha 16 alinhamos o componente a direita da tela e, finalmente, na linha 17 definimos sua margem superior como 15 pixeis. E como ficaria a nossa Activity. Veja a Listagem 2:
Listagem 2: 01: public class WhereIsMyContact extends Activity { 02: private Spinner sp; 03: 04: public void onCreate(Bundle savedInstanceState) {

05; 06: 07: 08: 09:

super.onCreate(savedInstanceState); setContentView(R.layout.main); } } sp = (Spinner) findViewById(R.id.spNomes);

A Listagem acima contm apenas o b-a-b do Android. Apesar de recuperarmos o Spinner na linha 8, ainda no adicionamos os nomes dos contatos na sua lista.

Buscando as informaes dos contatos


Nossa classe ter um mtodo muito importante, o LerContatos:
private ArrayAdapter<String> lerContatos(){

Veja o contedo do mtodo na Listagem 3:


Listagem 3: 01:ContentResolver cr = getContentResolver(); 02:Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null); 03: 04:if (cur.getCount() > 0) { 05: Vector contatos = new Vector(); 06: Vector enderecos = new Vector(); 07: 08: while (cur.moveToNext()) { 09: String id = cur.getString(cur.getColumnIndex( 10: ContactsContract.Contacts._ID)); 11: String name = cur.getString(cur.getColumnIndex( ContactsContract.Contacts.DISPLAY_NA ME)); 12: 15: String addrWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; 16: String[] addrWhereParams = new String[] { id, ContactsContract.CommonDataKinds. StructuredPostal.CONTENT_ITEM_TYPE }; 17: 18: Cursor addrCur = cr.query( ContactsContract.Data.CONTENT_URI, null, addrWhere, addrWhereParams, null); 19: 20: if (addrCur.moveToNext()) { 21: String street = addrCur.getString(addrCur .getColumnIndex( ContactsContract.CommonDataKinds. StructuredPostal.STREET));

22: 23:

String city = addrCur.getString(addrCur . getColumnIndex(ContactsContract.CommonDataKinds.StructuredP ostal.CITY)); if (street != null && city != null){ enderecos.addElement(street+", "+city); contatos.addElement(name); }

24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:

addrCur.close(); } cur.close(); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, contatos);

35: 36: return adapter; 37:} else { 38: return null; 39:}

OBS: Sua explicao longa e ficar lendo o texto e voltando at aqui para ver o cdigo fonte vai atrapalhar muito. Ento, deixe a IDE aberta e alterne entre a janela do artigo e a da IDE. Na linha 1 usamos o mtodo getContentResolver para criar a instncia de ContentResolver. Esta classe possui um mtodo query, usado na linha 2, que serve como uma consulta aos dados de contedo do aparelho. Ele realmente se assemelha muito as querys do SQL. O Android possui algumas tabelas de dados que podem ser consultados atravs do ContentResolver. As informaes de contatos esto em ContactsContract.Contacts.CONTENT_URI . Nas verses anteriores ao Android 2.0, a URI outra. O mtodo query ainda possui outras features, como a possibilidade de buscar os dados com uma ordenao, porm, isso no ser necessrio aqui, mas bom saber disso. Os dados retornados da consulta so uma instncia de Cursor. Seu nome j diz tudo, uma forma de navegar pelos resultados que a consulta retornou do banco de dados. Na linha 4 verificamos se o Cursou possui mais de um item, caso positivo, entramos em uma extensa lgica de cdigo, caso contrrio, retornamos null para o mtodo e encerramos a busca (linha 38). Nas linhas 5 e 6 criamos os vetores com os nomes e endereos dos contatos, respectivamente.

Na linha 8 utilizamos o mtodo moveToNext() do Cursor para navegar entre seus elementos. Nas linha 9 e 10 buscamos o id e o nome de cada contato. O id ser importante posteriormente. Note, os dados bsicos do contato, esto em ContactsContract.Data. O leitor pode acessar o site http://developer.android.com/reference/android/provider/ContactsContract. Data.html para saber sobre as outras constantes que esto disponveis. Apenas para matar a curiosidade, veja algumas delas na Tabela 1:
Tabela 1:

Na linha 18 vamos criar um novo cursor para buscar as outras informaes necessrias do contato. Perceba que no estamos mais passando somente null para o mtodo query. Na verdade estamos passando uma clusula (addrWhere), que foi configurada na linha 16, e um conjunto de parmetros (addrWhereParams), que foi criado na linha 17. A linhas 16 e 17 merecem uma ateno especial:
String addrWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?"; String[] addrWhereParams = new String[] { id, ContactsContract.CommonDataKinds.StructuredPostal. CONTENT_ITEM_TYPE };

Perceba que estamos limitando a pesquisa para os dados referentes somente ao contato que estamos trabalhando no momento, referenciado pelo seu id, e, para dados do tipo StructuredPostal. Existem alguns outros

filtros de informaes que podem ser usados, no aqui, mas em outros casos:

ContactsContract.CommonDataKinds.Email; ContactsContract.CommonDataKinds.Event; ContactsContract.CommonDataKinds.GroupMembership; ContactsContract.CommonDataKinds.Im; ContactsContract.CommonDataKinds.Nickname; ContactsContract.CommonDataKinds.Note; ContactsContract.CommonDataKinds.Organization; ContactsContract.CommonDataKinds.Phone; ContactsContract.CommonDataKinds.Photo; ContactsContract.CommonDataKinds.Relation; ContactsContract.CommonDataKinds.StructuredName. Na linha 20 verificamos se foi achado algum dado na busca anterior.

Nas linhas 21 e 23 buscamos a rua e a cidade do contato. Na linha 24 testamos se o contato possui uma rua e uma cidade cadastrados, caso afirmativo, adicionamos seu nome e ser endereo nos seus respectivos vetores. Nas linhas 30 e 33 fechamos os cursores abertos. E, finalmente, na linha 34 criamos o adapter e na linha 36 retornamos a instncia deste objeto. Para testar se nossa lgica est certa abra o emulador, em seguida cadastre quatro contatos, dois com endereo e dois sem. Veja a Figura 2:

Figura 2: Contatos no dispositivo.

Agora execute nosso aplicativo. Ao clicar no Spinner o usurio deve ver apenas duas opes. Veja a Figura 3:

Figura 3: Contatos no aplicativo.

Buscando a posio geogrfica do contato


O leitor j deve ter percebido que cada contato tem seu endereo, mas precisamos de sua posio geogrfica, ou seja, sua latitude e longitude.

Para mudar uma string rua paraso, 100, so paulo para sua exata latitude e longitude, vamos usar um servio do Google, onde passamos o endereo e a URL nos retorna os possveis endereos com suas posies no formato JSON. OBS: JSON (com a pronuncia djeisn), um acrnimo para "JavaScript Object Notation", um formato leve para intercmbio de dados computacionais. JSON um subconjunto da notao de objeto de JavaScript, mas seu uso no requer Javascript exclusivamente. Por exemplo: buscando a rua Bonifcio de Andrada, 147, So Paulo:
http://maps.google.com/maps/api/geocode/json?address=Rua+Bonifacio+de+Andrada %2c+147%2c+sao+paulo&sensor=false&language=ptBR&key=ABQIAAAAPGO5qsT70FiyK7ypHmC_1xRKBN0VqitlrjMQ2o6oRJmuRxLx0hQhWhzC3uH HOIbfqO7s7NW2gq7BWA

Recebemos as seguintes informaes, mostradas na Figura 4:

Figura 4: Retorno do servio.

O leitor j deve ter percebido que teremos que fazer uma conexo http para este servio do Google. Antes de continuar deixe-me explicar. O foco deste artigo no conectividade, sendo assim, somente vou listar os cdigos usados aqui. Vamos criar uma classe que ser de vital importncia, a HttpClient. Esta, por sua vez, far toda a conexo com o servio e o parser do JSON. Veja a Listagem 4, mas no se assuste:

Listagem 4: public class HttpClient{ public static Vector<Hashtable> enderecos; private static void parseGoogleMapsData(String rawData) throws IOException { enderecos = new Vector<Hashtable>(); if (rawData == null) { throw new IOException("Nenhum resultado"); } try { rawData = new String(rawData.getBytes(), "utf-8"); JSONObject obj = new JSONObject(rawData); String status = obj.getString("status"); if (!"OK".equals(status) || "ZERO_RESULTS".equals(status)){ return; } JSONArray results = obj.getJSONArray("results"); for (int i = 0; i < results.length(); i++) { JSONObject localObject = results.getJSONObject(i); 01: String>(); Hashtable<String, String> endereco = new Hashtable<String,

02: endereco.put("endereco", localObject.getString("formatted_address")); 03: 04: JSONObject geometry = localObject.getJSONObject("geometry"); 05: JSONObject location = geometry.getJSONObject("location"); 06: endereco.put("lat", ""+location.getDouble("lat")); 07: endereco.put("lng", ""+location.getDouble("lng")); 08: 09: enderecos.add(endereco); } } catch (JSONException e) { throw new IOException(e.getMessage()); }

public static void executeMethod(String urlString) throws IOException{ URL url = new URL(urlString); URLConnection conn = url.openConnection(); int response; StringBuffer sb = new StringBuffer(); try{ HttpURLConnection httpConn = (HttpURLConnection) conn; httpConn.setAllowUserInteraction(false); httpConn.setInstanceFollowRedirects(true); httpConn.setRequestMethod("GET"); httpConn.connect();

response = httpConn.getResponseCode(); InputStreamReader is=new InputStreamReader(conn.getInputStream()); int responseCode = httpConn.getResponseCode(); if (response == HttpURLConnection.HTTP_OK) { int xmlchar; while ((xmlchar = is.read()) != -1) { sb.append((char)xmlchar); } } parseGoogleMapsData(sb.toString()); } catch (Exception ex) { throw new IOException(ex.toString()); } } }

Fique a vontade para estudar este cdigo, porm, para agora, o importante que tenham em mente estas linhas de cdigo:
01: String>(); Hashtable<String, String> endereco = new Hashtable<String,

02: endereco.put("endereco", localObject.getString("formatted_address")); 03: 04: JSONObject geometry = localObject.getJSONObject("geometry"); 05: JSONObject location = geometry.getJSONObject("location"); 06: endereco.put("lat", ""+location.getDouble("lat")); 07: endereco.put("lng", ""+location.getDouble("lng")); 08: 09: enderecos.add(endereco);

Perceba que criamos um Vector enderecos. Este vai armazenar uma instncia de Hashtable para cada endereo. O Hashtable, por sua vez, armazena a informaes de endereo, latitude e longitude no formato de par chave-valor. Pronto, esse o ponto chave, essencial. Voltando para a tela principal, precisamos implementar o listener que ouvir as aes de clique no boto. Veja a Listagem 5:
Listagem 5: Button botao = (Button) findViewById(R.id.btnGo); botao.setOnClickListener(new View.OnClickListener(){ public void onClick(View v) { buscarMapa(); }

});

No onClick do Button apenas chamamos o mtodo BuscarMapa, que mostrado na Listagem 6:


Listagem 6: 01:public void buscarMapa(){ 02: try { 03: String localizacao = URLEncoder.encode( enderecos.elementAt(sp.getSelectedItemPosition()).toString(), "utf-8"); 04: 05: HttpClient.executeMethod("http://maps.google.com/maps/api/ geocode/json?address="+localizacao+"&sensor=false&language=ptBR&key=ABQIAAAAPGO5qsT70FiyK7ypHmC_1xRKBN0VqitlrjMQ2o6oRJmuRxLx0hQhWhz C3uHHOIbfqO7s7NW2gq7BWA"); 06: 07; if (HttpClient.enderecos.size() > 1){ 08: Intent intent = new Intent(this, Enderecos.class); 09: startActivity(intent); 10: } 11: } catch (IOException e) { 12: e.printStackTrace(); 13: } 14:}

Na 3 utlizamos a classe URLEncoder para transformar o endereo que est informado no contato para um formato aceitvel para URL. Na linha 5 chamamos o mtodo de HttpClient que efetivamente busca os dados, faz o parser das informaes recebidas e, guarde o resultado no vetor com instncias de Hashtable. Na linha 7 verificamos se foram encontradas mais de uma opo de endereo. Isso porque, o endereo que o contato forneceu pode ser muito ambguo. Algo como: avenida paulista, So Paulo. Caso afirmativo, mostramos a tela de endereos. Veja a Figura 4:

Figura 4: Possveis endereos do contato.

A parte de seleo de uma das opes e visualizao do mapa 2D ser o prximo passo. Bem, vamos ver o xml e a classe Activity desta nova tela. A Listagem 7 mostra o endereos.xml, responsvel pela interface apresentada na Figura 4:
Listagem 7: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/android:list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>

Um simples ListView. Agora veja a Listagem 8 com a ListActivity que utiliza o xml visto anteriormente:
Listagem 8 public class Enderecos extends ListActivity{ String[] nomeRuas; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.enderecos); 01: Vector enderecos = HttpClient.enderecos; 02: nomeRuas = new String[enderecos.size()]; 03; for (int i = 0; i < enderecos.size(); i++){ 04: Hashtable<String, String> endereco = (Hashtable<String, String>) enderecos.elementAt(i); 05: nomeRuas[i] = endereco.get("endereco"); 06: } 07: 08: setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, nomeRuas)); } }

O que importante aqui so as linhas numeradas de 1 a 8. A linha nmero 1 recupera o Vector de endereos da classe HttpClient. A linha dois instancia o vetor de String que contm somente o nome dos logradouros. O lao da linha 3 recupera o endereo de cada Hashtable, na linha 4 e 5. Por fim, na linha 8, associamos o adapter ao ListView.

Mostrar Mapa
O prximo passo mostrar um mapa 2D ilustrando a localizao de um dos contatos. O primeiro passo criar uma nova tela que ser chamada pelo nosso aplicativo. Primeiramente vamos estudar o arquivo localizao.xml mostrado na Listagem 9
Listagem 9: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView android:id="@+id/mapView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:enabled="true" android:clickable="true" android:apiKey="xxxxxxxxxxxx" </RelativeLayout>

Como gerenciador de layout usamos a classe RelativeLayout. Posteriormente, veremos que ele ser muito til. Usamos um nico View, a classe MapView. Este componente apresenta um mapa na tela. Suas propriedades so bem conhecidas do leitor. Apenas atente para o valor true em enabled e clickable, com isso, o mapa pode ser clicado pelo usurio. Muito importante a propriedade apiKey, esta chave pode ser encontrada no site do google e ela que permite a utilizao do mapa 2D do Google. Depois de visto o layout xml, vamos estudar a classe Localizacao que utiliza este arquivo. Veja a Listagem 10:
Listagem 10: 1:public class Localizacao extends MapActivity 2:{ 3: MapView mapView; 4: MapController mc; 5: GeoPoint p; 6: 7: public void onCreate(Bundle savedInstanceState) { 8: super.onCreate(savedInstanceState); 9: setContentView(R.layout.localizacao); 10: 11: mapView = (MapView) findViewById(R.id.mapView); 12: 13: mc = mapView.getController(); 14: 15: String coordinates[] = {ConstLocalizacao.latitude, ConstLocalizacao.longitude}; 16: 17: double lat = Double.parseDouble(coordinates[0]); 18: double lng = Double.parseDouble(coordinates[1]); 19: 20: p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); 21: 22: mc.animateTo(p); 23: mc.setZoom(15); 24: 25: mapView.invalidate(); 26:} 27:@Override 28:protected boolean isRouteDisplayed() { 29: return false; 30:} 31:}

Na linha 1 j percebemos uma grande mudana. No utilizamos Activity, mas sim a classe MapActivity. Na linha 7 sobrescrevemos o mtodo onCreate, as linhas 8 e 9 tambm so muito conhecidas em qualquer aplicativo Android. Na linha 11 recuperamos o objeto MapView definido no xml. Na linha 13 usamos o

mtodo getController() para instanciar um objeto MapController. Esta classes, por sua vez, controla aspectos do mapa, como zoom por exemplo. Na linha 15 criamos um vetor de Strings com as constantes da classe ConstLocalizacao. Veja esta classe na Listagem 11. Nas linhas 17 e 18 transformamos os valores de String para double. Na linha 20 criamos uma instncia de GeoPoint, usando os dois valores em ponto flutuante citados anteriormente. Esta classe imutvel e representa um par de latitude e longitude. Lembram do MapController? Com ele podemos controlar o ponto central do mapa, para isso usamos seu mtodo animeTo(GeoPoint p) (linha 22). Tambm podemos chamar o mtodo setZoom paa definir a profundidade do mapa 2D. Finalmente, na linha 25, foramos uma atualizao da View tornando ela invlida.
Listagem 11: public class ConstLocalizacao { public static String latitude; public static String longitude; }

Volte na Listagem 6, perceba que s mostramos os possveis endereos (Figura 4) se o servio do Google retornar mais de um endereo, caso contrrio j possumos a localizao geogrfica (latitude e longitude). Vamos comear deste ponto. Ao o executar o exemplo, selecionei o contato Sheila Lima, que possui um endereo completo, sendo assim, o servio do Google j traz somente uma opo. Novamente, volte a Listagem 6, nas linhas 7 at a 10 temos somente o teste lgico if. Agora nosso cdigo ficou assim:
if (HttpClient.enderecos.size() > 1){ Intent intent = new Intent(this, Enderecos.class); startActivity(intent); } else { 1: Hashtable hash = (Hashtable) HttpClient.enderecos.elementAt(0); 2: 3: ConstLocalizacao.latitude = hash.get("lat").toString(); 4: ConstLocalizacao.longitude = hash.get("lng").toString(); 5: 6: startActivity(new Intent(this, Localizacao.class)); }

Como teremos s um endereo, podemos capturar o Hashtable que se encontra no ndice 0 (linha 1). Nas linhas 3 e 4 configuramos as constantes da classe ConstLocalizacao. Por fim, a linha 6 cria a inicia a Intent da classe Localizacao. Na aplicao, verei a tela da Figura 5 para o contato Sheila Lima:

Figura 5: Localizao do contato Sheila Lima.

E no segundo caso? Onde o contato no possui um endereo muito claro e a tela de possveis endereos mostrada (Figura 4). Na classe Enderecos vamos adicionar o mtodo que responde as aes de clique:
public void onListItemClick(ListView parent, View v, int position, long id) { Hashtable hash = (Hashtable) HttpClient.enderecos.elementAt(position); ConstLocalizacao.latitude = hash.get("lat").toString(); ConstLocalizacao.longitude = hash.get("lng").toString(); } startActivity(new Intent(this, Localizacao.class));

A nica diferena na primeira linha do mtodo. Anteriormente acessvamos o Hashtable diretamente do ndice 0 (zero), agora, teremos que buscar o elemento referente a posio que o usurio escolheu na lista. Vamos pegar agora o caso do segundo contato, o ricardo ogliari, ao clicarmos no boto Ver a tela de possveis endereos mostrada (Figura 4). Ao selecionar o primeiro item da lista, o endereo Avenida Paulista, 900 Bela Vista, So Paulo, 01310-100, Brasil, veremos a imagem da Figura 6:

Figura 6: Localizao do contato ricardo ogliari.

Melhorando o Mapa
Bem, digamos que atingimos nosso objetivo de ver onde nosso contato est. Porm, se o usurio quiser dar zoom in e zoom ou neste mapa? Ou ainda, poderamos colocar um cone que identifica o local exato do endereo de nossos amigos, o mapa atual abrange um bairro inteiro. Sendo assim, vamos adicionar algumas features na classe Localizacao. Primeiramente, altere o localizao.xml, adicione uma ViewGroup no RelativeLayout. Veja a Listagem 12:

Listagem 12: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView ... /> <LinearLayout android:id="@+id/zoom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> </RelativeLayout>

O LinearLayout adicionado nessa verso, ser o container dos controles de zoom. Perceba que tanto sua altura quanto sua largura ser referente aos seus prprios limites. Como utilizamos o RelativeLayout como gerenciador de layout, colocamos este ltimo componente abaixo do anterior (alignParentBottom), alm de adicionar ele ao centro (centerHorizontal). Agora as alteraes sero na classe Localizacao. Veja na Listagem 13 as linhas de cdigo que foram adicinadas:
Listagem 13: mapView = (MapView) findViewById(R.id.mapView); 1: LinearLayout zoomLayout = (LinearLayout)findViewById(R.id.zoom); 2: View zoomView = mapView.getZoomControls(); 3: 4: zoomLayout.addView(zoomView, new LinearLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 5: mapView.displayZoomControls(true); 6: mc = mapView.getController();

As linhas no numeradas j existiam na verso anterior do cdigo fonte. Na primeira linha recuperamos o objeto LinearLayout. Na linha 2 usamos o mtodo getZoomControls() da classe MapView. Este mtodo retorna um Widget que controla o nvel de zoom do mapa. A instncia criada adiciona ao LinearLayout, na linha 4. Na linha 5 definimos que ao mostrar o mapa, este widget ser visualizada por alguns segundos, depois disso, o mesmo pode retornar quando o mapa clicado. Agora, veja as figuras 7 e 8:

Figura 7: Mapa com o MapController.

Figura 8: Mapa com ZoomIn.

Na Figura 7 eu cliquei no mapa e o MapController foi ativado. Ento, apliquei o zoom in algumas vezes e cheguei na Figura 8. O MapView tambm possui dois mtodos muito teis, o setSatellite(boolean b) e o setStreetView(boolean b). Como o leitor deve imaginar, com ele, podemos mudar o modo de visualizao do mapa. Vamos fazer isso agora. Na classe Localizao, adicione o cdigo da Listagem 14:
Listagem 14: 1: private void createMenu(android.view.Menu menu) { 2: MenuItem mnu1 = menu.add(0, 0, 0, "Modo Satlite"); 3: { 4: mnu1.setAlphabeticShortcut('s'); 5: } 6: MenuItem mnu2 = menu.add(0, 1, 1, "Modo Rua"); 7: { 8: mnu2.setAlphabeticShortcut('r'); 9: } 10: } 11: 12: private boolean MenuChoice(MenuItem item) { 13: switch (item.getItemId()) { 14: case 0: 15: mapView.setSatellite(true); 16: mapView.setStreetView(false); 17: return true;

18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:

case 1: mapView.setSatellite(false); mapView.setStreetView(true); return true; } return false;

@Override public boolean onCreateOptionsMenu(android.view.Menu menu) { super.onCreateOptionsMenu(menu); createMenu(menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { return MenuChoice(item); }

Vamos sobrescrever o mtodo onCreateOptionMenu (linha 27) para criar um comportamento otimizado em resposta a ao da chamada ao menu principal. Na linha 28 chamamos o mtodo da prpria classe Activity, logo aps, chamamos o mtodo createMenu(), que tem seu corpo definido na linha 1. Neste mtodo adicionamos dois novos MenuItem. Um deles para modo satlite e outro para modo rua. Como nosso objetivo aqui no aprender interfaces grficas, no vamos entrar em detalhes da criao de menus em Android. Tambm sobrescrevemos o mtodo onOptionsItemSeleceted para responder as interaes nos itens de menu. Na linha 35 repassamos esta tarefa para o mtodo criado na linha 12. Caso o id do item de menu for igual a 0 (teste da linha 14), significa que a opo selecionada foi modo satlite, sendo assim, habilitamos o modo satlite (linha 15) e desabilitamos o modo rua (linha 16). O teste realizado na linha 18 apenas aplica a lgica inversa. Vamos executar novamente o aplicativo. Veja as Figuras 9 e 10.

Figura 9: Mapa com o MapController.

Figura 10: Mapa com o MapController.

Na Figura 9 cliquei no boto Menu do aparelho, recebendo o menu. Optei pela opo Rua, visto que, por padro, o mapa est no modo satlite. O resultado pode ser visto na Figura 10. Para deixarmos nosso mapa perfeito falta apenas uma questo. Ainda no colocamos um identificador que defina exatamente a localizao do contato. Algo como a Figura 11:

Figura 11: Identificador de posicionamento.

Para isso vamos usar uma classe abstrata chamada Overlay, que simplesmente um ponto que pode ser coloco no topo do mapa. Formando a aparncia visual vista na Figura 11. Logicamente, o primeiro passo ser criar uma classe que extenda de Overlay. Veja a Listagem 15:

Listagem 15: 1: class BolaOverlay extends Overlay{ 2: private int imageId; 3: private Paint paint = new Paint(); 4: private GeoPoint geoPoint; 5: private Point p; 6: 7: public BolaOverlay (GeoPoint geoPoint, int imageId){ 8: this.geoPoint = geoPoint; 9: this.imageId = imageId; 10: } 11: 12: @Override 13: public void draw(Canvas canvas, MapView mapView, boolean shadow) { 14: super.draw(canvas, mapView, shadow); 15: 16: if (geoPoint != null){ 17: p = mapView.getProjection().toPixels(geoPoint, null); 18: Bitmap bitmap = BitmapFactory.decodeResource( mapView.getResources(), this.imageId); 19: RectF r = new RectF(p.x-bitmap.getWidth()/2, p.ybitmap.getHeight(), p.x+bitmap.getWidth()/2, p.y); 20: canvas.drawBitmap(bitmap, null, r, paint); 21: } 22: } 23:}

O nico mtodo que deve ser sobrescrito de forma obrigatrio o draw. Nele, vamos desenhar oque queremos mostrar como overlay do mapa. No nosso caso apenas desenhamos uma imagem (linha 20) que recebida por parmetro (linha 9). Tambm por parmetro, recebido uma instncia de GeoPoint (linha 10) para definir o local da imagem. A linha 19 tambm importante, perceba que feito um clculo com o GeoPoint e o tamanho da tela e da imagem. Vamos a ltima alterao. No final do mtodo onCreate da classe Localizao colocamos o seguinte cdigo:
... BolaOverlay bola = new BolaOverlay(p, R.drawable.ponto); mapView.getOverlays().add(bola); } ... mapView.invalidate();

Primeiro cria-se a instncia de BolaOverlay, passando como parmetro a posio e a imagem. O ponto est na pasta drawable do projeto. Em seguida, atravs do mtodo getOverlays(), recuperamos a lista

de Overlays do mapa. Para, finalmente, adicionar a instncia de BolaOverlay a esta listagem. Agora a aplicao mostra o seguinte mapa para o contato ricardo ogliari e depois de alguns zoom in.

Figura 12: Identificador de posicionamento para o contato ricardo ogliari.

Concluso
O uso de sistemas baseados em localizao est se tornando quase uma commodity no mundo da tecnologia da informao. Isso se deve a muitos fatores, como por exemplo: o aumento do uso de smartphones, que apresentam na sua grande maioria um sistema de posicionamento global nativo, tambm, a cultura das pessoas vem mudando e o compartilhamento das suas posies geogrficas j no um impeditivo tecnolgico e cultural. Seguindo na mesma linha de crescimento e importncia, o sistema operacional vem ganhando espao e j figura entre os trs principais do mundo, porm, as previses que at final de 2010 fique atrs somente do Symbian.

Portanto, temos a unio de LBS e Android, que foi tratada na primeira parte deste artigo, alm de dar uma rpida olhada em como importar os contatos do smatphone para seu aplicativo. Aguardem a prxima parte, onde adicionaremos a rota entre o usurio e seu contato.

Sobre Mim
Meu nome Ricardo da Silva Ogliari, sou graduado em Cincia da Computao pela Universidade de Passo Fundo. Atualmente tambm estou cursando uma ps-graduao em web, estratgias de inovao e tecnologia, no Senac SP. Trabalho com mobile a 6 anos, escrevo artigos para algumas revistas nacionais especializadas. Sou criador e mantenedor do http://www.mobilidadetudo.com e sou um dos membros do www.javamovel.com. Palestrei em eventos nacionais e internacionais, como o FISL e o JustJava, alm de ter dezenas de artigos meus espalhados pelo mundo da internet.