Baixe o app para aproveitar ainda mais
Prévia do material em texto
Minicurso Android Avançado Flávio Augusto de Freitas flaviocefetrp@gmail.com http://flavioaf.blogspot.com Sumário Passos Iniciais .................................................................................................................................................................... 3 Montando um Ambiente de Desenvolvimento para Android ...................................................................................... 4 Configurando um Dispositivo Android com o AVD Manager ....................................................................................... 9 Tutoriais Avançados ........................................................................................................................................................ 12 Persistência em Banco de Dados ................................................................................................................................ 13 Editando Registros do Banco de Dados ...................................................................................................................... 22 Configurando Preferências ......................................................................................................................................... 36 Ajustando o Layout para Paisagem (II) ....................................................................................................................... 41 Integrando Bibliotecas de Terceiros (Twitter) ............................................................................................................ 44 Utilizando um IntentService........................................................................................................................................ 53 Integrando o GPS ........................................................................................................................................................ 57 Alterando o Ícone do Launcher ................................................................................................................................... 65 Integrando com o Google Maps ................................................................................................................................. 67 Alarmes ....................................................................................................................................................................... 69 Notificações ................................................................................................................................................................ 76 Internacionalização (i18n) ........................................................................................................................................... 78 Widgets ....................................................................................................................................................................... 84 Fazendo Ligações (Chamadas) .................................................................................................................................... 91 Terminamos .................................................................................................................................................................... 97 Contatos .......................................................................................................................................................................... 98 Passos Iniciais Montando um Ambiente de Desenvolvimento para Android Hoje vou mostrar como montar um ambiente de desenvolvimento para Android! Pra quem nunca ouviu falar, o Android é um sistema operacional da Google para dispositivos móveis. Hoje em dia, milhões de celulares e tablets utilizam o Android como sistema. Uma coisa bem legal é que você pode disponibilizar seus aplicativos no Market do Android (após o pagamento de uma taxa) e talvez até ganhar um dinheiro com isso! Legal, né? O desenvolvimento de aplicativos para Android é feito utilizando a linguagem Java, com a utilização de arquivos XML para a criação das interfaces. Apesar de parecer complexo, é relativamente simples criar seus aplicativos. Além disso, é bem fácil ter acesso a diversos recursos geralmente disponíveis em dispositivos móveis, tais como câmera, GPS, Bluetooth, etc. Para facilitar o desenvolvimento, foi criado um plug-in para o Eclipse. Através dele, é fácil gerenciar as plataformas (diversas versões do Android) e as máquinas virtuais para executar seus aplicativos. Bom, então pra começar, vamos fazer o download da JDK. Se você já programa em Java, este passo não é necessário. No momento em que escrevo este tutorial, a versão mais recente é a Java 7 update 2. Siga até esta página e faça o download. A instalação deve ocorrer sem problemas (o famoso, next, next, next, finish). O próximo passo é baixar o Eclipse. Vá até esta página e faça o download relacionado a versão de seu sistema operacional. Para os nossos propósitos, a versão Eclipse IDE for Java Developers deve ser suficiente. Ao concluir o download, basta descompactar o arquivo em algum lugar da sua máquina. Eu recomendo, no caso do Windows, na raiz C: ou em sua pasta de usuário (C:\Users\<Seu Usuário>). Neste exemplo, vou referenciar a pasta do Eclipse como C:\eclipse. Prosseguindo, devemos agora baixar o Android SDK. É ele quem nos fornecerá todas as ferramentas da plataforma, como emulador, bibliotecas, etc. Vá até essa página e baixe a versão zipada da SDK (apesar de recomendarem a versão “instalável”) – isso é pra evitarmos problemas de permissão na hora de baixar as SDKs, caso esteja na pasta de programas do sistema (Program Files ou Arquivos de Programas). No momento em que escrevo este tutorial, a versão mais recente é a 16. Após a conclusão do download, descompacte o arquivo (pode ser no mesmo local onde você colocou o Eclipse). Aqui, por exemplo, vai ficar C:\android-sdk-windows. Após extrair, vamos executar o SDK Manager para baixar uma SDK para começarmos a programar. Ao executar pela primeira vez, o SDK Manager irá verificar os repositórios do Android em busca das últimas versões do SDK. Para começar, vamos baixar o SDK da versão 2.2, já que os aplicativos desenvolvidos nela funcionam na grande maioria dos dispositivos Android de hoje. Se quiser instalar outras versões, fique à vontade. Expanda a pasta Android 2.2 (API 8 ) e marque as opções SDK Platform. Além disso, na categoria Tools, marque a opção Android SDK Platform- tools. Clique em Install 2 packages… (ou mais, se você selecionou mais alguma coisa), marque Accept All e então clique em Install. Após a conclusão dos downloads, é hora de configurar o Eclipse. Vá até o diretório onde ele foi descompactado e execute-o. Ao ser consultado sobre qual workspace utilizar, basta confirmar e utilizar o padrão (workspace é o local onde seus projetos serão salvos; ele fica na pasta C:\Users\<usuário>\workspace). Vamos agora adicionar o plugin para integrar o SDK Manager e o AVD Manager ao Eclipse. Clique no menu Help -> Install New Software… e na janela que abrir, clique no botão Add…. Na tela seguinte, preencha o nome do plugin (ADT Plugin) e coloque o endereço https://dl-ssl.google.com/android/eclipse, conforme a imagem abaixo: Clique em OK e aguarde o carregamento do repositório. Ao concluir, marque a caixa Developer Tools e clique em Next > duas vezes. Na tela seguinte, aceite os termos da licença e clique em Finish. Agora aguarde a instalação e, caso seja alertado sobre conteúdo não-assinado, cliqueem OK para continuar. Ao final, clique em Restart Now para reiniciar o Eclipse e concluir a instalação. O próximo passo é configurar o local onde as SDKs estão. No Eclipse, vá ao menu Window -> Preferences. Clique no botão Browse… e aponte para a pasta que você descompactou. Após a confirmação, deverão ser exibidas as SDKs que você baixou. Pronto! Seu ambiente Android já está pronto para ser utilizado! No próximo tutorial veremos como configurar um dispositivo para executar nossa aplicação. Configurando um Dispositivo Android com o AVD Manager Olá pessoal! No último tutorial sobre Android, vimos como configurar o ambiente para programarmos, utilizando o Eclipse. Neste tutorial, vamos ver como criar um dispositivo para a execução dos aplicativos que serão criados. Assim, você não precisa necessariamente de um celular com Android para começar a desenvolver para a plataforma. Bom, o primeiro passo é abrir o Eclipse e clicar no ícone do AVD Manager (AVD = Android Virtual Device), ou ir até o menu Window -> AVD Manager. Será, então, aberta a janela com a listagem de dispositivos criados (no nosso caso, nenhum ainda). Então, para criarmos um novo dispositivos, clicamos no botão New…. Nesta tela, devemos preencher os dados relativos ao nosso dispositivo, como nome (Name), versão do Android que irá executar (Target), além de dados como tamanho do cartão SD virtual (caso desejado), tamanho da tela e periféricos (câmera, GPS, Acelerômetro, Teclado físico, etc.). Após montar seu dispositivo, clique em Create AVD e terá seu dispositivo listado! Quando estiver desenvolvendo, é recomendável criar diferentes tipos de dispositivos, com versões diferentes do Android e tamanhos de tela variados, de forma a fazer seu aplicativo ser executado corretamente em diversas configurações. Tutoriais Avançados Persistência em Banco de Dados Olá leitores! No tutorial de hoje, vamos criar um sistema de persistência para a nossa Lista de Restaurantes. Assim, os restaurantes cadastrados serão mantidos a cada execução do aplicativo. O sistema Android nos fornece nativamente as opções de persistir dados utilizando arquivos ou em banco de dados, utilizando o SQLite. Se você não conhece o projeto, é interessante dar uma lida sobre ele. É um banco de dados bastante leve, que nos permite facilmente trabalhar com SQL sobre um arquivo. Neste tutorial, estou assumindo que você tenha um conhecimento básico em SQL. Se você nunca mexeu com isso, não se preocupe, pois os conceitos não são complicados de entender. Bom, começando o nosso tutorial, vamos criar uma classe que gerenciará a criação e abertura do nosso banco de dados. Vamos chamá-la de GerenciadorRestaurantes. Coloque-a no pacote com.blogspot.flavioaf.restaurante. 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.content.Context; 4 import android.database.sqlite.SQLiteDatabase; 5 import android.database.sqlite.SQLiteOpenHelper; 6 7 public class GerenciadorRestaurantes extends SQLiteOpenHelper { 8 9 private static final String NOME_BANCO = "restaurantes.db"; 10 private static final int VERSAO_SCHEMA = 1; 11 12 public GerenciadorRestaurantes(Context context) { 13 super(context, NOME_BANCO, null, VERSAO_SCHEMA); 14 } 15 16 @Override 17 public void onCreate(SQLiteDatabase db) { 18 db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," 19 + " nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT);"); 20 21 } 22 23 @Override 24 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 25 26 } 27 } A princípio, definimos que nosso banco de dados será armazenado no arquivo restaurantes.db e que utilizaremos a primeira versão do schema do banco. Neste ponto o projeto ainda deve compilar sem problemas. Em seguida, vamos implementar o método onCreate() para que ele crie o nosso banco de dados. 18 @Override 19 public void onCreate(SQLiteDatabase db) { 20 db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 21 " nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT);"); 22 } Neste trecho simplesmente criamos a tabela restaurantes com seus campos. O método onUpgrade() não será útil para nós por enquanto. Em um aplicativo real, você poderia implementá-lo para fazer backup dos dados em uma tabela temporária, atualizar a estrutura do banco e então retornar os dados. O próximo passo é remover partes do código da classe ListaRestaurantes que não nos serão úteis daqui pra frente (como os trechos que manipulam a nossa barra de progresso). As primeiras exclusões são os atributos estaAtivo e progresso. Em seguida, podemos remover a chamada ao método requestWindowFeature() dentro do método onCreate(). Podemos também excluir as implementações dos métodos onPause(), onResume(), onCreateOptionsMenu() e onOptionsItemSelected(). Por fim, podemos excluir também os métodos iniciarTarefa(), fazerAlgoDemorado() e a nossa tarefaLonga. A classe GerenciadorRestaurantes será a nossa ponte entre a aplicação e o banco de dados. Dessa forma, vamos criar um atributo na classe ListaRestaurantes chamado gerenciador do tipo GerenciadorRestaurante. 34 GerenciadorRestaurantes gerenciador; Lá no método onCreate(), após a chamada a setContentView(), vamos então instanciar o atributo: 40 gerenciador = new GerenciadorRestaurantes(this); Complementando, implemente o método onDestroy() na classe ListaRestaurantes. 67 @Override 68 public void onDestroy() { 69 super.onDestroy(); 70 gerenciador.close(); 71 } Nós vamos agora, substituir nosso objeto de modelo (e seu ArrayList associado) pelo banco de dados, utilizando também a classe Cursor do Android para controlar as instâncias. Primeiramente, vamos adicionar o método inserir() na classe GerenciadorRestaurantes: 27 public void inserir(String nome, String endereco, String tipo, String anotacoes) { 28 ContentValues valores = new ContentValues(); 29 30 valores.put("nome", nome); 31 valores.put("endereco", endereco); 32 valores.put("tipo", tipo); 33 valores.put("anotacoes", anotacoes); 34 35 getWritableDatabase().insert("restaurantes", "nome", valores); 36 } Neste método, recebemos os valores individuais dos campos que compõem a classe Restaurante e adicionamos a um objeto ContentValues, relacionando os valores com as colunas da tabela do nosso banco de dados. Por fim, obtemos uma instância do banco para escrita e inserimos os valores na tabela restaurantes. Agora, devemos realizar a chamada a este método ao pressionarmos o botão Salvar em nosso formulário (onSave). 93 private OnClickListener onSave = new OnClickListener() { 94 95 public void onClick(View arg0) { 96 String tipo = null; 97 98 switch (tipos.getCheckedRadioButtonId()) { 99 case R.id.rodizio: 100 tipo = "rodizio"; 101 break; 102 case R.id.fast_food: 103 tipo = "fast_food"; 104 break; 105 case R.id.a_domicilio: 106 tipo = "a_domicilio"; 107 break; 108 } 109 110 gerenciador.inserir(nome.getText().toString(), 111 endereco.getText().toString(), tipo, 112 anotacoes.getText().toString()); 113 } 114 }; Em seguida, vamos fazer com que a listagem de restaurantes seja realizada a partir do nosso banco de dados. Sevocê já mexeu com banco de dados no Java, já deve ter visto o funcionamento de um ResultSet. Ele armazena o conteúdo de uma consulta ao banco de dados. No Android, utilizamos a classe Cursor que tem funcionamento semelhante. Assim, vamos criar um método na classe GerenciadorRestaurantes para obter a lista de restaurantes salvos no banco. Vamos implementar o método obterTodos(): 39 public Cursor obterTodos() { 40 return getReadableDatabase().rawQuery("select id, nome, endereco, tipo, " + 41 "anotacoes FROM restaurantes ORDER BY nome", null); 42 } Precisaremos também de métodos que nos forneçam acesso a determinados campos do Cursor. Dessa forma, adicione estes métodos à classe GerenciadorRestaurantes: 44 public String obterNome(Cursor c) { 45 return c.getString(1); 46 } 47 48 public String obterEndereco(Cursor c) { 49 return c.getString(2); 50 } 51 52 public String obterTipo(Cursor c) { 53 return c.getString(3); 54 } 55 56 public String obterAnotacoes(Cursor c) { 57 return c.getString(4); 58 } Na nossa implementação atual, a classe Adaptador estende a classe ArrayAdapter, de forma que ela não conseguirá manipular os dados contidos no Cursor. Assim, modificaremos sua implementação para, então, estender não mais ArrayAdapter, mas sim CursorAdapter. 118 class AdaptadorRestaurante extends CursorAdapter { 119 AdaptadorRestaurante(Cursor c) { 120 super(ListaRestaurantes.this, c); 121 } 122 123 @Override 124 public void bindView(View view, Context context, Cursor cursor) { 125 ArmazenadorRestaurante armazenador = (ArmazenadorRestaurante) view.getTag(); 126 armazenador.popularFormulario(cursor, gerenciador); 127 } 128 129 @Override 130 public View newView(Context context, Cursor cursor, ViewGroup parent) { 131 LayoutInflater inflater = getLayoutInflater(); 132 View linha = inflater.inflate(R.layout.linha, parent, false); 133 ArmazenadorRestaurante armazenador = new ArmazenadorRestaurante(linha); 134 linha.setTag(armazenador); 135 return linha; 136 } 137 } Como pode ser percebido, a classe ArmazenadorRestaurante também necessita de alguns ajustes, para manipular o objeto da classe Cursor. Mas antes, vamos modificar o atributo listaRestaurantes do tipo List para Cursor. 28 Cursor listaRestaurantes; Agora, no método onCreate(), substitua o código que populava o antigo ArrayList por este: 53 listaRestaurantes = gerenciador.obterTodos(); 54 startManagingCursor(listaRestaurantes); 55 adaptador = new AdaptadorRestaurante(listaRestaurantes); 56 lista.setAdapter(adaptador); Prosseguindo, vamos atualizar a classe ArmazenadorRestaurante para trabalhar com o Cursor: 138 static class ArmazenadorRestaurante { 139 private TextView nome = null; 140 private TextView endereco = null; 141 private ImageView icone = null; 142 143 ArmazenadorRestaurante(View linha) { 144 nome = (TextView) linha.findViewById(R.id.titulo); 145 endereco = (TextView) linha.findViewById(R.id.endereco); 146 icone = (ImageView) linha.findViewById(R.id.icone); 147 } 148 149 void popularFormulario(Cursor c, GerenciadorRestaurantes gerenciador) { 150 nome.setText(gerenciador.obterNome(c)); 151 endereco.setText(gerenciador.obterEndereco(c)); 152 153 if (gerenciador.obterTipo(c).equals("rodizio")) { 154 icone.setImageResource(R.drawable.rodizio); 155 } else if (gerenciador.obterTipo(c).equals("fast_food")) { 156 icone.setImageResource(R.drawable.fast_food); 157 } else { 158 icone.setImageResource(R.drawable.entrega); 159 } 160 } 161 } Por fim, vamos modificar todas as referências ao ArrayList que tínhamos no nosso onListClick. 78 private OnItemClickListener onListClick = new OnItemClickListener() { 79 public void onItemClick(AdapterView<?> parent, View view, int position, 80 long id) { 81 listaRestaurantes.moveToPosition(position); 82 nome.setText(gerenciador.obterNome(listaRestaurantes)); 83 endereco.setText(gerenciador.obterEndereco(listaRestaurantes)); 84 anotacoes.setText(gerenciador.obterAnotacoes(listaRestaurantes)); 85 86 if (gerenciador.obterTipo(listaRestaurantes).equals("rodizio")) { 87 tipos.check(R.id.rodizio); 88 } else if (gerenciador.obterTipo(listaRestaurantes).equals("fast_food")) { 89 tipos.check(R.id.fast_food); 90 } else { 91 tipos.check(R.id.a_domicilio); 92 } 93 94 getTabHost().setCurrentTab(1); 95 } 96 }; Como último passo precisamos adicionar uma linha para que a lista seja atualizada a cada inserção. Insira a seguinte linha após a inserção lá no onSave: 117 listaRestaurantes.requery(); Pronto! Você já pode executar a sua versão persistente do Lista de Restaurantes! Segue a listagem completa da classe ListaRestaurantes: 1 package com.blogspot.flavioaf.restaurante; 2 3 import com.blogspot.flavioaf.restaurante.model.Restaurante; 4 import android.app.TabActivity; 5 import android.content.Context; 6 import android.database.Cursor; 7 import android.os.Bundle; 8 import android.view.LayoutInflater; 9 import android.view.View; 10 import android.view.View.OnClickListener; 11 import android.view.ViewGroup; 12 import android.widget.AdapterView; 13 import android.widget.AdapterView.OnItemClickListener; 14 import android.widget.Button; 15 import android.widget.CursorAdapter; 16 import android.widget.EditText; 17 import android.widget.ImageView; 18 import android.widget.ListView; 19 import android.widget.RadioGroup; 20 import android.widget.TabHost.TabSpec; 21 import android.widget.TextView; 22 public class ListaRestaurantes extends TabActivity { 23 24 Cursor listaRestaurantes; 25 AdaptadorRestaurante adaptador = null; 26 Restaurante atual = null; 27 28 EditText nome = null; 29 EditText endereco = null; 30 EditText anotacoes = null; 31 RadioGroup tipos = null; 32 GerenciadorRestaurantes gerenciador; 33 34 @Override 35 public void onCreate(Bundle savedInstanceState) { 36 super.onCreate(savedInstanceState); 37 setContentView(R.layout.main); 38 gerenciador = new GerenciadorRestaurantes(this); 39 40 nome = (EditText) findViewById(R.id.nome); 41 endereco = (EditText) findViewById(R.id.end); 42 anotacoes = (EditText) findViewById(R.id.anotacoes); 43 tipos = (RadioGroup) findViewById(R.id.tipos); 44 45 Button salvar = (Button) findViewById(R.id.salvar); 46 salvar.setOnClickListener(onSave); 47 48 ListView lista = (ListView) findViewById(R.id.restaurantes); 49 50 listaRestaurantes = gerenciador.obterTodos(); 51 startManagingCursor(listaRestaurantes); 52 adaptador = new AdaptadorRestaurante(listaRestaurantes); 53 lista.setAdapter(adaptador); 54 55 TabSpec descritor = getTabHost().newTabSpec("tag1"); 56 descritor.setContent(R.id.restaurantes); 57 descritor.setIndicator("Lista", 58 getResources().getDrawable(R.drawable.lista)); 59 getTabHost().addTab(descritor); 60 61 descritor= getTabHost().newTabSpec("tag2"); 62 descritor.setContent(R.id.detalhes); 63 descritor.setIndicator("Detalhes", 64 getResources().getDrawable(R.drawable.restaurante)); 65 getTabHost().addTab(descritor); 66 67 getTabHost().setCurrentTab(0); 68 69 lista.setOnItemClickListener(onListClick); 70 } 71 72 @Override 73 public void onDestroy() { 74 super.onDestroy(); 75 gerenciador.close(); 76 } 77 78 private OnItemClickListener onListClick = new OnItemClickListener() { 79 public void onItemClick(AdapterView<?> parent, View view, int position, 80 long id) { 81 listaRestaurantes.moveToPosition(position); 82 nome.setText(gerenciador.obterNome(listaRestaurantes)); 83 endereco.setText(gerenciador.obterEndereco(listaRestaurantes)); 84 anotacoes.setText(gerenciador.obterAnotacoes(listaRestaurantes)); 85 86 if (gerenciador.obterTipo(listaRestaurantes).equals("rodizio")) { 87 tipos.check(R.id.rodizio); 88 } else if (gerenciador.obterTipo(listaRestaurantes).equals("fast_food")) { 89 tipos.check(R.id.fast_food); 90 } else { 91 tipos.check(R.id.a_domicilio); 92 } 93 94 getTabHost().setCurrentTab(1); 95 } 96 }; 97 98 private OnClickListener onSave = new OnClickListener() { 99 100 public void onClick(View arg0) { 101 String tipo = null; 102 103 switch (tipos.getCheckedRadioButtonId()) { 104 case R.id.rodizio: 105 tipo = "rodizio"; 106 break; 107 case R.id.fast_food: 108 tipo = "fast_food"; 109 break; 110 case R.id.a_domicilio: 111 tipo = "a_domicilio"; 112 break; 113 } 114 115 gerenciador.inserir(nome.getText().toString(), endereco.getText() 116 .toString(), tipo, anotacoes.getText().toString()); 117 listaRestaurantes.requery(); 118 } 119 }; 120 121 class AdaptadorRestaurante extends CursorAdapter { 122 AdaptadorRestaurante(Cursor c) { 123 super(ListaRestaurantes.this, c); 124 } 125 126 @Override 127 public void bindView(View view, Context context, Cursor cursor) { 128 ArmazenadorRestaurante armazenador = (ArmazenadorRestaurante) view.getTag(); 129 armazenador.popularFormulario(cursor, gerenciador); 130 } 131 132 @Override 133 public View newView(Context context, Cursor cursor, ViewGroup parent) { 134 LayoutInflater inflater = getLayoutInflater(); 135 View linha = inflater.inflate(R.layout.linha, parent, false); 136 ArmazenadorRestaurante armazenador = new ArmazenadorRestaurante(linha); 137 linha.setTag(armazenador); 138 return linha; 139 } 140 } 141 142 static class ArmazenadorRestaurante { 143 private TextView nome = null; 144 private TextView endereco = null; 145 private ImageView icone = null; 146 147 ArmazenadorRestaurante(View linha) { 148 nome = (TextView) linha.findViewById(R.id.titulo); 149 endereco = (TextView) linha.findViewById(R.id.endereco); 150 icone = (ImageView) linha.findViewById(R.id.icone); 151 } 152 153 void popularFormulario(Cursor c, GerenciadorRestaurantes gerenciador) { 154 nome.setText(gerenciador.obterNome(c)); 155 endereco.setText(gerenciador.obterEndereco(c)); 156 157 if (gerenciador.obterTipo(c).equals("rodizio")) { 158 icone.setImageResource(R.drawable.rodizio); 159 } else if (gerenciador.obterTipo(c).equals("fast_food")) { 160 icone.setImageResource(R.drawable.fast_food); 161 } else { 162 icone.setImageResource(R.drawable.entrega); 163 } 164 } 165 } 166 } … e GerenciadorRestaurantes … 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.content.ContentValues; 4 import android.content.Context; 5 import android.database.Cursor; 6 import android.database.sqlite.SQLiteDatabase; 7 import android.database.sqlite.SQLiteOpenHelper; 8 9 public class GerenciadorRestaurantes extends SQLiteOpenHelper { 10 11 private static final String NOME_BANCO = "restaurantes.db"; 12 private static final int VERSAO_SCHEMA = 1; 13 14 public GerenciadorRestaurantes(Context context) { 15 super(context, NOME_BANCO, null, VERSAO_SCHEMA); 16 } 17 18 @Override 19 public void onCreate(SQLiteDatabase db) { 20 db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 21 " nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT);"); 22 } 23 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 27 } 28 29 public void inserir(String nome, String endereco, String tipo, String anotacoes) { 30 ContentValues valores = new ContentValues(); 31 32 valores.put("nome", nome); 33 valores.put("endereco", endereco); 34 valores.put("tipo", tipo); 35 valores.put("anotacoes", anotacoes); 36 37 getWritableDatabase().insert("restaurantes", "nome", valores); 38 } 39 40 public Cursor obterTodos() { 41 return getReadableDatabase().rawQuery("select _id, nome, endereco, tipo, " + 42 "anotacoes FROM restaurantes ORDER BY nome", null); 43 } 44 45 public String obterNome(Cursor c) { 46 return c.getString(1); 47 } 48 49 public String obterEndereco(Cursor c) { 50 return c.getString(2); 51 } 52 53 public String obterTipo(Cursor c) { 54 return c.getString(3); 55 } 56 57 public String obterAnotacoes(Cursor c) { 58 return c.getString(4); 59 } 60 } Editando Registros do Banco de Dados No último tutorial, tornamos o nosso aplicativo Lista de Restaurantes persistente. No tutorial de hoje, vamos aprimorar a forma como ele lida com o banco de dados, fazendo com que os registros inseridos possam ser editados. Além disso, também faremos uma mudança no visual da aplicação, retirando as abas e incluindo a tela de adição de restaurantes como uma opção no menu. O primeiro passo é criarmos uma nova Activity, que será onde ficará, a partir de agora, o nosso formulário de cadastro (e consequentemente, de edição). Separaremos as funções da nossa Activity inicial. Então, crie a classe FormularioDetalhes. 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 6 public class FormularioDetalhes extends Activity { 7 8 @Override 9 public void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 } 12 } Por enquanto, esta Activity não tem nenhum layout atribuído, já que ainda não criamos o seu layout. Antes de utilizar esta Activity em nosso projeto,precisamos declará-la no arquivo AndroidManifest.xml. Ele encontra-se na raiz da árvore do projeto. Abra-o e selecione a aba inferior AndroidManifest.xml para abri-lo para a edição. Dentro do nó application, adicionaremos um novo nó activity. 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.blogspot.flavioaf.restaurante" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk android:minSdkVersion="8" /> 8 9 <application 10 android:icon="@drawable/ic_launcher" 11 android:label="@string/app_name" > 12 <activity 13 android:label="@string/app_name" 14 android:name=".ListaRestaurantes" > 15 <intent-filter > 16 <action android:name="android.intent.action.MAIN" /> 17 18 <category android:name="android.intent.category.LAUNCHER" /> 19 </intent-filter> 20 </activity> 21 <activity android:name=".FormularioDetalhes"> 22 </activity> 23 </application> 24 25 </manifest> Prosseguindo, precisamos iniciar esta Activity quando clicarmos sobre um dos itens da lista. Assim, modifique o onListClick dessa forma: 80 private OnItemClickListener onListClick = new OnItemClickListener() { 81 public void onItemClick(AdapterView<?> parent, View view, int position, 82 long id) { 83 Intent i = new Intent(ListaRestaurantes.this, FormularioDetalhes.class); 84 startActivity(i); 85 } 86 }; Se quiser testar a aplicação, ela deve exibir uma tela vazia ao clicar em algum item da lista. Continuando, vamos agora fazer a migração do formulário para a nova Activity. Primeiramente, crie o arquivo form_detalhes.xml na pasta res/layout, podendo utilizar o main.xml como base para ele: 1 <?xml version="1.0" encoding="utf-8"?> 2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="wrap_content" 5 android:stretchColumns="1" > 6 <TableRow> 7 <TextView android:text="Nome:"/> 8 <EditText android:id="@+id/nome"/> 9 </TableRow> 10 <TableRow> 11 <TextView android:text="Endereço:"/> 12 <EditText android:id="@+id/end"/> 13 </TableRow> 14 <TableRow> 15 <TextView android:text="Tipo:"/> 16 <RadioGroup android:id="@+id/tipos"> 17 <RadioButton android:id="@+id/rodizio" 18 android:text="Rodízio"/> 19 <RadioButton android:id="@+id/fast_food" 20 android:text="Fast Food"/> 21 <RadioButton android:id="@+id/a_domicilio" 22 android:text="A Domicílio"/> 23 </RadioGroup> 24 </TableRow> 25 <TableRow> 26 <TextView android:text="Anotações:"/> 27 <EditText android:id="@+id/anotacoes" 28 android:singleLine="false" 29 android:gravity="top" 30 android:lines="2" 31 android:scrollHorizontally="false" 32 android:maxLines="2" 33 android:maxWidth="200sp"/> 34 </TableRow> 35 <Button android:id="@+id/salvar" 36 android:layout_width="fill_parent" 37 android:layout_height="wrap_content" 38 android:text="Salvar"/> 39 </TableLayout> Agora, volte a Activity FormularioDetalhes e adicione esta linha ao final do método onCreate: 11 setContentView(R.layout.form_detalhes); O próximo passo é mover toda a lógica do formulário para a nossa classe FormularioDetalhes. Primeiramente, adicione os atributos da classe que estavam na ListaRestaurantes para a FormularioDetalhes: 10 EditText nome = null; 11 EditText endereco = null; 12 EditText anotacoes = null; 13 RadioGroup tipos = null; 14 GerenciadorRestaurantes gerenciador; Agora, copie a busca aos widgets no formulário do método onCreate() do ListaRestaurantes para o FormularioDetalhes, no mesmo local. 22 gerenciador = new GerenciadorRestaurantes(this); 23 24 nome = (EditText) findViewById(R.id.nome); 25 endereco = (EditText) findViewById(R.id.end); 26 anotacoes = (EditText) findViewById(R.id.anotacoes); 27 tipos = (RadioGroup) findViewById(R.id.tipos); 28 29 Button salvar = (Button) findViewById(R.id.salvar); 30 salvar.setOnClickListener(onSave); Por fim, vamos copiar a implementação do nosso listener onSave para a classe FormularioDetalhes, porém retirando a parte que trata da inserção no banco de dados: 35 private OnClickListener onSave = new OnClickListener() { 36 37 public void onClick(View arg0) { 38 String tipo = null; 39 40 switch (tipos.getCheckedRadioButtonId()) { 41 case R.id.rodizio: 42 tipo = "rodizio"; 43 break; 44 case R.id.fast_food: 45 tipo = "fast_food"; 46 break; 47 case R.id.a_domicilio: 48 tipo = "a_domicilio"; 49 break; 50 } 51 } 52 }; Agora é hora de “limparmos” a interface original do aplicativo, no main.xml. Retiraremos o formulário que existia e as abas, além do ajuste no layout para abrigar somente a lista. O que nos resta é isso: 1 <?xml version="1.0" encoding="utf-8"?> 2 <ListView xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@android:id/list" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" /> Após isso, exclua a pasta layout_land e o arquivo main.xml dentro dela. No momento, ListaRestaurantes estende TabActivity. Porém, como modificamos a estrutura de nossa aplicação, isso não é mais necessário. Modifique a classe, de forma que ListaRestaurante estenda ListActivity. Em seguida, modifique o método onCreate() para retirar os códigos que diziam respeito às abas que utilizávamos: 34 @Override 35 public void onCreate(Bundle savedInstanceState) { 36 super.onCreate(savedInstanceState); 37 setContentView(R.layout.main); 38 39 gerenciador = new GerenciadorRestaurantes(this); 40 listaRestaurantes = gerenciador.obterTodos(); 41 startManagingCursor(listaRestaurantes); 42 adaptador = new AdaptadorRestaurante(listaRestaurantes); 43 setListAdapter(adaptador); 44 } Antes de seguirmos em frente, vamos analisar o que vamos fazer: o FormularioDetalhes será utilizado tanto na criação de novos restaurantes quando na edição de restaurantes já cadastrados. Além disso, ele precisa saber, quando estiver editando, qual restaurante se trata. Para isso, precisamos do identificador do restaurante (o campo _id do banco de dados). Primeiramente, vamos criar um atributo para a classe ListaRestaurantes: 27 public final static String _ID = "com.blogspot.flavioaf.restaurante._ID"; Após isso, vamos mudar o objeto onListClick para um onListItemClick(), onde vamos passar o valor do id para a outra Activity: 53 @Override 54 public void onListItemClick(ListView l, View v, int position, long id) { 55 Intent i = new Intent(ListaRestaurantes.this, FormularioDetalhes.class); 56 i.putExtra(_ID, String.valueOf(id)); 57 startActivity(i); 58 } Em seguida, adicione o seguinte atributo na classe FormularioDetalhes: 18 String idRestaurante = null; Este atributo será nulo se estivermos adicionando um novo restaurante,ou o identificador, caso estejamos editando um restaurante. Como criamos o GerenciadorRestaurantes no método onCreate(), precisamos encerrá-lo no método onDestroy(): 36 @Override 37 public void onDestroy() { 38 super.onDestroy(); 39 gerenciador.close(); 40 } Como agora temos o ID como controle dos restaurantes, precisamos de um método que nos retorne o Restaurante com o identificador correspondente. Adicione o seguinte método a classe GerenciadorRestaurantes: 61 public Cursor obterPorId(String id) { 62 String[] argumentos = {id}; 63 64 return getReadableDatabase().rawQuery( 65 "SELECT _id, nome, endereco, tipo, anotacoes " + 66 "FROM restaurantes WHERE _id = ?", argumentos); 67 } Agora, adicione o seguinte trecho ao fim do método onCreate() da classe FormularioDetalhes: 35 idRestaurante = getIntent().getStringExtra(ListaRestaurantes._ID); 36 37 if (idRestaurante != null) { 38 carregar(); 39 } Adicione, então, a implementação do método carregar(): 66 private void carregar() { 67 Cursor c = gerenciador.obterPorId(idRestaurante); 68 69 c.moveToFirst(); 70 nome.setText(gerenciador.obterNome(c)); 71 endereco.setText(gerenciador.obterEndereco(c)); 72 anotacoes.setText(gerenciador.obterAnotacoes(c)); 73 74 if (gerenciador.obterTipo(c).equals("rodizio")) { 75 tipos.check(R.id.rodizio); 76 } else if (gerenciador.obterTipo(c).equals("fast_food")) { 77 tipos.check(R.id.fast_food); 78 } else { 79 tipos.check(R.id.a_domicilio); 80 } 81 82 c.close(); 83 } Agora, vamos adicionar a opção de menu Adicionar para que possamos, a partir da listagem (que agora será a tela principal do aplicativo), inserir um novo restaurante. Modifique o arquivo opcao.xml que encontra-se em res/menu. 1 <?xml version="1.0" encoding="utf-8"?> 2 <menu xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:id="@+id/adicionar" 4 android:title="Adicionar" 5 android:icon="@drawable/adicionar"/> 6 </menu> Este item de mídia é padrão do Android, e pode ser encontrado no seu diretório de instalação do SDK, em platforms -> versão do Android que está usando (no meu caso, android-8 (ou 2.2)) -> data -> res -> tamanho de tela (podemos utilizar drawable-mdpi). Procure pelo ícone ic_menu_add.png. Copie-o e coloque na pasta res/drawable da sua aplicação. Para padronizar o nome, eu o renomeei para adicionar.png. Agora que já temos o menu, vamos ajustar a classe ListaRestaurantes para manipulá-lo corretamente. Vamos novamente implementar o método onCreateOptionsMenu(): 62 @Override 63 public boolean onCreateOptionsMenu(Menu menu) { 64 new MenuInflater(this).inflate(R.menu.opcao, menu); 65 66 return super.onCreateOptionsMenu(menu); 67 } E adicione, também, a implementação de onOptionsItemSelected(): 70 @Override 71 public boolean onOptionsItemSelected(MenuItem item) { 72 if (item.getItemId() == R.id.adicionar) { 73 startActivity(new Intent(ListaRestaurantes.this, FormularioDetalhes.class)); 74 return true; 75 } 76 77 return super.onOptionsItemSelected(item); 78 } Bom, lá na nossa classe GerenciadorRestaurantes, temos o método para inserir um novo restaurante, mas não temos o método para atualizar. Portanto, adicione o seguinte método à classe: 40 public void atualizar(String id, String nome, String endereco, String tipo, String anotacoes) { 41 ContentValues valores = new ContentValues(); 42 String[] argumentos = {id}; 43 44 valores.put("nome", nome); 45 valores.put("endereco", endereco); 46 valores.put("tipo", tipo); 47 valores.put("anotacoes", anotacoes); 48 49 getWritableDatabase().update("restaurantes", valores, "_id=?", argumentos); 50 } Por fim, precisamos adicionar o comportamento do botão Salvar no formulário. Modifique a implementação do onSave na classe FormularioDetalhes para verificar se a operação é de inclusão ou alteração: 47 private OnClickListener onSave = new OnClickListener() { 48 public void onClick(View arg0) { 49 String tipo = null; 50 51 switch (tipos.getCheckedRadioButtonId()) { 52 case R.id.rodizio: 53 tipo = "rodizio"; 54 break; 55 case R.id.fast_food: 56 tipo = "fast_food"; 57 break; 58 case R.id.a_domicilio: 59 tipo = "a_domicilio"; 60 break; 61 } 62 63 if (idRestaurante == null) { 64 gerenciador.inserir(nome.getText().toString(), 65 endereco.getText().toString(), 66 tipo, anotacoes.getText().toString()); 67 } else { 68 gerenciador.atualizar(idRestaurante, 69 nome.getText().toString(), 70 endereco.getText().toString(), 71 tipo, anotacoes.getText().toString()); 72 } 73 74 finish(); 75 } 76 }; Verifique se não há código duplicado, corrija os imports (Ctrl + Shift + O) e pronto! Já temos nossa aplicação funcionando! Pra simplificar, aí vão as listagens completas: Restaurante.java: 1 package com.blogspot.flavioaf.restaurante.model; 2 3 public class Restaurante { 4 5 private String nome = ""; 6 private String endereco = ""; 7 private String tipo = ""; 8 private String anotacoes = ""; 9 10 public String getNome() { 11 return nome; 12 } 13 14 public void setNome(String nome) { 15 this.nome = nome; 16 } 17 18 public String getEndereco() { 19 return endereco; 20 } 21 22 public void setEndereco(String endereco) { 23 this.endereco = endereco; 24 } 25 26 public String getTipo() { 27 return tipo; 28 } 29 30 public void setTipo(String tipo) { 31 this.tipo = tipo; 32 } 33 34 public String getAnotacoes() { 35 return anotacoes; 36 } 37 38 public void setAnotacoes(String anotacoes) { 39 this.anotacoes = anotacoes; 40 } 41 42 @Override 43 public String toString() { 44 return getNome(); 45 } 46 } FormularioDetalhes.java: 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.app.Activity; 4 import android.database.Cursor; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.view.View.OnClickListener; 8 import android.widget.Button; 9 import android.widget.EditText; 10 import android.widget.RadioGroup; 11 12 public class FormularioDetalhes extends Activity { 13 14 EditText nome = null; 15 EditText endereco = null; 16 EditText anotacoes = null; 17 RadioGroup tipos = null; 18 GerenciadorRestaurantes gerenciador; 19 String idRestaurante = null; 20 21 @Override 22 public void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.form_detalhes); 25 26 gerenciador = new GerenciadorRestaurantes(this); 27 28 nome = (EditText) findViewById(R.id.nome); 29 endereco = (EditText) findViewById(R.id.end);30 anotacoes = (EditText) findViewById(R.id.anotacoes); 31 tipos = (RadioGroup) findViewById(R.id.tipos); 32 33 Button salvar = (Button) findViewById(R.id.salvar); 34 salvar.setOnClickListener(onSave); 35 36 idRestaurante = getIntent().getStringExtra(ListaRestaurantes._ID); 37 38 if (idRestaurante != null) { 39 carregar(); 40 } 41 } 42 43 @Override 44 public void onDestroy() { 45 super.onDestroy(); 46 gerenciador.close(); 47 } 48 49 private OnClickListener onSave = new OnClickListener() { 50 51 public void onClick(View arg0) { 52 String tipo = null; 53 54 switch (tipos.getCheckedRadioButtonId()) { 55 case R.id.rodizio: 56 tipo = "rodizio"; 57 break; 58 case R.id.fast_food: 59 tipo = "fast_food"; 60 break; 61 case R.id.a_domicilio: 62 tipo = "a_domicilio"; 63 break; 64 } 65 66 if (idRestaurante == null) { 67 gerenciador.inserir(nome.getText().toString(), 68 endereco.getText().toString(), 69 tipo, anotacoes.getText().toString()); 70 } else { 71 gerenciador.atualizar(idRestaurante, 72 nome.getText().toString(), 73 endereco.getText().toString(), 74 tipo, anotacoes.getText().toString()); 75 } 76 77 finish(); 78 } 79 }; 80 81 private void carregar() { 82 Cursor c = gerenciador.obterPorId(idRestaurante); 83 84 c.moveToFirst(); 85 nome.setText(gerenciador.obterNome(c)); 86 endereco.setText(gerenciador.obterEndereco(c)); 87 anotacoes.setText(gerenciador.obterAnotacoes(c)); 88 89 if (gerenciador.obterTipo(c).equals("rodizio")) { 90 tipos.check(R.id.rodizio); 91 } else if (gerenciador.obterTipo(c).equals("fast_food")) { 92 tipos.check(R.id.fast_food); 93 } else { 94 tipos.check(R.id.a_domicilio); 95 } 96 97 c.close(); 98 } 99 } GerenciadorRestaurantes.java: 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.content.ContentValues; 4 import android.content.Context; 5 import android.database.Cursor; 6 import android.database.sqlite.SQLiteDatabase; 7 import android.database.sqlite.SQLiteOpenHelper; 8 9 public class GerenciadorRestaurantes extends SQLiteOpenHelper { 10 11 private static final String NOME_BANCO = "restaurantes.db"; 12 private static final int VERSAO_SCHEMA = 1; 13 14 public GerenciadorRestaurantes(Context context) { 15 super(context, NOME_BANCO, null, VERSAO_SCHEMA); 16 } 17 18 @Override 19 public void onCreate(SQLiteDatabase db) { 20 db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 21 " nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT);"); 22 } 23 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 27 } 28 29 public void inserir(String nome, String endereco, String tipo, String anotacoes) { 30 ContentValues valores = new ContentValues(); 31 32 valores.put("nome", nome); 33 valores.put("endereco", endereco); 34 valores.put("tipo", tipo); 35 valores.put("anotacoes", anotacoes); 36 37 getWritableDatabase().insert("restaurantes", "nome", valores); 38 } 39 40 public void atualizar(String id, String nome, String endereco, String tipo, String anotacoes) { 41 ContentValues valores = new ContentValues(); 42 String[] argumentos = {id}; 43 44 valores.put("nome", nome); 45 valores.put("endereco", endereco); 46 valores.put("tipo", tipo); 47 valores.put("anotacoes", anotacoes); 48 49 getWritableDatabase().update("restaurantes", valores, "_id=?", argumentos); 50 } 51 52 public Cursor obterTodos() { 53 return getReadableDatabase().rawQuery("select _id, nome, endereco, tipo, " + 54 "anotacoes FROM restaurantes ORDER BY nome", null); 55 } 56 57 public String obterNome(Cursor c) { 58 return c.getString(1); 59 } 60 61 public String obterEndereco(Cursor c) { 62 return c.getString(2); 63 } 64 65 public String obterTipo(Cursor c) { 66 return c.getString(3); 67 } 68 69 public String obterAnotacoes(Cursor c) { 70 return c.getString(4); 71 } 72 73 public Cursor obterPorId(String id) { 74 String[] argumentos = {id}; 75 76 return getReadableDatabase().rawQuery( 77 "SELECT _id, nome, endereco, tipo, anotacoes " + 78 "FROM restaurantes WHERE _id = ?", argumentos); 79 } 80 } ListaRestaurantes.java: 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.app.ListActivity; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.database.Cursor; 7 import android.os.Bundle; 8 import android.view.LayoutInflater; 9 import android.view.Menu; 10 import android.view.MenuInflater; 11 import android.view.MenuItem; 12 import android.view.View; 13 import android.view.ViewGroup; 14 import android.widget.CursorAdapter; 15 import android.widget.ImageView; 16 import android.widget.ListView; 17 import android.widget.TextView; 18 19 public class ListaRestaurantes extends ListActivity { 20 21 Cursor listaRestaurantes = null; 22 AdaptadorRestaurante adaptador = null; 23 public final static String _ID = "com.blogspot.flavioaf.restaurante._ID"; 24 GerenciadorRestaurantes gerenciador; 25 26 @Override 27 public void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.main); 30 31 gerenciador = new GerenciadorRestaurantes(this); 32 listaRestaurantes = gerenciador.obterTodos(); 33 startManagingCursor(listaRestaurantes); 34 adaptador = new AdaptadorRestaurante(listaRestaurantes); 35 setListAdapter(adaptador); 36 } 37 38 @Override 39 public void onDestroy() { 40 super.onDestroy(); 41 gerenciador.close(); 42 } 43 44 @Override 45 public void onListItemClick(ListView l, View v, int position, long id) { 46 Intent i = new Intent(ListaRestaurantes.this, FormularioDetalhes.class); 47 i.putExtra(_ID, String.valueOf(id)); 48 System.out.println("ID VALUE: " + id); 49 startActivity(i); 50 } 51 52 @Override 53 public boolean onCreateOptionsMenu(Menu menu) { 54 new MenuInflater(this).inflate(R.menu.opcao, menu); 55 56 return super.onCreateOptionsMenu(menu); 57 } 58 59 @Override 60 public boolean onOptionsItemSelected(MenuItemitem) { 61 if (item.getItemId() == R.id.adicionar) { 62 startActivity(new Intent(ListaRestaurantes.this, FormularioDetalhes.class)); 63 return true; 64 } 65 66 return super.onOptionsItemSelected(item); 67 } 68 69 class AdaptadorRestaurante extends CursorAdapter { 70 AdaptadorRestaurante(Cursor c) { 71 super(ListaRestaurantes.this, c); 72 } 73 74 @Override 75 public void bindView(View view, Context context, Cursor cursor) { 76 ArmazenadorRestaurante armazenador = (ArmazenadorRestaurante) view.getTag(); 77 armazenador.popularFormulario(cursor, gerenciador); 78 } 79 80 @Override 81 public View newView(Context context, Cursor cursor, ViewGroup parent) { 82 LayoutInflater inflater = getLayoutInflater(); 83 View linha = inflater.inflate(R.layout.linha, parent, false); 84 ArmazenadorRestaurante armazenador = new ArmazenadorRestaurante(linha); 85 linha.setTag(armazenador); 86 return linha; 87 } 88 } 89 90 static class ArmazenadorRestaurante { 91 private TextView nome = null; 92 private TextView endereco = null; 93 private ImageView icone = null; 94 95 ArmazenadorRestaurante(View linha) { 96 nome = (TextView) linha.findViewById(R.id.titulo); 97 endereco = (TextView) linha.findViewById(R.id.endereco); 98 icone = (ImageView) linha.findViewById(R.id.icone); 99 } 100 101 void popularFormulario(Cursor c, GerenciadorRestaurantes gerenciador) { 102 nome.setText(gerenciador.obterNome(c)); 103 endereco.setText(gerenciador.obterEndereco(c)); 104 105 if (gerenciador.obterTipo(c).equals("rodizio")) { 106 icone.setImageResource(R.drawable.rodizio); 107 } else if (gerenciador.obterTipo(c).equals("fast_food")) { 108 icone.setImageResource(R.drawable.fast_food); 109 } else { 110 icone.setImageResource(R.drawable.entrega); 111 } 112 } 113 } 114 } Configurando Preferências Vamos adicionar ao nosso aplicativo Lista de Restaurantes a opção do usuário configurar de que forma deve ocorrer a listagem dos restaurantes (nome, tipo, ordem alfabética, etc.). Pra começar, vamos criar um arquivo XML que tomará conta das configurações de preferência. Dessa forma, crie o arquivo preferencias.xml e coloque-o em res/xml (a pasta ainda não existe… então crie-a). O conteúdo dele será: 1 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 2 <ListPreference 3 android:key="listagem" 4 android:title="Modo de Listagem" 5 android:summary="Escolha o modo de listagem a ser utilizado" 6 android:entries="@array/nomes_ordenacao" 7 android:entryValues="@array/opcoes_ordenacao" 8 android:dialogTitle="Escolha o modo de listagem" /> 9 </PreferenceScreen> Em seguida, vamos criar o arquivo arrays.xml que definirá os dois arrays referenciados no XML definido acima. O arquivo arrays.xml deverá ser salvo na pasta res/values. Seu conteúdo é listado a seguir: 1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 <string-array name="nomes_ordenacao"> 4 <item>Por Nome, Ascendente</item> 5 <item>Por Nome, Descendente</item> 6 <item>Por Tipo</item> 7 <item>Por Endereço, Ascendente</item> 8 <item>Por Endereço, Descendente</item> 9 </string-array> 10 <string-array name="opcoes_ordenacao"> 11 <item>nome ASC</item> 12 <item>nome DESC</item> 13 <item>tipo, nome ASC</item> 14 <item>endereco ASC</item> 15 <item>endereco DESC</item> 16 </string-array> 17 </resources> O próximo passo é a criação da Activity responsável pelas preferências. Vamos criar a classe EdicaoPreferencias, que estenderá PreferenceActivity, dentro do pacote com.blogspot.flavioaf.restaurante: 1 package com.blogspot.flavioaf.restaurante; 2 3 import android.os.Bundle; 4 import android.preference.PreferenceActivity; 5 6 public class EdicaoPreferencias extends PreferenceActivity { 7 8 @Override 9 public void onCreate(Bundle savedInstanceState) { 10 super.onCreate(savedInstanceState); 11 12 addPreferencesFromResource(R.xml.preferencias); 13 } 14 } Também é necessário atualizar o arquivo AndroidManifest.xml, já que adicionamos uma nova Activity ao nosso projeto. 1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.blogspot.flavioaf.restaurante" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk android:minSdkVersion="8" /> 8 9 <application 10 android:icon="@drawable/ic_launcher" 11 android:label="@string/app_name" > 12 <activity 13 android:label="@string/app_name" 14 android:name=".ListaRestaurantes" > 15 <intent-filter > 16 <action android:name="android.intent.action.MAIN" /> 17 18 <category android:name="android.intent.category.LAUNCHER" /> 19 </intent-filter> 20 </activity> 21 <activity android:name=".FormularioDetalhes"> 22 </activity> 23 <activity android:name=".EdicaoPreferencias"> 24 </activity> 25 </application> 26 Continuando, vamos agora vincular a nossa nova Activity ao menu de opções. Primeiramente, vamos editar o arquivo opcao.xml, que se encontra em res/menu. 1 <?xml version="1.0" encoding="utf-8"?> 2 <menu xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item android:id="@+id/adicionar" 4 android:title="Adicionar" 5 android:icon="@drawable/adicionar"/> 6 <item android:id="@+id/prefs" 7 android:title="Configurações" 8 android:icon="@drawable/menu_preferencias"/> 9 </menu> O ícone referenciado é padrão do sistema e, como mostrado no último tutorial, pode ser encontrado na própria instalação da SDK. Este ícone utilizado se chama ic_menu_preferences (aqui foi renomeado para menu_preferencias em nosso projeto). Agora, vamos modificar o método onOptionsItemSelected na classe ListaRestaurantes para mapear esta nova opção adicionada ao menu: 58 @Override 59 public boolean onOptionsItemSelected(MenuItem item) { 60 if (item.getItemId() == R.id.adicionar) { 61 startActivity(new Intent(ListaRestaurantes.this, FormularioDetalhes.class)); 62 return true; 63 } else if (item.getItemId() == R.id.prefs) { 64 startActivity(new Intent(this, EdicaoPreferencias.class)); 65 return true; 66 } 67 68 return super.onOptionsItemSelected(item); 69 } Neste ponto, se você rodar a aplicação, já poderá conferir o menu: Agora, já que a parte visual está pronta, vamos aplicar a ordenação a nossa lista. Primeiramente, precisamos que o método obterTodos() da classe GerenciadorRestaurantes precisa receber o método de ordenação por parâmetro e aplicá-lo a SQL. Modifique-o para que fique assim: 52 public Cursor obterTodos(String ordenacao){ 53 return getReadableDatabase().rawQuery("select _id, nome, endereco, tipo, " + 54 "anotacoes FROM restaurantes ORDER BY " + ordenacao, null); 55 } Agora, precisamos de um atributo na classe ListaRestaurantes que nos permita saber a ordenação selecionada e aplicá-la a listagem. Adicione um atributo à classe chamado prefs do tipo SharedPreferences. 26 SharedPreferences prefs = null; Em seguida, adicione a inicialização do atributo no método onCreate(), próximo ao seu início. 34 prefs = PreferenceManager.getDefaultSharedPreferences(this); E modifique a chamada ao método obterTodos() logo em seguida: 36 listaRestaurantes = gerenciador.obterTodos(prefs.getString("ordenacao", "nome")); Por fim, vamos fazer com que seja aplicada as alterações realizadas pelo usuário em tempo de execução, já que, por enquanto, é necessário fechar a aplicação para que a nova ordenação tenha efeito. Adicione esta linha ao fim do método onCreate(): 40 prefs.registerOnSharedPreferenceChangeListener(prefListener); Em seguida, vamos criar o listener dentro da classe ListaRestaurantes: 1 private OnSharedPreferenceChangeListener prefListener = new OnSharedPreferenceChangeListener() { 2 3 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, 4 String key) { 5 if (key.equals("ordenacao")) { 6 7 } 8 } 9 }; Continuando, vamos isolar a inicialização da lista em um método à parte, deixando o método onCreate() mais limpo. Crie o método inicializarLista(): 77 private void inicializarLista() { 78 if (listaRestaurantes != null) { 79 stopManagingCursor(listaRestaurantes); 80 listaRestaurantes.close(); 81 } 82 83 listaRestaurantes = gerenciador.obterTodos(prefs.getString("listagem", "nome")); 84 startManagingCursor(listaRestaurantes); 85 adaptador = new AdaptadorRestaurante(listaRestaurantes); 86 setListAdapter(adaptador); 87 } Agora, referenciamos o recém-criado método inicializarLista() no método onCreate(): 30 @Override 31 public void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.main); 34 35 prefs = PreferenceManager.getDefaultSharedPreferences(this); 36 gerenciador = new GerenciadorRestaurantes(this); 37 inicializarLista(); 38 prefs.registerOnSharedPreferenceChangeListener(prefListener); 39 } E então, também fazemos uma chamada ao método inicializarLista() dentro do nosso prefListener: 86 private OnSharedPreferenceChangeListener prefListener = new OnSharedPreferenceChangeListener() { 87 88 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, 89 String key) { 90 if (key.equals("listagem")) { 91 inicializarLista(); 92 } 93 } 94 }; E pronto! Já temos o nosso aplicativo funcionando! Ajustando o Layout para Paisagem (II) Vamos tratar da adaptação do layout da aplicação Lista de Restaurantes também para o modo paisagem. Neste tutorial vamos fazer a rotação de uma maneira mais organizada, evitando alguns problemas. Primeiramente, precisamos de uma forma de armazenar os valores no caso da mudança de orientação do celular. Lá na classe FormularioDetalhes, vamos sobrescrever o método onSaveInstanceState(), que armazenará os valores pra gente. Adicione a seguinte implementação ao final da classe: 100 @Override 101 public void onSaveInstanceState(Bundle outState) { 102 super.onSaveInstanceState(outState); 103 104 outState.putString("nome", nome.getText().toString()); 105 outState.putString("endereco", endereco.getText().toString()); 106 outState.putString("anotacoes", anotacoes.getText().toString()); 107 outState.putInt("tipo", tipos.getCheckedRadioButtonId()); 108 } Pronto. Já fizemos com que os valores do formulário fossem salvos. Agora, vamos implementar o método onRestoreInstanceState() que devolverá os dados no formulário. 110 @Override 111 public void onRestoreInstanceState(Bundle savedInstanceState) { 112 super.onRestoreInstanceState(savedInstanceState); 113 114 nome.setText(savedInstanceState.getString("nome")); 115 endereco.setText(savedInstanceState.getString("endereco")); 116 anotacoes.setText(savedInstanceState.getString("anotacoes")); 117 tipos.check(savedInstanceState.getInt("tipo")); 118 } Por fim, vamos definir novamente o nosso layout em modo paisagem. Crie novamente a pasta (se você a excluiu) res/layout-land e crie o arquivo form_detalhes.xml. Se você ainda tem o arquivo main.xml lá, exclua-o. 1 <?xml version="1.0" encoding="utf-8"?> 2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="wrap_content" 5 android:stretchColumns="1,3" > 6 <TableRow> 7 <TextView android:text="Nome:"/> 8 <EditText android:id="@+id/nome" 9 android:layout_span="3" /> 10 </TableRow> 11 <TableRow> 12 <TextView android:text="Endereço:"/> 13 <EditText android:id="@+id/end" 14 android:layout_span="3" /> 15 </TableRow> 16 <TableRow> 17 <TextView android:text="Tipo:"/> 18 <RadioGroup android:id="@+id/tipos"> 19 <RadioButton android:id="@+id/rodizio" 20 android:text="Rodízio"/> 21 <RadioButton android:id="@+id/fast_food" 22 android:text="Fast Food"/> 23 <RadioButton android:id="@+id/a_domicilio" 24 android:text="A Domicílio"/> 25 </RadioGroup> 26 <TextView android:text="Anotações:"/> 27 <LinearLayout 28 android:layout_width="fill_parent" 29 android:layout_height="fill_parent" 30 android:orientation="vertical"> 31 <EditText android:id="@+id/anotacoes" 32 android:singleLine="false" 33 android:gravity="top" 34 android:lines="4" 35 android:scrollHorizontally="false" 36 android:maxLines="4" 37 android:maxWidth="140sp" 38 android:layout_width="fill_parent" 39 android:layout_height="wrap_content"/> 40 <Button android:id="@+id/salvar" 41 android:layout_width="fill_parent" 42 android:layout_height="wrap_content" 43 android:text="Salvar"/> 44 </LinearLayout> 45 </TableRow> 46 </TableLayout> E pronto! Quanto a tela de listagem, não precisamos alterar seu layout pois ele funciona bem tanto em modo retrato quanto paisagem. Observação Após a última atualização do plugin ADT do Eclipse, ele acusou alguns ‘warnings‘ nos layouts XML. Por enquanto não se preocupem com isso! Integrando Bibliotecas de Terceiros (Twitter) Veremos neste tutorial como integrar uma biblioteca externa ao nosso aplicativo em Android. Através dela, vamos vincular uma conta do Twitter ao restaurante e poderemos obter os últimos tweets referentes àquele restaurante. Para o acesso ao Twitter, utilizaremos a biblioteca twitter4j, que nos fornece acesso completo aos recursos da rede social. No tutorial vamos utilizar a versão 2.2.5 otimizada para Android (twitter4j-android-2.2.5), ou superior. O primeiro passo é adicionara conta do Twitter ao nosso modelo de dados. Isso implica em modificar a classe de persistência GerenciadorRestaurantes. Comece alterando o método onCreate() para abrigar o novo campo no banco de dados: 18 @Override 19 public void onCreate(SQLiteDatabase db) { 20 db.execSQL("CREATE TABLE restaurantes (_id INTEGER PRIMARY KEY AUTOINCREMENT," + 21 " nome TEXT, endereco TEXT, tipo TEXT, anotacoes TEXT, twitter TEXT);"); 22 } Além disso, vamos alterar a versão do Schema do banco, para que ele seja atualizado em versões anteriores: 12 private static final int VERSAO_SCHEMA = 2; Precisamos também atualizar o nosso método onUpdate(), que antes não fazia nada. Ele será executado se o usuário possuir a versão antiga do banco. Nesse caso, iremos adicionar a nova coluna twitter a tabela restaurantes. 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 db.execSQL("ALTER TABLE restaurantes ADD COLUMN twitter TEXT"); 27 } Para concluir as modificações nesta classe, vamos atualizar os método obterTodos(), obterPorId(), inserir() e atualizar(). Além disso, também vamos adicionar o método obterTwitter(): 29 public void inserir(String nome, String endereco, String tipo, String anotacoes, String twitter) { 30 ContentValues valores = new ContentValues(); 31 32 valores.put("nome", nome); 33 valores.put("endereco", endereco); 34 valores.put("tipo", tipo); 35 valores.put("anotacoes", anotacoes); 36 valores.put("twitter", twitter); 37 38 getWritableDatabase().insert("restaurantes", "nome", valores); 39 } 40 41 public void atualizar(String id, String nome, String endereco, String tipo, String anotacoes, String twitter) { 42 ContentValues valores = new ContentValues(); 43 String[] argumentos = {id}; 44 45 valores.put("nome", nome); 46 valores.put("endereco", endereco); 47 valores.put("tipo", tipo); 48 valores.put("anotacoes", anotacoes); 49 valores.put("twitter", twitter); 50 51 getWritableDatabase().update("restaurantes", valores, "_id=?", argumentos); 52 } 53 54 public Cursor obterTodos(String ordenacao) { 55 return getReadableDatabase().rawQuery("select _id, nome, endereco, tipo, " + 56 "anotacoes, twitter FROM restaurantes ORDER BY " + ordenacao, null); 57 } 58 59 public String obterTwitter(Cursor c) { 60 return c.getString(5); 61 } 62 63 public Cursor obterPorId(String id) { 64 String[] argumentos = {id}; 65 66 return getReadableDatabase().rawQuery( 67 "SELECT _id, nome, endereco, tipo, anotacoes, twitter " + 68 "FROM restaurantes WHERE _id = ?", argumentos); 69 } Pronto. Com relação aos dados do aplicativo, já estamos prontos. Vamos agora ajustar o formulário de detalhes, adicionando o campo Twitter em ambos. Lembre-se que, como temos duas versões deste formulário (res/layout e res/layout-land), precisaremos fazer a modificação em ambos. Primeiramente o formulário para modo retrato (res/layout/form_detalhes.xml): 1 <?xml version="1.0" encoding="utf-8"?> 2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="wrap_content" 5 android:stretchColumns="1" > 6 <TableRow> 7 <TextView android:text="Nome:"/> 8 <EditText android:id="@+id/nome"/> 9 </TableRow> 10 <TableRow> 11 <TextView android:text="Endereço:"/> 12 <EditText android:id="@+id/end"/> 13 </TableRow> 14 <TableRow> 15 <TextView android:text="Tipo:"/> 16 <RadioGroup android:id="@+id/tipos"> 17 <RadioButton android:id="@+id/rodizio" 18 android:text="Rodízio"/> 19 <RadioButton android:id="@+id/fast_food" 20 android:text="Fast Food"/> 21 <RadioButton android:id="@+id/a_domicilio" 22 android:text="A Domicílio"/> 23 </RadioGroup> 24 </TableRow> 25 <EditText android:id="@+id/anotacoes" 26 android:singleLine="false" 27 android:gravity="top" 28 android:lines="2" 29 android:scrollHorizontally="false" 30 android:maxLines="2" 31 android:maxWidth="200sp" 32 android:hint="Anotações"/> 33 <EditText android:id="@+id/twitter" 34 android:hint="Conta do Twitter" /> 35 <Button android:id="@+id/salvar" 36 android:layout_width="fill_parent" 37 android:layout_height="wrap_content" 38 android:text="Salvar"/> 39 </TableLayout> Basicamente, a única modificação é a adição do campo twitter e a remoção do TextView do campo anotações, exibido agora como o atributo hint. A mesma coisa fazemos no formulário do modo paisagem (res/layout- land/form_detalhes.xml): 1 <?xml version="1.0" encoding="utf-8"?> 2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="wrap_content" 5 android:stretchColumns="1,3" > 6 <TableRow> 7 <TextView android:text="Nome:"/> 8 <EditText android:id="@+id/nome" 9 android:layout_span="3" /> 10 </TableRow> 11 <TableRow> 12 <TextView android:text="Endereço:"/> 13 <EditText android:id="@+id/end" 14 android:layout_span="3" /> 15 </TableRow> 16 <TableRow> 17 <TextView android:text="Tipo:"/> 18 <RadioGroup android:id="@+id/tipos"> 19 <RadioButton android:id="@+id/rodizio" 20 android:text="Rodízio"/> 21 <RadioButton android:id="@+id/fast_food" 22 android:text="Fast Food"/> 23 <RadioButton android:id="@+id/a_domicilio" 24 android:text="A Domicílio"/> 25 </RadioGroup> 26 <TextView android:text="Anotações:"/> 27 <LinearLayout 28 android:layout_width="fill_parent" 29 android:layout_height="fill_parent" 30 android:orientation="vertical"> 31 <EditText android:id="@+id/anotacoes" 32 android:singleLine="false" 33 android:gravity="top" 34 android:lines="4" 35 android:scrollHorizontally="false" 36 android:maxLines="4" 37 android:maxWidth="140sp" 38 android:layout_width="fill_parent" 39 android:layout_height="wrap_content" 40 android:hint="Anotações" /> 41 <EditText android:id="@+id/twitter" 42 android:layout_width="fill_parent" 43 android:layout_height="wrap_content" 44 android:hint="Conta do Twitter"/> 45 <Button android:id="@+id/salvar" 46 android:layout_width="fill_parent" 47 android:layout_height="wrap_content" 48 android:text="Salvar"/> 49 </LinearLayout> 50 </TableRow> 51 </TableLayout> Agora, iremos adicionar o atributo twitter na classe FormularioDetalhes. 24 EditText twitter = null; Em seguida, no método onCreate(), obtemos o valor do formulário e o aplicamos ao atributo da classe: 39 twitter = (EditText) findViewById(R.id.twitter); E no método carregar(), configuramos o texto do EditText: 130 twitter.setText(gerenciador.obterTwitter(c));
Compartilhar