Assuming you already make use of version 2 of Google’s Maps API, the goal here is to show how to search by addresses or coordinates (available at source) using either Google’s API for Android or the online geolocation API (accessed through an HTTP request).
To do this, we will create a class that will have five other nested classes:
Geolocationsearch: This encapsulates the whole process.
Geocodertask: This searches by address using API features for Android.
Geocodertaskjson: This other one also searches by address, but using the external API (request).
Geocodertasklatlng: This one searches for the address corresponding to a coordinate. Uses the API for Android.
Geocodertasklatlngjson: It also searches for the address corresponding to a coordinate, but uses the external API.
Local: This class serves as an entity for address data to be encapsulated into objects of the same.
Within the Geolocationsearch class there is also an interface that will serve as an event at the time an address is found.
OnLocationSearchListener
Let’s go to structure...
The class starts as follows:
public class GeoLocationSearch
{
private Context context;
private OnLocationSearchListener onLocationSearchListener;
public OnLocationSearchListener getOnLocationSearchListener() {
return onLocationSearchListener;
}
public void setOnLocationSearchListener(OnLocationSearchListener onLocationSearchListener) {
this.onLocationSearchListener = onLocationSearchListener;
}
public Context getContext() {
return context;
}
public GeoLocationSearch(Context context)
{
this.context = context;
}
Deve ser passado para ela o Context, pois dentro dela existem chamadas de diálogos, e esses precisam de um contexto para serem exibidos.
Ela possui como atributo, um objeto da interface citada anteriormente, que servirá de evento quando um endereço for encontrado.
Abaixo, vemos a implementação dessa interface:
public interface OnLocationSearchListener{
public void onLocationSearch(Local local);
}
Ou seja, caso você informe um listener, um método que escutará o evento, ele deve informar para esse atributo (no fim isso ficará claro ). Com certeza você fará isso =)
Consulta por endereço
O objetivo da consulta por um endereço é saber a coordenada daquele endereço pesquisado e exibir um ponto no mapa.
NOTA: Outra utilidade também é em formulários onde é necessário informar endereço. O usuário pode digitar apenas o CEP e você poderá realizar a busca passando esse CEP como se fosse um endereço. O Google encontra!!!
Para consultar por endereços ou coordenadas, você pode chamar um dos métodos abaixo:
public void searchByAddress(String address, int maxResult){
new GeocoderTask(address,maxResult).execute();
}
public void searchByAddress(String address, int maxResult, OnLocationSearchListener onLocationSearchListener){
setOnLocationSearchListener(onLocationSearchListener);
new GeocoderTask(address,maxResult).execute();
}
public void searchByCoordenate(LatLng address){
new GeocoderTaskLatLng().execute(address);
}
public void searchByCoordenate(LatLng address, OnLocationSearchListener onLocationSearchListener){
setOnLocationSearchListener(onLocationSearchListener);
new GeocoderTaskLatLng().execute(address);
}
NOTE: You will not use the classes that query by request the API
external, because the Geolocationsearch class gives preference to the service
available in the API for Android (using the Geocoder class).
It first calls the execution done by the Geocodertask class and if it does not find the address (because of network failure or for some other reason), it tries to request the external API (calling the execution of the other class).
Here is the implementation of this class:
private class GeocoderTask extends AsyncTask<Void, Void, List<Address>>
{
//Máximo de resultados a serem retornados na pesquisa
private int MAX_REQUESTS_RETURNS = 5;
public GeocoderTask(String endereco){
this.endereco = endereco;
}
public GeocoderTask(String endereco,int maxResult){
MAX_REQUESTS_RETURNS = maxResult;
this.endereco = endereco;
}
private String endereco;
private ProgressDialog progressDialog;
@Override
public void onPreExecute(){
progressDialog = new ProgressDialog(context);
progressDialog.setMessage(Html.fromHtml("Pesquisando...<br><b>" + endereco + "</b></br>"));
progressDialog.show();
}
@Override
protected List<Address> doInBackground(Void... args)
{
///este objeto é o responsável pela busca
Geocoder geocoder = new Geocoder(context);
//esta lista irá armazenar o resultado da busca
List<Address> addresses = null;
try
{
addresses = geocoder.getFromLocationName(this.endereco, MAX_REQUESTS_RETURNS);
}
catch (IOException e)
{
Log.e("GEO_TASK",e.getMessage());
}
catch(Exception e)
{
Log.e("GEO_TASK",e.getMessage());
}
//retorna o que foi encontrado
//se houver falha na rede ou algum outro tipo de falha, é retornado null
return addresses;
}
@Override
protected void onPostExecute(final List<Address> addresses)
{
progressDialog.dismiss();
//nesse caso, tenta a pesquisa pela requisição da API externa
if(addresses == null)
{
//Tenta pesquisar pelo JSon
new GeocoderTaskJSon(endereco).execute();
return;
}
if(addresses.size() == 0 )
{
Toast.makeText(context, "Não foi possível encontrar o endereço pesquisado", Toast.LENGTH_SHORT).show();
return;
}
if(addresses.size() == 1)
{
//Retorna endereço pesquisado
Local local = new Local();
String cidade_estado_cep = (addresses.get(0).getMaxAddressLineIndex() > 0 ?
addresses.get(0).getAddressLine(1) : "") + (addresses.get(0).getMaxAddressLineIndex() > 1 ? ", " + addresses.get(0).getAddressLine(2) : "");
local.setDescricao(cidade_estado_cep);
LatLng latLng = new LatLng(addresses.get(0).getLatitude(), addresses.get(0).getLongitude());
local.setCoordenadas(latLng);
local.setCidade_estado(addresses.get(0).getMaxAddressLineIndex() >= 0 ? addresses.get(0).getAddressLine(0) : "");
//Retorna o endereço encontrado passando para o escutador
if(onLocationSearchListener != null)
onLocationSearchListener.onLocationSearch(local);
}
else
{
//Quando mais de um endereço é encontrado (no caso de endereços de nome semelhantes)
//uma lista é exibida para o usuário escolher o endereço que deseja
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle("Você quis dizer:");
ListAdapter adapter = getAdapterSuggestions(addresses);
alert.setAdapter(adapter, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
//Retonra endereço pesquisado
Local local = new Local();
String cidade_estado_cep = (addresses.get(0).getMaxAddressLineIndex() > 0 ?
addresses.get(0).getAddressLine(1) : "") + (addresses.get(0).getMaxAddressLineIndex() > 1 ? ", " + addresses.get(0).getAddressLine(2) : "");
local.setDescricao(cidade_estado_cep);
LatLng latLng = new LatLng(addresses.get(0).getLatitude(), addresses.get(0).getLongitude());
local.setCoordenadas(latLng);
local.setCidade_estado(addresses.get(0).getMaxAddressLineIndex() >= 0 ? addresses.get(0).getAddressLine(0) : "");
//Retorna o endereço encontrado passando para o escutador
if(onLocationSearchListener != null)
onLocationSearchListener.onLocationSearch(local);
}
});
alert.create().show();
}
}
// Retorna um adapter com os itens a serem exibidos para o usuário
private ListAdapter getAdapterSuggestions(final List<Address> items)
{
ListAdapter adapter = new ArrayAdapter<Address>(context, R.layout.address_item, items)
{
ViewHolder holder;
class ViewHolder
{
private TextView title;
private TextView sub_title;
public TextView getDescricao()
{
return title;
}
public void setDescricao(TextView title) {
this.title = title;
}
public TextView getImagePin() {
return sub_title;
}
public void setSubTitle(TextView sub_title) {
this.sub_title = sub_title;
}
}
public View getView(int position, View convertView, ViewGroup parent)
{
final LayoutInflater inflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
{
convertView = inflater.inflate(R.layout.address_item, null);
}
holder = new ViewHolder();
holder.setDescricao((TextView) convertView.findViewById(R.id.TextViewEndereco));
holder.setSubTitle((TextView) convertView.findViewById(R.id.TextViewBairroMunEst));
Address address = items.get(position);
holder.getDescricao().setText(address.getMaxAddressLineIndex() >= 0 ? address.getAddressLine(0) : "");
String cidade_estado_cep = (address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(1) : "")
+ (address.getMaxAddressLineIndex() > 1 ? ", " + address.getAddressLine(2) : "");
holder.getImagePin().setText(cidade_estado_cep);
return convertView;
}
};
return adapter;
}
}
This class is executed and at the end, in the onPostExecute method, it is checked if something has been returned. If it is not (null address list), it is called an execution of the class that does the query using the external API, as stated above.
Below, the implementation of the class that requests using the external API:
private class GeocoderTaskJSon extends AsyncTask<Void, Void, List<Local>>
{
private int MAX_REQUESTS_RETURNS = 5;
public GeocoderTaskJSon(String endereco){
this.endereco = endereco;
}
public GeocoderTaskJSon(String endereco,int maxResult){
MAX_REQUESTS_RETURNS = maxResult;
this.endereco = endereco;
}
private String endereco;
private ProgressDialog progressDialog;
@Override
public void onPreExecute(){
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Pesquisando...");
progressDialog.show();
}
@Override
protected List<Local> doInBackground(Void... args)
{
// POR LATITUDE
List<Local> addresses = null;
StringBuilder stringBuilder = new StringBuilder();
try
{
String query_uri = String.format("https://maps.googleapis.com/maps/api/geocode/json?address=%s&sensor=true",this.endereco).replace(" ", "%20");
HttpGet httpGet = new HttpGet(query_uri);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(httpGet);
StatusLine placeSearchStatus = response.getStatusLine();
//only carry on if response is OK
if (placeSearchStatus.getStatusCode() == 200)
{
//get response entity
HttpEntity placesEntity = response.getEntity();
//get input stream setup
InputStream placesContent = placesEntity.getContent();
//create reader
InputStreamReader placesInput = new InputStreamReader(placesContent);
//use buffered reader to process
BufferedReader placesReader = new BufferedReader(placesInput);
//read a line at a time, append to string builder
String lineIn;
while ((lineIn = placesReader.readLine()) != null)
{
stringBuilder.append(lineIn);
}
JSONObject jsonObject = new JSONObject(stringBuilder.toString());
JSONArray results = (JSONArray)jsonObject.get("results");
JSONArray components;
if(results != null)
{
addresses = new ArrayList<Local>();
String endereco, cidade_estado;
LatLng coord;
double lat,lng;
Local local;
int max = results.length();
for (int i = 0; i < max && i < MAX_REQUESTS_RETURNS; i++)
{
lng = results.getJSONObject(i)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lng");
lat = results.getJSONObject(i)
.getJSONObject("geometry")
.getJSONObject("location")
.getDouble("lat");
components = results.getJSONObject(i).getJSONArray("address_components");
//Verifica se é o número
int i_bairro = 1;
try
{
Double.parseDouble(components.getJSONObject(0).getString("long_name"));
i_bairro = 2;
endereco = components.getJSONObject(1).getString("long_name") + ", " + components.getJSONObject(0).getString("long_name");
}
catch (Exception e)
{
endereco = components.getJSONObject(0).getString("long_name");
}
endereco += " - " + components.getJSONObject(i_bairro).getString("long_name");
cidade_estado = components.getJSONObject(i_bairro + 1 ).getString("long_name");
cidade_estado += " - " + components.getJSONObject( i_bairro + 2).getString("short_name");
cidade_estado += ", " + components.getJSONObject(i_bairro + 4).getString("long_name");
coord = new LatLng(lat, lng);
local = new Local();
local.setEndereco(endereco);
local.setCidade_estado(cidade_estado);
local.setCoordenadas(coord);
addresses.add(local);
}
}
}
}
catch (ClientProtocolException e)
{
Log.e("GEO_TASK", e.getMessage());
}
catch (JSONException e)
{
Log.e("GEO_TASK",e.getMessage());
}
catch (IOException e)
{
Log.e("GEO_TASK",e.getMessage());
}
catch(Exception e)
{
Log.e("GEO_TASK",e.getMessage());
}
return addresses;
}
@Override
protected void onPostExecute(final List<Local> addresses)
{
progressDialog.dismiss();
if(addresses == null)
{
Toast.makeText(context,"Falha na rede", Toast.LENGTH_LONG).show();
return;
}
else if(addresses.size() == 0 )
{
Toast.makeText(context, "Não foi possível encontrar o endereço pesquisado", Toast.LENGTH_SHORT).show();
return;
}
if(addresses.size() == 1)
{
//Retorna o endereço encontrado passando para o escutador
if(onLocationSearchListener != null)
onLocationSearchListener.onLocationSearch(addresses.get(0));
}
else
{
//Pede o usuário para selecionar um entre os encontrados
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle("Você quis dizer:");
ListAdapter adapter = getAdapterSuggestions(addresses);
alert.setAdapter(adapter, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Local address = addresses.get(which);
//Retorna o endereço encontrado passando para o escutador
if(onLocationSearchListener != null)
onLocationSearchListener.onLocationSearch(address);
}
});
alert.create().show();
}
}
private ListAdapter getAdapterSuggestions(final List<Local> items)
{
ListAdapter adapter = new ArrayAdapter<Local>(context, R.layout.address_item, items)
{
ViewHolder holder;
class ViewHolder
{
private TextView title;
private TextView sub_title;
public TextView getDescricao()
{
return title;
}
public void setDescricao(TextView title) {
this.title = title;
}
public TextView getImagePin() {
return sub_title;
}
public void setSubTitle(TextView sub_title) {
this.sub_title = sub_title;
}
}
public View getView(int position, View convertView, ViewGroup parent)
{
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
{
convertView = inflater.inflate(R.layout.address_item, null);
}
holder = new ViewHolder();
holder.setDescricao((TextView) convertView.findViewById(R.id.TextViewEndereco));
holder.setSubTitle((TextView) convertView.findViewById(R.id.TextViewBairroMunEst));
Local address = items.get(position);
holder.getDescricao().setText(address.getEndereco());
holder.getImagePin().setText(address.getCidade_estado());
return convertView;
}
};
return adapter;
}
}
Basicamente, esta classe executa o mesmo procedimento feito pela classe GeocoderTask, porém utiliza outra fonte.
Como você pode ver, quando o endereço é retornado, um objeto da classe Local é instanciado e repassado ao evento, caso informado.
Exemplo de utilização da classe:
GeoLocationSearch geoLocationSearch = new GeoLocationSearch(this);
geoLocationSearch.searchByAddress("Av. Cristiano Machado, 1682",10,new GeoLocationSearch.OnLocationSearchListener() {
@Override
public void onLocationSearch(GeoLocationSearch.Local local) {
//Se houver algum resultado, ele será retornado aqui
//Na chamada do método, informei que quero no máximo 10 resultados
}
});
As you can see above, the functionality is given throughout this implementation, but the actual use of the resource is made in less than 10 lines of code :)
Here is the simple implementation of the Local class:
public class Local implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 1L;
private String endereco;
private String cidade_estado;
private LatLng coordenadas;
private String descricao;
public String getEndereco() {
return endereco;
}
public void setEndereco(String endereco) {
this.endereco = endereco;
}
public LatLng getCoordenadas() {
return coordenadas;
}
public void setCoordenadas(LatLng coordenadas) {
this.coordenadas = coordenadas;
}
public String getCidade_estado() {
return cidade_estado;
}
public void setCidade_estado(String cidade_estado) {
this.cidade_estado = cidade_estado;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
Para os itens que são exibidos para o usuário, no caso de mais de um endereço ser retornado, é utilizado o seguinte layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="5dp"
android:background="@android:drawable/list_selector_background"
android:orientation="vertical"
android:weightSum="1" >
<TextView
android:id="@+id/TextViewEndereco"
style="?android:attr/spinnerItemStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:singleLine="true"
android:text="Av. Cristiano Machado, 1682"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:id="@+id/TextViewBairroMunEst"
style="?android:attr/spinnerItemStyle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="3dp"
android:singleLine="true"
android:text="Cidade Nova, Belo Horizonte -MG, Minas Gerais"
android:textSize="14sp" />
</LinearLayout>
NOTA: Esse arquivo de layout deve ser inserido na pasta res/layout do seu projeto.
See the source to see how it is done with coordinates, that is, you pass a Latlng object and the class also searches for the address of that coordinate. In this case two other classes will be used at the beginning: Geocodertasklatlng and Geocodertasklatlngjson.
TIP: If you want to treat when an address is not returned,
you can make the following modification:
public interface OnLocationSearchListener{
public void onLocationSearch(Local local);
public void onNetworkFailed();
public void onAddressNotFound();
}
Thus, these two additional methods will be called in the appropriate situations, ie when there is network failure or when the address is not found. You must follow the same pattern by calling these methods where there is a Toast message call.
Link to the library updated and complete
Just index his first position,
double[] coord = coords[0]
and keeps everything as it is.– Wakim
It didn’t work, the ADT accuses the following:
The method getFromLocation(double, double, int) in the type Geocoder is not applicable for the arguments (double[], double[], int)
– Renan Lazarotto
The problem is this, you need to set in a local variable the first position of the
coords
and then use it within that method as well– Wakim
Something like this:
double[] coord = coords[0];
double lat = coord[0];
double lng = coord[1];
?– Renan Lazarotto
That, and then pass the
coord
progetFromLocation
also– Wakim
I haven’t tried it yet, but at least I haven’t gotten a bug. Thank you!
– Renan Lazarotto