Estou com um monte de posts legais pra publicar, mas infelizmente (pra variar) estou sem tempo. São coisas que precisei fazer nos projetos que trabalhei, e que gosto de compartilhar com quem lê o blog. Além do mais, me serve como fonte de consulta para uma uso posterior. Afinal de contas, é pra isso que serve o blog (pelo menos pra mim).
Hoje vou mostrar como criar uma consulta personalizada para o componente AutoCompleteTextView. Esse componente é utilizado quando você tem uma lista de valores, e ao invés de selecioná-lo em uma lista, você digita parte do texto e os resultados vão sendo exibidos em uma lista (estilo drop-down) abaixo do componente. Este componente já conta com um mecanismo de busca padrão, entretanto a necessidade de personalizá-lo surgiu quando precisei realizar a busca por nomes de cidades. O cliente solicitou que a acentuação fosse ignorada, ou seja, quando eu digitasse "sao" era para aparecer "São Paulo" nos resultados.
Vamos à implementação! Primeiro vou mostrar o método que substitui os caracteres acentuados pelos mesmos não acentuados.
public static String[] REPLACES =
{ "a", "e", "i", "o", "u", "c" };
public static Pattern[] PATTERNS = null;
public static void compilePatterns() {
PATTERNS = new Pattern[REPLACES.length];
PATTERNS[0] = Pattern.compile(
"[âãáàä]", Pattern.CASE_INSENSITIVE);
PATTERNS[1] = Pattern.compile(
"[éèêë]", Pattern.CASE_INSENSITIVE);
PATTERNS[2] = Pattern.compile(
"[íìîï]", Pattern.CASE_INSENSITIVE);
PATTERNS[3] = Pattern.compile(
"[óòôõö]", Pattern.CASE_INSENSITIVE);
PATTERNS[4] = Pattern.compile(
"[úùûü]", Pattern.CASE_INSENSITIVE);
PATTERNS[5] = Pattern.compile(
"[ç]", Pattern.CASE_INSENSITIVE);
}
public static String removeAcentos(String text) {
if (PATTERNS == null) {
compilePatterns();
}
String result = text;
for (int i = 0; i < PATTERNS.length; i++) {
Matcher matcher = PATTERNS[i].matcher(result);
result = matcher.replaceAll(REPLACES[i]);
}
return result.toUpperCase();
}
O código acima, utiliza as classes Pattern e Matcher para checar a presença de caracteres acentuados e substitui-los pelos caracteres correspondentes sem acentuação. Observe que só são sendo tratadas as vogais e o cedilha, para textos em outros idiomas você deve fazer os ajustes necessários (como o 'ñ' do espanhol).
Para que um AutoCompleteTextView faça a busca, é necessário definir um Adapter com os dados que ele irá filtrar para exibir. Para fazer nossa busca personalizada (ignorando acentuação) devemos criar nosso Adapter e sobrescrever o método getFilter(). Esse método deve retornar um objeto da classe android.widget.Filter que representa o resultado da busca.
public class MeuAutoCompleteAdapter
extends ArrayAdapter<String>
implements Filterable {
private List<String> listaCompleta;
private List<String> resultados;
private Filter meuFiltro;
public MeuAutoCompleteAdapter(
Context ctx, int layout,
List<String> textos) {
super(ctx, layout, textos);
this.listaCompleta = textos;
this.resultados = listaCompleta;
this.meuFiltro = new MeuFiltro();
}
@Override
public int getCount() {
return resultados.size();
}
@Override
public String getItem(int position) {
if (resultados != null
&& resultados.size() > 0
&& position < resultados.size()){
return resultados.get(position);
} else {
return null;
}
}
@Override
public Filter getFilter() {
return meuFiltro;
}
private class MeuFiltro extends Filter {
@Override
protected FilterResults performFiltering(
CharSequence constraint) {
FilterResults filterResults =
new FilterResults();
ArrayList<String> temp =
new ArrayList<String>();
if (constraint != null) {
String term = removeAcentos(
constraint.toString().trim().toLowerCase());
String placeStr;
for (String p : listaCompleta) {
placeStr = removeAcentos(p.toLowerCase());
if ( placeStr.indexOf(term) > -1){
temp.add(p);
}
}
}
filterResults.values = temp;
filterResults.count = temp.size();
return filterResults;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(
CharSequence contraint,
FilterResults filterResults) {
resultados = (ArrayList<String>)
filterResults.values;
notifyDataSetChanged();
}
}
}
A classe acima tem duas listas de strings: a original e a que serve como resultado da busca. Note que os métodos getCount e getItem trabalha em cima da lista dos resultados. O outro atributo da classe é da classe MeuFiltro, e esse atributo é retornado no método getFilter.
A classe interna MeuFiltro faz a nossa busca personalizada. O filtro é realizado no método performFiltering, que retorna um objeto da classe android.widget.Filter.FilterResults que contém o resultado da busca e o total de registros encontrados. Esse resultado é passado para o método publishResults que seta o resultado da busca no atributo resultados, e logo após atualiza o adapter através do método notifyDatasetChanged.
Agora vamos ver como usar esse nosso adapter.
List<String> cidade = new ArrayList<String>();
cidade.add("Recife");
cidade.add("São Paulo");
cidade.add("Santos");
cidade.add("Santa Cruz");
MeuAutoCompleteAdapter adapter =
new MeuAutoCompleteAdapter(
contexto,
android.R.layout.simple_dropdown_item_1line,
cidade);
AutoCompleteTextView actv = (AutoCompleteTextView)
findViewById(R.id.autoCompleteTextView1);
actv.setAdapter(adapter);
O resultado é exibido na figura abaixo:

Qualquer dúvida, deixem seus comentários.
4br4ç05,
nglauber