Partindo do princípio que você já faz uso da versão 2 da API de Maps do Google, o objetivo aqui é mostrar como fazer pesquisa por endereços ou coordenadas (disponível no fonte) utilizando recursos do Google, tanto da API  para Android ou da API de geolocalização online (acessada por meio de uma requisição HTTP).

(Você pode ver como usar a API Maps V2  no post Android & GPS & Google Maps V2 por Leonardo Casa Santa)

Para isso, vamos criar uma classe que terá outras cinco classes aninhadas:  

  •  GeoLocationSearch:    Esta  encapsula todo o processo.
  •  GeocoderTask:               Esta faz a busca por endereço utilizando recursos da API para Android.
  • GeocoderTaskJSon:     Esta outra também faz a busca por endereço, mas utilizando a API externa (requisição).
  • GeocoderTaskLatLng: Esta aqui busca pelo endereço correspondente a uma coordenada. Utiliza a API para Android.
  • GeocoderTaskLatLngJSon:  Esta também busca pelo endereço correspondente a uma coordenada, mas utiliza a API externa.
  • Local: Esta classe serve como entidade para que os dados de endereço sejam encapsulados em objetos da mesma.

Dentro da classe GeoLocationSearch também existe uma interface que servirá como evento no momento em que um endereço for encontrado.

  • OnLocationSearchListener

Vamos a estrutura…

A classe inicia da seguinte maneira:

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);
}

 

 

NOTA: Você não irá usar as classes que pesquisam por requisição a API externa, pois a classe GeoLocationSearch dá preferência ao serviço disponível na API para Android (utilizando a classe Geocoder).

 

Ela chama primeiro a execução feita pela classe GeocoderTask e caso essa não encontre o endereço (por conta de falha na rede ou por algum outro motivo), ela tenta pela requisição da API externa (chamando a execução da outra classe).

Aqui está a implementação desta classe:

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;
	}

}

Essa classe é executada e ao final, no método onPostExecute, é verificado se foi retornado algo. Caso não seja (lista de endereços null), é chamada uma execução da classe que faz a consulta utilizando a API externa, como dito anteriormente.

Abaixo, a implementação da classe que faz a requisição utilizando a API externa:

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
	}
});

 

Como você pôde ver acima, a funcionalidade se dá por toda essa implementação, mas a utilização em si do recurso é feita em menos de 10 linhas de código 🙂

Aqui está a implementação simples da classe Local:

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.

 

 

Veja o fonte para ver como é feito com coordenadas, ou seja, você passa um objeto LatLng e a classe também faz a busca pelo endereço dessa coordenada.  Nesse caso serão utilizadas outras duas classes citadas no início:  a GeocoderTaskLatLng  e a GeocoderTaskLatLngJson.

 

DICA:  Caso você queira tratar quando um endereço não é retornado, você pode fazer a seguinte modificação:

  public  interface  OnLocationSearchListener{

        public void onLocationSearch(Local local);
        public void onNetworkFailed();
        public void onAddressNotFound();
    }

 

Assim, esses dois métodos adicionais serão chamados nas devidas situações, ou seja, quando houver falha na rede ou quando o endereço não for encontrado.  Você deve seguir o mesmo padrão chamando esses métodos onde existe uma chamada de mensagem Toast.
Fonte do projeto: Google Maps Seearch Addres