Neste tutorial, mostramos como encontrar a localização atual de um dispositivo Android e exibir detalhes do lugar, como um ponto de interesse comercial ou de outros tipos. Siga este tutorial se você quiser criar um app para Android usando o Maps SDK for Android, o Places SDK for Android e o provedor de localização combinada nas APIs Location do Google Play Services.
Acessar o código
Clone ou faça o download do repositório de exemplos da API Google Maps Android v2 do GitHub.
Configurar seu projeto de desenvolvimento
Siga estas etapas para criar o projeto de tutorial no Android Studio.
- Faça o download do Android Studio e instale-o.
- Adicione o pacote Google Play Services ao Android Studio.
- Clone ou faça o download do repositório de exemplos da API Google Maps Android v2, caso ainda não tenha feito isso no início do tutorial.
Importe o projeto de tutorial:
- No Android Studio, selecione File > New > Import Project.
- Acesse o local onde você salvou o repositório de exemplos da API Google Maps Android v2 após fazer o download.
- Encontre o projeto CurrentPlaceDetailsOnMap neste local:
PATH-TO-SAVED-REPO/android-samples/tutorials/CurrentPlaceDetailsOnMap
- Selecione o diretório do projeto e clique em OK. O Android Studio criará seu projeto usando a ferramenta Gradle.
Receber uma chave de API e ativar as APIs necessárias
Para acompanhar este tutorial, você precisa de uma chave de API do Google que possa usar o Maps SDK for Android e o Places SDK for Android.
Clique no botão abaixo para a receber uma chave e ativar as APIs.
Primeiros passosSe você quiser mais detalhes, consulte o guia completo sobre como acessar uma chave de API.
Adicionar a chave de API ao seu app
- Edite o arquivo
gradle.properties
do projeto. Cole sua chave de API no valor da propriedade
GOOGLE_MAPS_API_KEY
.GOOGLE_MAPS_API_KEY=PASTE-YOUR-API-KEY-HERE
Quando você cria o app, o Gradle copia essa chave no manifesto do Android. O arquivo
build.gradle
do aplicativo contém a linha a seguir, que mapeia a stringgoogle_maps_key
no manifesto para a propriedadeGOOGLE_MAPS_API_KEY
do Gradle:resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
Criar e executar seu app
- Conecte um dispositivo Android ao computador. Siga as instruções se você quiser ativar as opções do desenvolvedor no seu dispositivo Android e permitir que o sistema detecte o aparelho. Também é possível usar o gerenciador de AVD para configurar um dispositivo virtual. Ao escolher um emulador, selecione uma imagem que inclua as APIs do Google. Para mais detalhes, consulte o guia de primeiros passos.
- No Android Studio, clique na opção de menu Run ou no ícone do botão de reprodução. Escolha um dispositivo quando solicitado.
O Android Studio invoca o Gradle para criar o app e, em seguida, executa o aplicativo no aparelho ou no emulador. Será exibido um mapa com vários marcadores centralizados ao redor do seu local atual, semelhante à imagem desta página.
Solução de problemas:
- Se o mapa não aparecer, confirme se você recebeu uma chave de API e a incluiu no app, como descrito acima. Verifique se há mensagens de erro sobre essa chave no registro do Android Monitor do Android Studio.
- Se o mapa exibir apenas um marcador na Ponte da Baía de Sydney (o local padrão especificado no app), verifique se você concedeu a permissão de localização ao aplicativo. O app solicita essa permissão durante o tempo de execução, de acordo com o padrão descrito no guia de permissões do Android. Também é possível definir permissões diretamente no dispositivo. Para isso, escolha Configurações > Apps > nome do aplicativo > Permissões > Localização. Se você quiser saber como lidar com permissões no seu código, consulte o guia abaixo para solicitar a permissão de localização no seu app.
- Use as ferramentas de depuração do Android Studio para ver os registros e depurar o app.
Entender o código
Nesta parte do tutorial, explicamos as partes mais importantes do app CurrentPlaceDetailsOnMap para ajudar você a criar um aplicativo semelhante.
Instanciar os clientes da API Places
As interfaces a seguir oferecem os principais pontos de entrada do Places SDK for Android:
- A
GeoDataClient
dá acesso ao banco de dados do Google, que tem informações sobre lugares e empresas. - A
PlaceDetectionClient
concede acesso rápido ao lugar atual do dispositivo e informa a localização do aparelho.
A interface LocationServices é o principal ponto de entrada para os Serviços de localização do Android.
Para usar as APIs, instancie GeoDataClient
, PlaceDetectionClient
e FusedLocationProviderClient
no método onCreate()
do seu fragmento ou da atividade, conforme a amostra de código a seguir:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Construct a GeoDataClient. mGeoDataClient = Places.getGeoDataClient(this, null); // Construct a PlaceDetectionClient. mPlaceDetectionClient = Places.getPlaceDetectionClient(this, null); // Construct a FusedLocationProviderClient. mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this); }
Solicitar permissão de localização
Seu app precisa solicitar permissão de localização para determinar o lugar do dispositivo e permitir que o usuário toque no botão Meu local do mapa.
Neste tutorial, mostramos o código necessário para solicitar a permissão de localização precisa. Se você quiser mais detalhes, consulte o guia sobre permissões do Android.
Adicione a permissão como um elemento filho de
<manifest>
no seu manifesto do Android:<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.currentplacedetailsonmap"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> </manifest>
Solicite permissões de tempo de execução no seu app. Com isso, o usuário poderá conceder ou negar o compartilhamento de localização. O código a seguir verifica se o usuário atribuiu a permissão exata. Em caso negativo, uma solicitação é exibida:
private void getLocationPermission() { /* * Request location permission, so that we can get the location of the * device. The result of the permission request is handled by a callback, * onRequestPermissionsResult. */ if (ContextCompat.checkSelfPermission(this.getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mLocationPermissionGranted = true; } else { ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); } }
Modifique o callback
onRequestPermissionsResult()
para lidar com o resultado da solicitação de permissão:@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { mLocationPermissionGranted = false; switch (requestCode) { case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { mLocationPermissionGranted = true; } } } updateLocationUI(); }
Posteriormente neste tutorial, descreveremos o método
updateLocationUI()
.
Adicionar um mapa
Exiba um mapa usando o Maps SDK for Android.
Adicione um elemento
<fragment>
ao arquivo de layout da sua atividade,activity_maps.xml
. Esse elemento define umSupportMapFragment
para atuar como contêiner do mapa e conceder acesso ao objetoGoogleMap
. Neste tutorial, usamos a versão do fragmento de mapa da Biblioteca de Suporte do Android para oferecer suporte às versões anteriores do framework Android.<fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.currentplacedetailsonmap.MapsActivityCurrentPlace" />
No método
onCreate()
da sua atividade, defina o arquivo de layout como a visualização de conteúdo:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); }
Implemente a interface
OnMapReadyCallback
e modifique o métodoonMapReady()
para configurar o mapa quando o objetoGoogleMap
estiver disponível:public void onMapReady(GoogleMap map) { mMap = map; // Do other setup activities here too, as described elsewhere in this tutorial. // Turn on the My Location layer and the related control on the map. updateLocationUI(); // Get the current location of the device and set the position of the map. getDeviceLocation(); }
No método
onCreate()
da sua atividade, gere um identificador para o fragmento de mapa chamandoFragmentManager.findFragmentById()
. Em seguida, usegetMapAsync()
para se registrar no callback do mapa:SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this);
Escreva um método
updateLocationUI()
para definir os controles de local no mapa. Se o usuário tiver concedido permissão de localização, ative a camada "My Location" e o controle relacionado. Caso contrário, desative ambos e defina o local atual como "null":private void updateLocationUI() { if (mMap == null) { return; } try { if (mLocationPermissionGranted) { mMap.setMyLocationEnabled(true); mMap.getUiSettings().setMyLocationButtonEnabled(true); } else { mMap.setMyLocationEnabled(false); mMap.getUiSettings().setMyLocationButtonEnabled(false); mLastKnownLocation = null; getLocationPermission(); } } catch (SecurityException e) { Log.e("Exception: %s", e.getMessage()); } }
Acessar o local do dispositivo Android e posicionar o mapa
Use o provedor de localização combinada para encontrar o último local conhecido do dispositivo e posicione o mapa nesse lugar. No tutorial, informamos o código que você precisa utilizar. Para mais detalhes sobre o local do dispositivo, consulte o guia sobre o provedor de localização combinada nas APIs Location do Google Play Services.
private void getDeviceLocation() { /* * Get the best and most recent location of the device, which may be null in rare * cases when a location is not available. */ try { if (mLocationPermissionGranted) { TasklocationResult = mFusedLocationProviderClient.getLastLocation(); locationResult.addOnCompleteListener(this, new OnCompleteListener () { @Override public void onComplete(@NonNull Task task) { if (task.isSuccessful()) { // Set the map's camera position to the current location of the device. mLastKnownLocation = task.getResult(); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM)); } else { Log.d(TAG, "Current location is null. Using defaults."); Log.e(TAG, "Exception: %s", task.getException()); mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM)); mMap.getUiSettings().setMyLocationButtonEnabled(false); } } }); } } catch(SecurityException e) { Log.e("Exception: %s", e.getMessage()); } }
Visualizar o lugar atual
Use o Places SDK for Android para ver uma lista de possíveis lugares atuais do dispositivo, que, nesse contexto, é uma empresa ou outro ponto de interesse.
Neste tutorial, o lugar atual é consultado quando o usuário clica no botão Get Place. Em seguida, é exibida uma lista de possíveis lugares para escolher, e um marcador é adicionado ao mapa, no local correspondente. No tutorial, fornecemos o código necessário para interagir com o Places SDK for Android. Se você quiser mais detalhes, consulte o guia sobre como visualizar o lugar atual.
- Crie um arquivo de layout (
current_place_menu.xml
) para o menu "opções" e modifique o métodoonCreateOptionsMenu()
de forma a configurar esse menu. Veja o exemplo de app complementar para o código. - Modifique o método
onOptionsItemSelected()
para acessar o lugar atual quando o usuário clicar na opção Get Place:@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.option_get_place) { showCurrentPlace(); } return true; }
Crie um método
showCurrentPlace()
para ver uma lista de possíveis lugares atuais do dispositivo:private void showCurrentPlace() { if (mMap == null) { return; } if (mLocationPermissionGranted) { // Use fields to define the data types to return. List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS, Place.Field.LAT_LNG); // Use the builder to create a FindCurrentPlaceRequest. FindCurrentPlaceRequest request = FindCurrentPlaceRequest.newInstance(placeFields); // Get the likely places - that is, the businesses and other points of interest that // are the best match for the device's current location. @SuppressWarnings("MissingPermission") final Task<FindCurrentPlaceResponse> placeResult = mPlacesClient.findCurrentPlace(request); placeResult.addOnCompleteListener (new OnCompleteListener<FindCurrentPlaceResponse>() { @Override public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) { if (task.isSuccessful() && task.getResult() != null) { FindCurrentPlaceResponse likelyPlaces = task.getResult(); // Set the count, handling cases where less than 5 entries are returned. int count; if (likelyPlaces.getPlaceLikelihoods().size() < M_MAX_ENTRIES) { count = likelyPlaces.getPlaceLikelihoods().size(); } else { count = M_MAX_ENTRIES; } int i = 0; mLikelyPlaceNames = new String[count]; mLikelyPlaceAddresses = new String[count]; mLikelyPlaceAttributions = new List[count]; mLikelyPlaceLatLngs = new LatLng[count]; for (PlaceLikelihood placeLikelihood : likelyPlaces.getPlaceLikelihoods()) { // Build a list of likely places to show the user. mLikelyPlaceNames[i] = placeLikelihood.getPlace().getName(); mLikelyPlaceAddresses[i] = placeLikelihood.getPlace().getAddress(); mLikelyPlaceAttributions[i] = placeLikelihood.getPlace() .getAttributions(); mLikelyPlaceLatLngs[i] = placeLikelihood.getPlace().getLatLng(); i++; if (i > (count - 1)) { break; } } // Show a dialog offering the user the list of likely places, and add a // marker at the selected place. MapsActivityCurrentPlace.this.openPlacesDialog(); } else { Log.e(TAG, "Exception: %s", task.getException()); } } }); } else { // The user has not granted permission. Log.i(TAG, "The user did not grant location permission."); // Add a default marker, because the user hasn't selected a place. mMap.addMarker(new MarkerOptions() .title(getString(R.string.default_info_title)) .position(mDefaultLocation) .snippet(getString(R.string.default_info_snippet))); // Prompt the user for permission. getLocationPermission(); } }
Crie um método
openPlacesDialog()
para exibir um formulário que permita ao usuário escolher um lugar em uma lista de opções. Adicione ao mapa um marcador para o lugar selecionado. O conteúdo do marcador inclui o nome e o endereço do local e todas as atribuições que a API fornece:private void openPlacesDialog() { // Ask the user to choose the place where they are now. DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // The "which" argument contains the position of the selected item. LatLng markerLatLng = mLikelyPlaceLatLngs[which]; String markerSnippet = mLikelyPlaceAddresses[which]; if (mLikelyPlaceAttributions[which] != null) { markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[which]; } // Add a marker for the selected place, with an info window // showing information about that place. mMap.addMarker(new MarkerOptions() .title(mLikelyPlaceNames[which]) .position(markerLatLng) .snippet(markerSnippet)); // Position the map's camera at the location of the marker. mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng, DEFAULT_ZOOM)); } }; // Display the dialog. AlertDialog dialog = new AlertDialog.Builder(this) .setTitle(R.string.pick_place) .setItems(mLikelyPlaceNames, listener) .show(); }
Crie um layout personalizado para o conteúdo da janela de informações. Isso permite a exibição de várias linhas de texto nessa janela. Primeiro, adicione um arquivo de layout XML,
custom_info_contents.xml
, contendo uma visualização de texto para o título da janela e outra para o snippet (isto é, o conteúdo de texto da janela):<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layoutDirection="locale" android:orientation="vertical"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textColor="#ff000000" android:textStyle="bold" /> <TextView android:id="@+id/snippet" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#ff7f7f7f" /> </LinearLayout>
Implemente a interface
InfoWindowAdapter
para preencher o layout e carregar o conteúdo da janela de informações:@Override public void onMapReady(GoogleMap map) { // Do other setup activities here too, as described elsewhere in this tutorial. mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() { @Override // Return null here, so that getInfoContents() is called next. public View getInfoWindow(Marker arg0) { return null; } @Override public View getInfoContents(Marker marker) { // Inflate the layouts for the info window, title and snippet. View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_contents, null); TextView title = ((TextView) infoWindow.findViewById(R.id.title)); title.setText(marker.getTitle()); TextView snippet = ((TextView) infoWindow.findViewById(R.id.snippet)); snippet.setText(marker.getSnippet()); return infoWindow; } }); }
Salvar o estado do mapa
Salve a posição da câmera do mapa e a localização do aparelho. Quando o usuário gira o dispositivo Android ou muda alguma configuração, o framework exclui a atividade no Google Maps e cria uma nova. Para aprimorar a experiência do usuário, armazene o estado de aplicativos relevantes e restaure-o quando necessário.
Neste tutorial, fornecemos todo o código necessário para salvar o estado do mapa. Se você quiser mais detalhes, consulte o guia do pacote savedInstanceState
.
Na atividade no Google Maps, configure chaves-valor para armazenar o estado da atividade:
private static final String KEY_CAMERA_POSITION = "camera_position"; private static final String KEY_LOCATION = "location";
Implemente o callback
onSaveInstanceState()
para salvar o estado quando a atividade for pausada:@Override protected void onSaveInstanceState(Bundle outState) { if (mMap != null) { outState.putParcelable(KEY_CAMERA_POSITION, mMap.getCameraPosition()); outState.putParcelable(KEY_LOCATION, mLastKnownLocation); super.onSaveInstanceState(outState); } }
No método
onCreate()
da sua atividade, recupere a localização do dispositivo e a posição da câmera do mapa, caso tenham sido salvas anteriormente:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { mCurrentLocation = savedInstanceState.getParcelable(KEY_LOCATION); mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION); } ... }