Programação para a Plataforma Android – Aula 14 Mapas • Como obter um cer<ficado para usar a API de mapas do google? • Como importar as bibliotecas necessárias à manipulação de mapas? • Como exibir um mapa no aparelho móvel? • Como ler as coordenadas geográficas de um ponto no mapa? • Como obter um endereço a par<r de um ponto no mapa? • Quais são os eventos mais comuns que ocorrem sobre mapas? Obtendo uma licença • É necessário um cer<ficado digital para usar a API de mapas do google. • Para obter um cer<ficado, forneça ao servidor de auten<cação a sua chave digital. • Para gerar a chave digital: $ keytool ‐list ‐keystore ~/.android/debug.keystore Cer<ficate fingerprint (MD5): 94:1E:43:49:87:73:BB:E6:A6:88:D7:20:F1:8E:B5:98 • Use o password anônimo (vazio). Obtendo uma licença • De posse da chave digital, basta informá‐la ao servidor de auten<cação: – heps://developers.google.com/maps/ documenta<on/android/v1/maps‐api‐signup – É preciso uma conta no google. Obtendo uma licença A API de Mapas v2 • Existe uma nova versão da biblioteca de mapas, que permite a renderização de mapas 3D. • Essa nova versão chama‐se Google Maps Android v2. • Neste curso estaremos vendo Google Maps Android v1. Usando as APIs do Google • Mapas não são parte da biblioteca Android padrão. • Para usá‐los, é preciso montar o projeto com a API do Google. • E é preciso indicar o caminho para as classes apropriadas no manifesto: <uses-library android:name="com.google.android.maps" /> Usando a biblioteca de mapas <?xml version="1.0" encoding="um‐8"?> <manifest xmlns:android="hep://schemas.android.com/apk/res/android" package="com.aula14" android:versionCode="1" android:versionName="1.0"> <uses‐permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses‐permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses‐permission android:name="android.permission.INTERNET" /> <applica<on android:icon="@drawable/icon" android:label="@string/app_name"> <ac<vity android:name=".MyMapAc<vity" android:label="@string/app_name"> <intent‐filter> <ac<on android:name="android.intent.ac<on.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent‐filter> </ac<vity> <uses‐library android:name="com.google.android.maps" /> </applica<on> <uses‐sdk android:minSdkVersion="2" /> </manifest> AndroidManifest.xml Criando um layout para mapas <?xml version="1.0" encoding="um‐8"?> <LinearLayout xmlns:android="hep://schemas.android.com/apk/res/android" android:id="@+id/frame" android:orienta<on="ver<cal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView android:id="@+id/map" android:apiKey="0mryOfB7U‐H2GtZ_mxHxG3cXwnkwu0FMW60611g" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" /> </LinearLayout> a é a chave main.xml Ess s que obtemo E essa é a a r u t a via assin biblioteca que . digital contém a API de mapas. MapAc<vity public class MapsAc<vity extends MapAc<vity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } protected boolean isRouteDisplayed() { return false; } } MapsAc<vity.java mos a s u e u q r o P o layout relativo? Zooming <?xml version="1.0" encoding="um‐8"?> <Rela?veLayout xmlns:android="hep://schemas.android.com/apk/res/android" android:id="@+id/frame" android:orienta<on="ver<cal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView android:id="@+id/map" android:apiKey="0mryOfB7U‐H2GtZ_mxHxG3cXwnkwu0FMW60611g" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" /> <LinearLayout android:id="@+id/zoom" android:layout_width="wrap_content" so android:layout_height="wrap_content" Mas é preci android:layout_alignParentBoFom="true" também android:layout_centerHorizontal="true" atualizar a /> ividade. t a </Rela<veLayout> map.xml Modificando a A<vidade De onde public void onCreate(Bundle savedInstanceState) { vem essa super.onCreate(savedInstanceState); constante? setContentView(R.layout.main); mapView = (MapView) findViewById(R.id.mapView); LinearLayout zoomLayout = (LinearLayout)findViewById(R.id.zoom); View zoomView = mapView.getZoomControls(); zoomLayout.addView(zoomView, new LinearLayout.LayoutParams( Podemos LayoutParams.WRAP_CONTENT, amar o r g o r p LayoutParams.WRAP_CONTENT) m via o o z ); ventos. e mapView.displayZoomControls(true); } MapsAc<vity.java Controle de Zoom • Basta colocar o foco sobre a tela de mapa, que os controladores de zoom aparecem. Modificando a A<vidade De onde public void onCreate(Bundle savedInstanceState) { vem essa super.onCreate(savedInstanceState); constante? setContentView(R.layout.main); mapView = (MapView) findViewById(R.id.mapView); LinearLayout zoomLayout = (LinearLayout)findViewById(R.id.zoom); View zoomView = mapView.getZoomControls(); zoomLayout.addView(zoomView, new LinearLayout.LayoutParams( Podemos LayoutParams.WRAP_CONTENT, amar o r g o r p LayoutParams.WRAP_CONTENT) m via o o z ); ventos. e mapView.displayZoomControls(true); } MapsAc<vity.java Controles de Zoom • Podemos usar o layout an<go, e programar o zoom em Java: <?xml version="1.0" encoding="um‐8"?> <LinearLayout xmlns:android="hep://schemas.android.com/apk/res/android" android:id="@+id/frame" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orienta<on="ver<cal" > <com.google.android.maps.MapView android:id="@+id/map" android:layout_width="fill_parent" android:layout_height="fill_parent" android:apiKey="0mryOfB7U‐H2GtZ_mxHxG3cXwnkwu0FMW60611g" android:clickable="true" /> </LinearLayout> Controles de Zoom public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac<vity_main); mapView = (MapView) findViewById(R.id.map); mapView.setBuiltInZoomControls(true); } É possível s posicionar o controles em o es d outras part mapa. Posicionando os Controles public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ac<vity_main); mapView = (MapView) findViewById(R.id.map); mapView.setBuiltInZoomControls(true); ZoomBueonsController zbc = mapView.getZoomBueonsController(); ViewGroup container = zbc.getContainer(); for (int i = 0; i < container.getChildCount(); i++) { View child = container.getChildAt(i); if (child instanceof ZoomControls) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); lp.gravity = Gravity.RIGHT | Gravity.BOTTOM; child.requestLayout(); break; } } } Zoom via Eventos MapsAc<vity.java public boolean onKeyDown(int keyCode, KeyEvent event) { MapController mc = mapView.getController(); switch (keyCode) { case KeyEvent.KEYCODE_3: mc.zoomIn(); break; case KeyEvent.KEYCODE_1: mc.zoomOut(); break; } return super.onKeyDown(keyCode, event); } Modos de Mapa • Existem diferentes formas de visualização de mapas: que faz cada – mapView.setSatellite(true) O uma dessas chamadas? – mapView.setStreetView(true) Como poderíamos programá-las via eventos? MapsAc<vity.java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { MapController mc = mapView.getController(); switch (keyCode) { o case KeyEvent.KEYCODE_1: Será que nã olocar c poderíamos mc.zoomOut(); opções em s a s s e break; a de ma caixinh u case KeyEvent.KEYCODE_2: diálogo? mc.zoomIn(); break; case KeyEvent.KEYCODE_3: mapView.setSatellite(true); break; case KeyEvent.KEYCODE_4: mapView.setSatellite(false); break; } return super.onKeyDown(keyCode, event); } Caixa de Opções MapsAc<vity.java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { MapController mc = mapView.getController(); Insira um menu de switch (keyCode) { opções no mapa. case KeyEvent.KEYCODE_0: Apertando a tecla "0" esse menu deverá showOp?onsDialog(); ser aberto. break; case KeyEvent.KEYCODE_1: mc.zoomOut(); a Usamos um break; caixa de case KeyEvent.KEYCODE_2: diálogo no mc.zoomIn(); Sudoku. break; … } MapsAc<vity.java private void showOp<onsDialog() { String op?ons[] = { "0: show op?ons", "1: zoom out", "2: zoom in", "3: satellite on", "4: satellite off"}; new AlertDialog.Builder(this).setTitle("Key Op<ons").setItems(op?ons, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { MapController mc = mapView.getController(); switch (i) { A fórmula case 1: não muda mc.zoomOut(); break; muito. case 2: Precisamo mc.zoomIn(); break; s d e um case 3: arranjo de mapView.setSatellite(true); break; opções. case 4: mapView.setSatellite(false); break; } E de um dialoginterface.cancel(); escutador de } eventos de }).show(); clique. } MapsAc<vity.java private void showOp<onsDialog() { String op<ons[] = { "0: show op<ons", "1: zoom out", "2: zoom in", "3: satellite on", "4: satellite off"}; new AlertDialog.Builder(this).setTitle("Key Op?ons").setItems(op<ons, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { MapController mc = mapView.getController(); Interface switch (i) { fluente: case 1: cadeias de mc.zoomOut(); break; chamadas. case 2: mc.zoomIn(); break; case 3: mapView.setSatellite(true); break; case 4: mapView.setSatellite(false); break; } dialoginterface.cancel(); } }).show(); } Indicando um ponto geodésico • Por padrão, Android inicializa o mapa tendo os Estados Unidos como centro. • Pode‐se informar novas coordenadas para o programa, usando a função animateTo. Quais são as s coordenada de Belo Horizonte? Indicando um ponto geodésico • Por padrão, Android inicializa o mapa tendo os Estados Unidos como centro. • Pode‐se informar novas coordenadas para o programa, usando a função animateTo. Quais são as s coordenada de Belo Horizonte? MapController mc = mapView.getController(); String coordinates[] = {"‐19.919133", "‐43.938667"}; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); GeoPoint p = new GeoPoint( (int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); mapView.invalidate(); MapsAc<vity.java Indicando um ponto geodésico • Por padrão, Android inicializa o mapa tendo os Estados Unidos como centro. • Pode‐se informar novas coordenadas para o programa, usando a função animateTo. String coordinates[] = {"‐19.919133", "‐43.938667"}; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); GeoPoint p = new GeoPoint( (int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); mapView.invalidate(); Desenhando bitmaps • Pode‐se adicionar marcadores sobre o mapa. • Isso é feito via Overlays. Estes são objetos que podem ser desenhados sobre um mapa. Mas onde deixamos nossos marcadores mesmo? Desenhando bitmaps • Pode‐se adicionar marcadores sobre o mapa. • Isso é feito via Overlays. Estes são objetos que podem ser desenhados sobre um mapa. Crie o evento tecla 5, para mostrar um marcador em BH. Overlays p é um GeoPoint, declaradado globalmente. class MapOverlay extends com.google.android.maps.Overlay { @Override public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) { super.draw(canvas, mapView, shadow); Point screenPts = new Point(); mapView.getProjec<on().toPixels(p, screenPts); Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.push_pin3); canvas.drawBitmap(bmp, screenPts.x, screenPts.y ‐ 50, null); return true; } } MapsAc<vity.java Adicionando um Overlay ao Mapa case KeyEvent.KEYCODE_5: String coordinates[] = { "‐19.919133", "‐43.938667" }; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); MapOverlay mapOverlay = new MapOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.clear(); listOfOverlays.add(mapOverlay); mapView.invalidate(); break; MapsAc<vity.java Escala… oops! bem Não era isso que s de o m a í r a t gos fazer. Existem muitas formas de desenhar bitmaps. Podemos usar matrizes. Matrizes MapsAc<vity.java public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) { super.draw(canvas, mapView, shadow); Point screenPts = new Point(); mapView.getProjec<on().toPixels(p, screenPts); Bitmap bmp = BitmapFactory.decodeResource( getResources(), R.drawable.push_pin3); Matrix matrix = new Matrix(); matrix.postScale(0.3F, 0.3F); matrix.postTranslate(180.0F, 260.0F); canvas.drawBitmap(bmp, matrix, null); return true; } Vários Overlays • Existe uma cadeia de overlays associada a um mapa. • Podemos inserir novos elementos a essa cadeia, para adicionar mais funcionalidades ao nosso mapa. Vários Overlays MapsAc<vity.java case KeyEvent.KEYCODE_5: String coordinates[] = { "‐19.919133", "‐43.938667" }; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); MapOverlay mapOverlay = new MapOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.clear(); Quantos listOfOverlays.add(mapOverlay); overlays mapView.invalidate(); temos, nesse break; ponto? Como Remover o Overlay? case KeyEvent.KEYCODE_6: { List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.clear(); mapView.invalidate(); break; } Eventos de Toque • Podemos adicionar mais um overlay ao nosso mapa, para sermos capazes de capturar eventos de toque. m Seria bo as r a r t s o m as d a n e d r coo . do ponto Como obter essas coordenadas? Como m ostrar essas coorden adas? MapsAc<vity.java Eventos de Toque class OnTouchOverlay extends com.google.android.maps.Overlay { @Override public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { if (event.getAc?on() == Mo?onEvent.ACTION_UP) { GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); Toast.makeText(getBaseContext(), p.getLa?tudeE6() / 1E6 + "," + p.getLongitudeE6() / 1E6, Toast.LENGTH_SHORT).show(); } return false; E esse código , O que esse ta? o que ele faz? } código deno } Adicionando o novo Overlay @Override Mas, atenção! public void onCreate(Bundle savedInstanceState) { O Evento 5 es tá super.onCreate(savedInstanceState); removendo o setContentView(R.layout.map); overlay!!! mapView = (MapView) findViewById(R.id.map); LinearLayout zoomLayout = (LinearLayout) findViewById(R.id.zoom); View zoomView = mapView.getZoomControls(); zoomLayout.addView(zoomView, new LinearLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); mapView.displayZoomControls(true); OnTouchOverlay ovly = new OnTouchOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.add(ovly); } MapsAc<vity.java Mantendo a lista de Overlays case KeyEvent.KEYCODE_5: String coordinates[] = { "‐19.919133", "‐43.938667" }; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); MapOverlay mapOverlay = new MapOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); // listOfOverlays.clear(); listOfOverlays.add(mapOverlay); mapView.invalidate(); break; MapsAc<vity.java Mantendo a lista de Overlays case KeyEvent.KEYCODE_5: String coordinates[] = { "‐19.919133", "‐43.938667" }; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); mc.animateTo(p); mc.setZoom(17); MapOverlay mapOverlay = new MapOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); // listOfOverlays.clear(); listOfOverlays.add(mapOverlay); mapView.invalidate(); break; Lendo Endereços • Em vez de obter as coordenadas geodésicas de um certo local, podemos obter o endereço daquele local, quando válido. public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { if (event.getAc<on() == Mo<onEvent.ACTION_UP) { GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault()); try { List<Address> addresses = geoCoder.getFromLoca<on( p.getLa<tudeE6() / 1E6, p.getLongitudeE6() / 1E6, 1); String add = ""; if (addresses.size() > 0) { for (int i = 0; i < addresses.get(0).getMaxAddressLineIndex(); i++) add += addresses.get(0).getAddressLine(i) + "\n"; } Toast.makeText(getBaseContext(), add, Toast.LENGTH_SHORT).show(); } catch (IOExcep<on e) { e.printStackTrace(); } return true; } return false; } MapsAc<vity.java public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { if (event.getAc<on() == Mo<onEvent.ACTION_UP) { GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); Modifique esse Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault()); overlay, para que ele: try { I) informe a List<Address> addresses = geoCoder.getFromLoca<on( coordenada p.getLa<tudeE6() / 1E6, p.getLongitudeE6() / 1E6, 1); quando o dedo String add = ""; if (addresses.size() > 0) { tocar a tela. for (int i = 0; i < addresses.get(0).getMaxAddressLineIndex(); i++) II) Informe o add += addresses.get(0).getAddressLine(i) + "\n"; endereço quando } o dedo deixar a Toast.makeText(getBaseContext(), add, Toast.LENGTH_SHORT).show(); tela. } catch (IOExcep<on e) { e.printStackTrace(); } return true; } return false; } public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); if (event.getAc<on() == Mo<onEvent.ACTION_UP) { Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault()); try { List<Address> addresses = geoCoder.getFromLoca<on( p.getLa<tudeE6() / 1E6, p.getLongitudeE6() / 1E6, 1); String add = ""; if (addresses.size() > 0) { for (int i = 0; i < addresses.get(0).getMaxAddressLineIndex(); i++) add += addresses.get(0).getAddressLine(i) + "\n"; } Toast.makeText(getBaseContext(), add, Toast.LENGTH_SHORT).show(); } catch (IOExcep<on e) { e.printStackTrace(); } } else if (event.getAc<on() == Mo<onEvent.ACTION_DOWN) { Toast.makeText(getBaseContext(), p.getLa<tudeE6() / 1E6 + "," + p.getLongitudeE6() / 1E6, Toast.LENGTH_SHORT).show(); } return false; } MapsAc<vity.java Onde Estou? • Mapas e localizadores são bons companheiros. Crie uma atividade de mapa que posicione a tela no ponto em que o emulador se encontra geograficamente, logo que a atividade começa. Que passos de essa ativida envolve? Onde Estou? • Mapas e localizadores são bons companheiros. Que passos de essa ativida envolve? Crie uma atividade de mapa que Precisamos posicione a tela no obter a ponto em que o localização emulador se s de o encontra m e t E mapa o r e geograficamente, v mo s o m e c e uele q a a r a logo que a atividadeCom o a p obtend nto. o p começa. alização loc Como obter a localização atual? private GeoPoint getCurrentLoca<on() { Loca<onManager mgr = (Loca<onManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); String best = mgr.getBestProvider(criteria, true); Loca<on loca<on = mgr.getLastKnownLoca<on(best); double lat; double lng; m ter o b Agora é preciso e r p m e s É if (loca?on != null) { posicionar o mapa um plano de lat = loca?on.getLa?tude(); não o s a sobre o ponto em c , p u k c ba lng = loca?on.getLongitude(); obter que estamos. Como l e v í s s o p a j se } else { or! d a z fazer isso? i l a c o l m u lat = ‐19.879133; lng = ‐43.961667; } GeoPoint point = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); return point; } WhereAreAc<vity.java Posicionando o Mapa WhereAreAc<vity.java private void moveToLoca<on(GeoPoint gp, int zoomFactor) { MapController mc = mapView.getController(); mc.animateTo(gp); mc.setZoom(zoomFactor); Jogo da memória: o mapView.invalidate(); que essa } chamada faz? Precisamos desenhar um e alfinete sobr a tela. Queremos código reúsavel. ia o r e s l a Qu jeto o r p r o melh se para es ? a problem Objetos Marcadores @Override Métodos que public void onCreate(Bundle savedInstanceState) { acabamos de criar. ... cria o mapa, habilita o zoom, etc ... GeoPoint currentLoca?on = getCurrentLoca?on(); moveToLoca?on(currentLoca?on, 17); O que objetos MarkerOverlay ovly = new MarkerOverlay(this); ovly.addCoordinate(currentLoca?on); List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.add(ovly); E como } r WhereAreAc<vity.java implementa essa classe? dessa classe sabem fazer? O que esse código está fazendo? MarkerOverlay.java Porque precisamos de um contexto? public class MarkerOverlay extends com.google.android.maps.Overlay { private List<GeoPoint> coords; private Context context; Triste estória: Java não public MarkerOverlay(Context ct) { possui herança múltipla. this.context = ct; Se possuísse, poderíamos coords = new ArrayList<GeoPoint>(); estender tanto Overlay } quanto alguma lista. public void addCoordinate(GeoPoint coord) { coords.add(coord); } @Override public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) { Como será a ... implementação } de draw? } Desenhando Marcadores @Override public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) { super.draw(canvas, mapView, shadow); for (GeoPoint gp : coords) { Point screenPts = new Point(); mapView.getProjec<on().toPixels(gp, screenPts); Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.drawable.push_pin3); Matrix matrix = new Matrix(); Onde fica o matrix.postScale(0.3F, 0.3F); nosso matrix.postTranslate(100.0F, 130.0F); marcador? canvas.drawBitmap(bmp, matrix, null); } return true; } MarkerOverlay.java AndroidManifest.xml <?xml version="1.0" encoding="u?‐8"?> <manifest xmlns:android="hEp://schemas.android.com/apk/res/android" package="com.aula14" android:versionCode="1" android:versionName="1.0"> <uses‐permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses‐permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses‐permission android:name="android.permission.INTERNET" /> <applica<on android:icon="@drawable/icon" android:label="@string/app_name"> <ac<vity android:name=".MapsAcVvity" android:label="@string/app_name"> <intent‐filter> <ac<on android:name="android.intent.acVon.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent‐filter> </ac<vity> <ac?vity android:name=".WhereAreAc3vity" android:label="@string/where_app"> <intent‐filter> <ac?on android:name="android.intent.ac3on.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent‐filter> </ac?vity> <uses‐library android:name="com.google.android.maps" /> </applica<on> <uses‐sdk android:minSdkVersion="2" /> </manifest> Agenda de Endereços Queremos construir um catálogo de endereços. Essa aplicação possui duas partes: I) Um coletor de endereços, que dá para o usuário uma interface de mapa. Clicando em pontos dessa interface o coletor abre uma caixa de diálogo em que o usuário pode entrar com informações sobre o local. II) Um visualizador de endereços. Trata-se de uma lista que exibe as informações armazenadas na agenda. O que é um endereço? Como armazenar endereços? Quantas visões teremos? Como coletar endereços? Endereços • Endereços são tuplas formadas por quatro elementos: – Um rótulo, para ajudar em sua classificação – Uma string descrevendo informações sobre o local – Um inteiro representando uma la<tude – Um inteiro representando uma longitude O que é um endereço? Como armazenar endereços? public class AddressData extends SQLiteOpenHelper { private sta<c final String DATABASE_NAME = "events.db"; private sta<c final int DATABASE_VERSION = 1; public AddressData(Context ctx) { super(ctx, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + LON + " INTEGER," Qual a melhor + LAT + " INTEGER," forma de + LABEL + " TEXT NOT NULL, " declarar as constantes? + INFO + " TEXT);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } } AddressData.java Interfaces de Constantes Constants.java public interface Constants { public sta<c final String TABLE_NAME = "events"; public sta<c final String LABEL = "label"; public sta<c final String INFO = "info"; public sta<c final String LON = "lon"; public sta<c final String LAT = "lat"; Quantas } import sta<c android.provider.BaseColumns._ID; import sta<c com.etc.etc.etc.Constants.INFO; import sta<c com.etc.etc.etc.Constants.LABEL; import sta<c com.etc.etc.etc.Constants.LAT; import sta<c com.etc.etc.etc.Constants.LON; import sta<c com.etc.etc.etc.Constants.TABLE_NAME; AddressData.java visões teremos? Como coletar endereços? Visões Para que serve cada visão? Quantas visões teremos? Como coletar endereços? A<vidade Principal public class AddressBookAc<vity extends MapAc<vity { public sta<c AddressData addrs; class OnTouchOverlay extends com.google.android.maps.Overlay { @Override public boolean onTouchEvent(Mo?onEvent event, MapView mapView) { ... Para que } servirá essa } camada? @Override public void onCreate(Bundle savedInstanceState) { ... } @Override O que o métod o protected boolean isRouteDisplayed() { onCreate faz ? return false; } } AddressBookAc<vity.java Criando a Agenda @Override public void onCreate(Bundle savedInstanceState) { if (addrs == null) { Quais são addrs = new AddressData(this); nossas coordenadas } iniciais? super.onCreate(savedInstanceState); setContentView(R.layout.ac<vity_main); mapView = (MapView) findViewById(R.id.map); mapView.setBuiltInZoomControls(true); OnTouchOverlay ovly = new OnTouchOverlay(); List<Overlay> listOfOverlays = mapView.getOverlays(); listOfOverlays.add(ovly); moveToIni?alCoordinate(); } Movendo para um ponto específico private void moveToIni<alCoordinate() { String coordinates[] = { "‐19.919133", "‐43.938667" }; double lat = Double.parseDouble(coordinates[0]); double lng = Double.parseDouble(coordinates[1]); p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6)); MapController mc = mapView.getController(); mc.animateTo(p); Quais eventos mc.setZoom(17); r ta Como cole produzem ? mapView.invalidate(); s endereço dados? } AddressBookAc<vity.java private final int addrLbl = 1; class OnTouchOverlay extends com.google.android.maps.Overlay { @Override public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { if (event.getAc<on() == Mo<onEvent.ACTION_DOWN) { Intent intent = new Intent(AddressBookAc<vity.this, GetAddrLabelAc<vity.class); GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); intent.putExtra(LAT, p.getLa?tudeE6()); O que esse intent.putExtra(LON, p.getLongitudeE6()); método faz? startAc?vityForResult(intent, addrLbl); } Que informaçõ return false; es Qual o layout e s t a m os } da outra passando à ou tra } atividade? atividade? Coleta de Endereços Como criar esse layout? getlbl.xml <?xml version="1.0" encoding="u?‐8"?> <LinearLayout xmlns:android="hEp://schemas.android.com/apk/res/android" android:orienta<on="verVcal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:minWidth="200dip" android:padding="10dip"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Label:" android:textSize="15sp" /> <EditText android:id="@+id/label_txt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:editable="true"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Notas e Observacoes:" android:textSize="15sp" /> <EditText android:id="@+id/info_txt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:editable="true"/> <Bueon android:id="@+id/confirm_info" android:text="Ok" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> Podemos usar um tema para deixar a atividade com um jeito de caixa de dialogo. <ac<vity android:name=".GetAddrLabelAc<vity" android:theme="@android:style/Theme.Dialog"> </ac<vity> E como será a atividade? Coletando o Endereço public class GetAddrLabelAc<vity extends Ac<vity { private EditText infoBox, labelBox; public final void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.getlbl); infoBox = (EditText) findViewById(R.id.info_txt); labelBox = (EditText) findViewById(R.id.label_txt); final Intent myIntent = getIntent(); Como será o // evento do botão aqui!!! tratamento } de eventos? } GetAddrLabelAc<vity.java Retornando a A<vidade Bueon confirmBueon = (Bueon) findViewById(R.id.confirm_info); confirmBueon.setOnClickListener(new View.OnClickListener() { public void onClick(final View view) { Bundle bundle = new Bundle(); O que é um String labelStr = labelBox.getText().toString(); bundle? bundle.putString(INFO, labelStr); String infoStr = infoBox.getText().toString(); bundle.putString(LABEL, infoStr); O que esse bundle.putInt(LAT, myIntent.getIntExtra(LAT, 0)); método está bundle.putInt(LON, myIntent.getIntExtra(LON, 0)); fazendo? Intent mIntent = new Intent(); mIntent.putExtras(bundle); setResult(RESULT_OK, mIntent); Quem recebe finish(); essas } informações? }); GetAddrLabelAc<vity.java onAc<vityResult AddressBookAc<vity.java protected final void onAc<vityResult(final int requestCode, final int resultCode, final Intent intent) { super.onAc<vityResult(requestCode, resultCode, intent); if (requestCode == this.addrLbl) { De onde vem Bundle extras = intent.getExtras(); esse objeto? String infoStr = extras.getString(INFO); String labelStr = extras.getString(LABEL); int lat = extras.getInt(LAT); int lon = extras.getInt(LON); Temos de entar m le p im addAddress(infoStr, labelStr, lat, lon); sse método. e } Onde essa } informação foi criada? Adicionando Eventos AddressBookAc<vity.java private void addAddress(String info, String label, int lat, int lon) { SQLiteDatabase db = addrs.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(LAT, lat); values.put(LON, lon); values.put(INFO, info); values.put(LABEL, label); db.insertOrThrow(TABLE_NAME, null, values); } AddressBookAc<vity.java class OnTouchOverlay extends com.google.android.maps.Overlay { @Override public boolean onTouchEvent(Mo<onEvent event, MapView mapView) { Por que não passamos if (event.getAc<on() == Mo<onEvent.ACTION_DOWN) { esses dados Intent intent = new Intent(AddressBookAc<vity.this, diretamente? GetAddrLabelAc<vity.class); GeoPoint p = mapView.getProjec<on().fromPixels((int) event.getX(), (int) event.getY()); intent.putExtra(LAT, p.getLa?tudeE6()); intent.putExtra(LON, p.getLongitudeE6()); AddressBookAc<vity.java startAc?vityForResult(intent, addrLbl); } protected final void onAc?vityResult(final int requestCode, return false; final int resultCode, final Intent intent) { } super.onAc<vityResult(requestCode, resultCode, intent); a } estar t if (requestCode == this.addrLbl) { o m o C Bundle extras = intent.getExtras(); ão? ç a c i l p a String infoStr = extras.getString(INFO); String labelStr = extras.getString(LABEL); int lat = extras.getInt(LAT); O que seria int lon = extras.getInt(LON); um bom addAddress(infoStr, labelStr, lat, lon); teste? } } Mostrando os Eventos AddressBookAc<vity.java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_ENTER: showEvents(getEvents()); break; } return super.onKeyDown(keyCode, event); } e esse o d n a u Q será o t n e v e do? a r a p s i d Qual a maneira mais simples de implementar esse método? qu Lembra os de em ainda t ntar e m e l p im ção. a r e p o essa Recuperando Dados Armazenados AddressBookAc<vity.java private sta<c String[] FROM = { _ID, LABEL, INFO, LAT, LON, }; private Cursor getEvents() { SQLiteDatabase db = AddressBookAc<vity.addrs.getReadableDatabase(); Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null, null, null); startManagingCursor(cursor); return cursor; } Como imprimir os dados recuperados? Mostrando os Eventos private void showEvents(Cursor cursor) { while (cursor.moveToNext()) { Log.v("show ‐ inf", cursor.getString(cursor.getColumnIndex(INFO))); Log.v("show ‐ lbl", cursor.getString(cursor.getColumnIndex(LABEL))); Log.v("show ‐ lat", cursor.getString(cursor.getColumnIndex(LAT))); Log.v("show ‐ lon", cursor.getString(cursor.getColumnIndex(LON))); } } AddressBookAc<vity.java E como exibir esses eventos em uma lista? Que evento ar m a h c a i r e v de a lista? Oops: Manifesto! <ac<vity android:name=".AddressBookAc<vity" android:label="@string/<tle_ac<vity_main" > <intent‐filter> <ac<on android:name="android.intent.ac<on.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent‐filter> </ac<vity> <ac<vity android:name=".GetAddrLabelAc<vity" android:theme="@android:style/Theme.Dialog" > </ac<vity> Adicionando mais um Evento @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_0: Intent intent = new Intent(AddressBookAc<vity.this, AddressList.class); fillIntent(intent, getEvents(), "DATA"); startAc<vity(intent); break; case KeyEvent.KEYCODE_ENTER: showEvents(getEvents()); Como passar os break; dados a serem } exibidos para a return super.onKeyDown(keyCode, event); atividade lista? } AddressBookAc<vity.java Passando Listas de Dados AddressBookAc<vity.java private void fillIntent(Intent intent, Cursor cursor, String name) { ArrayList<String> list = new ArrayList<String>(); while (cursor.moveToNext()) { String data = ""; data += cursor.getString(cursor.getColumnIndex(INFO)); data += ", " + cursor.getString(cursor.getColumnIndex(LABEL)); data += " (" + cursor.getString(cursor.getColumnIndex(LAT)); data += ", " + cursor.getString(cursor.getColumnIndex(LON)) + ")"; list.add(data); } Para que intent.putStringArrayListExtra("DATA", list); servirá essa } constante? Disparando A<vidades @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_0: Intent intent = new Intent(AddressBookAc<vity.this, AddressList.class); fillIntent(intent, getEvents(), "DATA"); Qual a diferença startAc?vity(intent); entre break; startActivity e case KeyEvent.KEYCODE_ENTER: startActivityFor showEvents(getEvents()); Result? break; } return super.onKeyDown(keyCode, event); Precisamos implementar o } AddressBookAc<vity.java visualizador. Layout da Lista Como será o layout dessa atividade? addresslist.xml Layout da Lista <?xml version="1.0" encoding="u?‐8"?> <LinearLayout xmlns:android="hEp://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!‐‐ Note built‐in ids for 'list' and 'empty' ‐‐> <ListView android:id="@android:id/list" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@android:id/empty" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/empty" /> </LinearLayout> Não esqueçam as constantes. Como será a atividade? AddressList.java ListAc<vity Quem preencheu esses dados? public class AddressList extends ListAc<vity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.addresslist); Intent intent = getIntent(); ArrayList<String> entries = intent.getStringArrayListExtra("DATA"); this.setListAdapter(new ArrayAdapter<String>(this, R.layout.row, entries)); } Precisamos } definir o layout de uma linha. AddressList.java ListAc<vity public class AddressList extends ListAc<vity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.addresslist); Intent intent = getIntent(); ArrayList<String> entries = intent.getStringArrayListExtra("DATA"); this.setListAdapter(new ArrayAdapter<String>(this, R.layout.row, entries)); } row.xml } <?xml version="1.0" encoding="UTF‐8"?> <TextView android:id="@+id/text1" xmlns:android="hep://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"/> Exercício: adicionando um Menu Modifique a agenda de endereços para que ela utilize um menu para criar a lista de endereços. Esse menu deve possuir uma opção: “Show in list”, que quando selecionada exibe a atividade AddressList. Adicionando um Menu AddressBookAc<vity.java private sta<c final int SHOW_LIST = 1; public boolean onCreateOp<onsMenu(Menu menu) { menu.add(Menu.NONE, SHOW_LIST, Menu.NONE, "Show in list"); return true; } public boolean onOp<onsItemSelected(MenuItem item) { switch (item.getItemId()) { case SHOW_LIST: Intent intent = new Intent(AddressBookAc<vity.this, AddressList.class); fillIntent(intent, getEvents(), "DATA"); startAc<vity(intent); return true; default: ; } return false; } Exercício: visualizando marcadores Modifique o menu da agenda de endereços para que ele contenha mais duas opções: I) Markers ON: coloca um marcador sobre cada endereço armazenado no banco de dados. II) Markers OFF: remove os marcadores. AddressBookAc<vity.java Uma Dica para o Menu @Override public boolean onOp<onsItemSelected(MenuItem item) { switch (item.getItemId()) { case SHOW_LIST: Intent intent = new Intent(AddressBookAc<vity.this, AddressList.class); fillIntent(intent, getEvents(), "DATA"); startAc<vity(intent); return true; case SHOW_MARKERS: MarkerOverlay ovly = new MarkerOverlay(this); setMarkers(ovly); mapView.getOverlays().add(ovly); mapView.invalidate(); return true; case HIDE_MARKERS: mapView.getOverlays().clear(); mapView.invalidate(); private void setMarkers(MarkerOverlay ovly) { return true; Cursor cursor = getEvents(); default: while (cursor.moveToNext()) { ; int lat = cursor.getInt(cursor.getColumnIndex(LAT)); int lon = cursor.getInt(cursor.getColumnIndex(LON)); } ovly.addCoordinate(new GeoPoint(lat, lon)); return false; } } }