Desenvolvimento para Android - Utilizando o botão físico de busca

20 Feb 2012 . category: article . Comments
#android #java #desenvolvimento #mobile #carnandroid

Introdução

Alguns smartphones com Android possuem botões físicos. Um deles dispara um evento que o Android trata como uma solicitação de busca. Se a aplicação não tratar esse tipo de evento o sistema operacional irá tratá-la da forma que estiver configurado, na maioria dos casos um navegador de internet irá abrir com um textfield para inserir alguma informação para ser buscada.

O que eu quero mostrar neste artigo é uma forma da aplicação que você estiver desenvolvedor tratar esse evento de busca ao clicar no botão físico de busca.

Método para busca no banco

Iremos criar um novo método em nossa classe LinguagemDataSource que recebe um string. Essa string servirá de filtro para o nome das linguagens cadastradas no banco.

Arquivo: LinguagemDataSource.java

package com.diegorubin.search_list;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class LinguagemDataSource {

  private SQLiteDatabase db;
  private DatabaseHelper helper;
  
  public LinguagemDataSource(Context context) {
    helper = new DatabaseHelper(context);
    db = helper.getDatabase();
  }
  
  /**
   * Filtrando as linguagens cadastradas no banco.
   * Este método será utilizado ao pressionar o botão de busca.
   */
  public List<JSONObject> filterByNome(String nome) {
    List<JSONObject> result = new ArrayList<JSONObject>();
    Cursor cursor = 
      db.query("linguagens", new String[]{"nome", "descricao"}, 
               "nome like '%" + nome + "%'",
               null, null, null, "nome ASC" /*ordenando pelo nome*/);

    cursor.moveToFirst();
    while(!cursor.isAfterLast()) {
      result.add(readRow(cursor));
      
      cursor.moveToNext();
    }
    
    cursor.close();
    return result;
  }
  
  /**
   * Recuperando todas a linguagens cadastradas no nosso
   * banco de dados.
   * Iremos retorná-los em List<JSONObject>, pois é o 
   * formato que o nosso adapter espera.
   */
  public List<JSONObject> allLinguagens() {
    List<JSONObject> result = new ArrayList<JSONObject>();
    
    // Iremos buscar todas as linguagens cadastradas no banco
    // As colunas que iremos selecionar serão nome e descricao
    // O objeto de retorno contém a referencias das linhas retornadas
    Cursor cursor = 
      db.query("linguagens", new String[]{"nome", "descricao"}, 
               null, /* buscaremos todas, nao precisamos de nenhuma condicao*/
               null, null, null, "nome ASC" /*ordenando pelo nome*/);
    
    cursor.moveToFirst();
    while(!cursor.isAfterLast()) {
      result.add(readRow(cursor));
      
      cursor.moveToNext();
    }
    
    cursor.close();
    return result;
  }
  
  /**
   * Método auxiliar para ler resultado da query
   */
  private JSONObject readRow(Cursor cursor) {
    JSONObject obj = new JSONObject();
    
    try{
      // As colunas são recuperadas na ordem que foram selecionadas
      obj.put("nome", cursor.getString(0));
      obj.put("descricao",cursor.getString(1));
    }catch (JSONException e) {
    }
    
    return obj;
  }
}

Alterações no SearchListActivity

Temos que alterar a classe SearchListActivity. Agora iremos checar se foi chamado após o callback de busca ou não. Se ele atender o primeiro caso, usaremos a String que foi usada como entrada no dialogo de busca para filtrar os nossos dados.

Arquivo: SearchListActivity.java

package com.diegorubin.search_list;

import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Toast;

public class SearchListActivity extends Activity {
  
  private ListView lstLinguagens;
  private List<JSONObject> linguagens;
  private LinguagemArrayAdapter adapter;
  private LinguagemDataSource source;
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    // Buscando o elemento Listview da nossa interface principal interface 
    lstLinguagens = (ListView) findViewById(R.id.lstLinguagens);
    
    source = new LinguagemDataSource(getApplicationContext());
    
    //Verifica se a activity foi chamada através do callback de busca 
    Intent intent = getIntent();
    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
      String query = intent.getStringExtra(SearchManager.QUERY);
      
      // Recupera do banco as informações filtradas
      linguagens = source.filterByNome(query);
      
    } else {
    
      // Recupera do banco as informações que serão utilizados em nosso adapter
      linguagens = source.allLinguagens();
    }         
    
    // Passamos a lista de exemplo para gerar nosso adpater
    adapter = new LinguagemArrayAdapter(getApplicationContext(), R.layout.linguagem, linguagens);
    
    // Setando o adapter em nossa ListView
    lstLinguagens.setAdapter(adapter);
    
    // Setando callback ao selecionar um item da lista
    lstLinguagens.setOnItemClickListener(new OnItemClickListener() {
      
      public void onItemClick(AdapterView<?> parent, View view,
                              int position, long id) {
        
        try{
          JSONObject linguagem = linguagens.get(position);
          String descricao = linguagem.getString("descricao");
          Toast.makeText(getApplicationContext(), descricao, 10000).show();
        }catch (JSONException e) {
        }
      }
      
    });
    
    }
}

Configuração do manifest

Um novo arquivo deve ser criado em res/xml chamado searchconfig.xml, bom, esse nome não é obrigatório, mas acredito que seja um bom nome para ele. O conteúdo dele esta logo abaixo, com as configurações do dialogo de busca da nossa aplicação.

Arquivo: searchconf.xml

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:searchMode="showSearchLabelAsBadge"
    android:hint="Filtrar" >
</searchable>

Agora é necessário fazer algumas alterações no arquivo AndroidManifest.xml

Arquivo: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.diegorubin.search_list"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="7" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".SearchListActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
                
                <!-- Avisa que nossa activity pode ser utilizada na busca -->
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            
            <!-- Setando esta activity para ser utilizada como callback da busca -->
            <meta-data android:name="android.app.default_searchable"
                       android:value=".SearchListActivity" />
            <!-- configuração do dialogo de busca -->
            <meta-data android:name="android.app.searchable"
                     android:resource="@xml/searchconf"/> 
        </activity>
    </application>

</manifest>

O elemento meta-data com o atributo android:name igual a android.app.default_searchable deve ser setada em todas as activity’s que irão tratar o evento do botão fisico de busca.

Agora, ao pressionar o botão de busca, nossa aplicação tratará o evento e não o sistema. Não será aberto, por exemplo, o navegador, mas sim um dialogo para entrada de dados e esses dados repassado a nossa Activity.

Conclusão

Este foi o último arquivo da série sobre desenvolvimento para Android no CarnAndroid. Brincadeiras a parte, acredito que tenha alcançado meu objetivo.

Nosso projeto chegou a versão final, e estará no repositório do blog no github. Qualquer dúvida ou sugestão, por favor, utilizem o sistema de comentário do site.

Agradeço as pessoas que acompanharam os posts e em breve espero escrever mais sobre o desenvolvimento de aplicações para Android.

[]’s


Me

Tenho estudado esse mundo mágico da programação desde 2005. Já consegui sustentar minha família usando Ruby, Java, Python, C++ e Javascript. O resto tenho usado para diversão ou aprendizado.