sábado, 4 de fevereiro de 2012

Android - Criando uma Agenda de Contatos com SQLite

Olá,

Hoje apresentarei para vocês como trabalhar com o Banco de Dados SQLite, serão apresentadas as operações básicas de um banco de dados (inclusão, exclusão, alteração e consulta) o projeto é uma Agenda de Contatos e ele está separado em camadas e cada camada está dentro de uma package, abaixo seguem as camadas utilizadas:

Camadas:
1. UI - User Interface, responsável pela interação entre o usuário e o sistema.
2. POJO - Plain Old Java Objects, responsável pelo transporte dos dados entre o BD, regras de negócio e o usuário.
2. DAO - Data Access Object, responsável pela persistência dos dados.

Abaixo segue uma figura com a estrutura e todos os arquivos que criaremos nesse projeto:


Além das camadas o projeto também utiliza um Adapter customizado (ContatoAdapter), para poder apresentar mais de uma linha de informação para cada item de nossa ListView, pois na mesma tela presentaremos todos os contatos cadastrados.

Abaixo vou apresentar todos os fontes e xml criados nesse projeto, todos os fontes estarão com vários comentários para auxiliar no bom entendimento do projeto.

Iremos começar pelos arquivos xml:

Arquivo arrays.xml, contém os itens que serão apresentados no ContextMenu:
<?xml version="1.0" encoding="utf-8"?>
<resources>    
    <string-array name="menu">        
        <item>Editar</item>        
        <item>Excluir</item>        
    </string-array>        
</resources>


Arquivo main.xml, layout da tela de consulta dos Contatos:
<?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:orientation="vertical" >

    <LinearLayout
        android:id="@+id/group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <Button
            android:id="@+id/add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Adicionar" 
            android:onClick="onClick"/>
    </LinearLayout>

    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
</LinearLayout>

Layout gráfico do main.xml


Arquivo contato.xml, layout da tela de inclusão e alteração do Contato:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
    <TextView 
        android:id="@+id/txtAux1" 
        android:text="Nome :"
        android:gravity="right"
        android:width="70dip" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content"
        android:layout_marginTop="10dip"
        >
    </TextView>
    <EditText 
        android:id="@+id/edtNome" 
        android:layout_marginLeft="5dip"
        android:text="Nome contato" 
        android:width="190dip"
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content"
        android:layout_toRightOf="@+id/txtAux1">
    </EditText>
    <TextView 
        android:id="@+id/txtAux3" 
        android:text="Endereço :" 
        android:width="70dip"
        android:gravity="right"
        android:layout_marginTop="10dip"
        android:layout_centerVertical="@+id/txtAux2" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:layout_below="@+id/edtNome"
        android:layout_alignParentLeft="true">
    </TextView>
    <EditText 
        android:id="@+id/edtEndereco" 
        android:layout_marginLeft="5dip"
        android:text="Endereco contato"
        android:width="190dip" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content"
        android:layout_below="@+id/edtNome" 
        android:layout_toRightOf="@+id/txtAux3">
    </EditText>
    <TextView 
        android:id="@+id/txtAux5" 
        android:text="Telefone :" 
        android:width="70dip"
        android:gravity="right"
        android:layout_marginTop="10dip"
        android:layout_centerVertical="@+id/txtAux2" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content" 
        android:layout_below="@+id/edtEndereco"
        android:layout_alignParentLeft="true">
    </TextView>
    <EditText 
        android:id="@+id/edtTelefone" 
        android:layout_marginLeft="5dip"
        android:text="Telefone contato"
        android:width="190dip" 
        android:layout_height="wrap_content" 
        android:layout_width="wrap_content"
        android:layout_below="@+id/edtEndereco" 
        android:layout_toRightOf="@+id/txtAux5">
    </EditText>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/edtTelefone" 
        android:gravity="center_vertical">    
        <Button 
            android:text="Confirmar" 
            android:layout_width="0dip"
            android:layout_weight="1.0"
            android:layout_height="wrap_content" 
            android:id="@+id/btnConfirmar"
            android:layout_below="@+id/edtTelefone" 
            android:onClick="btnConfirmar_click"
            android:layout_alignParentLeft="true">
        </Button>

        <Button 
            android:text="Cancelar" 
            android:layout_height="wrap_content"
            android:layout_width="0dip"
            android:layout_weight="1.0"
            android:id="@+id/btnCancelar"
            android:layout_below="@+id/edtTelefone" 
            android:onClick="btnCancelar_click"
            android:layout_alignParentRight="true">
        </Button>
    </LinearLayout>
    
</RelativeLayout>

Layout gráfico do contato.xml


Arquivo contato_row.xml, layout de item do ListView utilizado no ContatoAdapter:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/LinearLayout01"
    android:layout_width="fill_parent" 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:orientation="vertical">
       <RelativeLayout 
           android:id="@+id/LinearLayout02"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent">
            
        <TextView 
            android:layout_height="wrap_content"
            android:layout_width="fill_parent" 
            android:id="@+id/txtNome"
            android:text="Nome">
        </TextView>
        <TextView 
            android:layout_height="wrap_content" 
            android:layout_width="fill_parent"
            android:id="@+id/txtEndereco"
            android:text="Endereço"
            android:layout_below="@+id/txtNome">
        </TextView>
        <TextView 
            android:layout_height="wrap_content" 
            android:layout_width="fill_parent"
            android:id="@+id/txtTelefone"
            android:text="Telefone"
            android:layout_below="@+id/txtEndereco">
        </TextView>
    </RelativeLayout>
</LinearLayout>

Layout gráfico do contato_row.xml:


Arquivo AndroidManifest.xml, arquivo de configuração utilizado pelo Android:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="br.com.Agenda.UI"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".main"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ContatoUI" android:label="@string/app_name"/>

    </application>
</manifest>

Agora irei apresentar as classes que esse projeto irá utilizar.

Arquivo main.java, primeiro classe a ser executada no projeto:
package br.com.Agenda.UI;

import java.util.List;

import br.com.Agenda.DAO.ContatoAdapter;
import br.com.Agenda.DAO.ContatoDAO;
import br.com.Agenda.POJO.ContatoVO;

import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

public class main extends ListActivity{
    private static final int INCLUIR = 0;
    private static final int ALTERAR = 1;
 
    private ContatoDAO lContaoDAO; //instância responsável pela persistência dos dados
    List<ContatoVO> lstContatos;  //lista de contatos cadastrados no BD
    ContatoAdapter adapter;   //Adapter responsável por apresentar os contatos na tela
 
    boolean blnShort = false;
    int Posicao = 0;    //determinar a posição do contato dentro da lista lstContatos

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        lContaoDAO = new ContatoDAO(this);
        lContaoDAO.open();

        lstContatos = lContaoDAO.Consultar();

        adapter = new ContatoAdapter(this, lstContatos);
        setListAdapter(adapter);
  
        registerForContextMenu(getListView());
    }
 
    // Este evento será chamado pelo atributo onClick
    // que está definido no botão criado no arquivo main.xml
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.add:
            InserirContato();
            break;
        }
    }

    //Rotina executada quando finalizar a Activity ContatoUI
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        ContatoVO lAgendaVO = null;
        
        try
        {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == Activity.RESULT_OK)
            {
                //obtem dados inseridos/alterados na Activity ContatoUI
                lAgendaVO = (ContatoVO)data.getExtras().getSerializable("agenda");
                
                //o valor do requestCode foi definido na função startActivityForResult
                if (requestCode == INCLUIR)
                {
                    //verifica se digitou algo no nome do contato
                    if (!lAgendaVO.getNome().equals("")) 
                    {
                        //necessário abrir novamente o BD pois ele foi fechado no método onPause()
                        lContaoDAO.open();
                        
                        //insere o contato no Banco de Dados SQLite
                        lContaoDAO.Inserir(lAgendaVO);
                        
                        //insere o contato na lista de contatos em memória
                        lstContatos.add(lAgendaVO);
                    }
                }else if (requestCode == ALTERAR){
                    lContaoDAO.open();
                    //atualiza o contato no Banco de Dados SQLite
                    lContaoDAO.Alterar(lAgendaVO);
                    
                    //atualiza o contato na lista de contatos em memória
                    lstContatos.set(Posicao, lAgendaVO);
                }
                
                //método responsável pela atualiza da lista de dados na tela
                adapter.notifyDataSetChanged();
            }
        }
        catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }        
    }    
    
    private void InserirContato(){
        try
        {
            //a variável "tipo" tem a função de definir o comportamento da Activity
            //ContatoUI, agora a variável tipo está definida com o valor "0" para
            //informar que será uma inclusão de Contato
            
            Intent it = new Intent(this, ContatoUI.class);
            it.putExtra("tipo", INCLUIR);
            startActivityForResult(it, INCLUIR);//chama a tela e incusão
        }
        catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }            
    }    
    
    @Override
    protected void onResume() {
        //quando a Activity main receber o foco novamente abre-se novamente a conexão
        lContaoDAO.open();
        super.onResume();
    }

    @Override
    protected void onPause() {
        //toda vez que o programa peder o foco fecha-se a conexão com o BD
        lContaoDAO.close();
        super.onPause();
    }

    public void toast (String msg)
    {
        Toast.makeText (getApplicationContext(), msg, Toast.LENGTH_SHORT).show ();
    } 
    
    private void trace (String msg) 
    {
        toast (msg);
    }

    @Override    
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {        
        try
        {
            //Criação do popup menu com as opções que termos sobre
            //nossos Contatos
            
            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
            if (!blnShort)
            {
                Posicao = info.position;
            }
            blnShort = false;
            
            menu.setHeaderTitle("Selecione:");            
            //a origem dos dados do menu está definido no arquivo arrays.xml 
            String[] menuItems = getResources().getStringArray(R.array.menu);             
            for (int i = 0; i<menuItems.length; i++) {                
                menu.add(Menu.NONE, i, i, menuItems[i]);            
            }        
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }            
    }  

    
    //Este método é disparado quando o usuário clicar em um item do ContextMenu
    @Override    
    public boolean onContextItemSelected(MenuItem item) {       
        ContatoVO lAgendaVO = null;
        try
        {
            int menuItemIndex = item.getItemId();        

            //Carregar a instância POJO com a posição selecionada na tela
            lAgendaVO = (ContatoVO) getListAdapter().getItem(Posicao);
            
            if (menuItemIndex == 0){
                //Carregar a Activity ContatoUI com o registro selecionado na tela
                
                Intent it = new Intent(this, ContatoUI.class);
                it.putExtra("tipo", ALTERAR);
                it.putExtra("agenda", lAgendaVO);
                startActivityForResult(it, ALTERAR); //chama a tela de alteração
            }else if (menuItemIndex == 1){
                //Excluir do Banco de Dados e da tela o registro selecionado
                
                lContaoDAO.Excluir(lAgendaVO);
                lstContatos.remove(lAgendaVO);
                adapter.notifyDataSetChanged(); //atualiza a tela
            }
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }   
        return true;   
        
    }    

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        //por padrão o ContextMenu, só é executado através de LongClick, mas
        //nesse caso toda vez que executar um ShortClick, abriremos o menu
        //e também guardaremos qual a posição do itm selecionado
        
        Posicao = position;
        blnShort = true;
        this.openContextMenu(l);
    }
}

Arquivo ContaoUI.java, classe que irá controlar a tela de cadastro de contato:
package br.com.Agenda.UI;

import br.com.Agenda.POJO.ContatoVO;
import br.com.Agenda.UI.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class ContatoUI extends Activity {
    private static final int INCLUIR = 0;
    //private static final int ALTERAR = 1;
    ContatoVO lContatoVO; 
    EditText txtNome;
    EditText txtEndereco;
    EditText txtTelefone;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contato);
        
        try
        {
            
        
            final Bundle data = (Bundle) getIntent().getExtras();
            int lint = data.getInt("tipo");
            if (lint == INCLUIR)
            {
                //quando for incluir um contato criamos uma nova instância
                lContatoVO = new ContatoVO(); 
            }else{
                //quando for alterar o contato carregamos a classe que veio por Bundle
                lContatoVO = (ContatoVO)data.getSerializable("agenda");
            }
            
            //Criação dos objetos da Activity
            txtNome = (EditText)findViewById(R.id.edtNome);
            txtEndereco = (EditText)findViewById(R.id.edtEndereco);
            txtTelefone = (EditText)findViewById(R.id.edtTelefone);
    
            //Carregando os objetos com os dados do Contato
            //caso seja uma inclusão ele virá carregado com os atributos text
            //definido no arquivo main.xml
            txtNome.setText(lContatoVO.getNome());
            txtEndereco.setText(lContatoVO.getEndereco());
            txtTelefone.setText(lContatoVO.getTelefone());
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }             
    }

    public void btnConfirmar_click(View view)
    {
        try
        {
            //Quando confirmar a inclusão ou alteração deve-se devolver
            //o registro com os dados preenchidos na tela e informar
            //o RESULT_OK e em seguida finalizar a Activity
            
            
            Intent data = new Intent();
            lContatoVO.setNome(txtNome.getText().toString());
            lContatoVO.setEndereco(txtEndereco.getText().toString());
            lContatoVO.setTelefone(txtTelefone.getText().toString());
            data.putExtra("agenda", lContatoVO);
            setResult(Activity.RESULT_OK, data);    
            finish();
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }             
    }
    
    public void btnCancelar_click(View view)
    {
        try
        {
            //Quando for simplesmente cancelar a operação de inclusão
            //ou alteração deve-se apenas informar o RESULT_CANCELED
            //e em seguida finalizar a Activity
            
            setResult(Activity.RESULT_CANCELED);
            finish();
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }             
    }

    public void toast (String msg)
    {
        Toast.makeText (getApplicationContext(), msg, Toast.LENGTH_SHORT).show ();
    } 
    
    private void trace (String msg) 
    {
        toast (msg);
    }    

}


Arquivo ContatoVO.java, será a classe responsável pelo transporte dos dados do Contato entre o usuário e o Banco de Dados SQLite:
package br.com.Agenda.POJO;

import java.io.Serializable;

//Classe responsável pelo transporte dos dados entre a 
//interface(tela) e Banco de Dados
public class ContatoVO implements Serializable {
    private static final long serialVersionUID = 1L;
    private long id;
    private String nome;
    private String endereco;
    private String telefone;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String value) {
        this.nome = value;
    }

    public String getEndereco() {
        return endereco;
    }

    public void setEndereco(String value) {
        this.endereco = value;
    }
    
    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String value) {
        this.telefone = value;
    }
    // Will be used by the ArrayAdapter in the ListView
    @Override
    public String toString() {
        return nome;
    }
}


Arquivo BaseDAO.java, representará o DBHelper, responsável pela criação do próprio Banco de Dados:
package br.com.Agenda.DAO;

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

//Classe responsável pela criação do Banco de Dados e tabelas
public class BaseDAO extends SQLiteOpenHelper {

    public static final String TBL_AGENDA = "agenda";
    public static final String AGENDA_ID = "_id";
    public static final String AGENDA_NOME = "nome";
    public static final String AGENDA_ENDERECO = "endereco";
    public static final String AGENDA_TELEFONE = "telefone";

    private static final String DATABASE_NAME = "agenda.db";
    private static final int DATABASE_VERSION = 1;

    //Estrutura da tabela Agenda (sql statement)
    private static final String CREATE_AGENDA = "create table " +
        TBL_AGENDA + "( " + AGENDA_ID       + " integer primary key autoincrement, " + 
                            AGENDA_NOME     + " text not null, " +
                            AGENDA_ENDERECO + " text not null, " +
                            AGENDA_TELEFONE + " text not null);";

    public BaseDAO(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase database) {
        //Criação da tabela
        database.execSQL(CREATE_AGENDA);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //Caso seja necessário mudar a estrutura da tabela
        //deverá primeiro excluir a tabela e depois recriá-la
        db.execSQL("DROP TABLE IF EXISTS " + TBL_AGENDA);
        onCreate(db);
    }

}


Arquivo ContatoDAO.java, responsável pela persistência dos dados do Contato no Banc de Dados:
package br.com.Agenda.DAO;

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

import br.com.Agenda.POJO.ContatoVO;

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

public class ContatoDAO {

    private SQLiteDatabase database;
    private BaseDAO dbHelper;

    //Campos da tabela Agenda
    private String[] colunas = {BaseDAO.AGENDA_ID, 
                                BaseDAO.AGENDA_NOME,
                                BaseDAO.AGENDA_ENDERECO, 
                                BaseDAO.AGENDA_TELEFONE };

    public ContatoDAO(Context context) {
        dbHelper = new BaseDAO(context);
    }

    public void open() throws SQLException {
        database = dbHelper.getWritableDatabase();
    }

    public void close() {
        dbHelper.close();
    }

    public long Inserir(ContatoVO pValue) {
        ContentValues values = new ContentValues();

        //Carregar os valores nos campos do Contato que será incluído
        values.put(BaseDAO.AGENDA_NOME, pValue.getNome());
        values.put(BaseDAO.AGENDA_ENDERECO, pValue.getEndereco());
        values.put(BaseDAO.AGENDA_TELEFONE, pValue.getTelefone());
        
        return database.insert(BaseDAO.TBL_AGENDA, null, values);
    }
    
    
    public int Alterar(ContatoVO pValue) {
        long id = pValue.getId();
        ContentValues values = new ContentValues();
        
        //Carregar os novos valores nos campos que serão alterados
        values.put(BaseDAO.AGENDA_NOME, pValue.getNome());
        values.put(BaseDAO.AGENDA_ENDERECO, pValue.getEndereco());
        values.put(BaseDAO.AGENDA_TELEFONE, pValue.getTelefone());

        //Alterar o registro com base no ID
        return database.update(BaseDAO.TBL_AGENDA, values, BaseDAO.AGENDA_ID + " = " + id, null);
    }

    public void Excluir(ContatoVO pValue) {
        long id = pValue.getId();
        
        //Exclui o registro com base no ID
        database.delete(BaseDAO.TBL_AGENDA, BaseDAO.AGENDA_ID + " = " + id, null);
    }

    public List<ContatoVO> Consultar() {
        List<ContatoVO> lstAgenda = new ArrayList<ContatoVO>();

        //Consulta para trazer todos os dados da tabela Agenda ordenados pela coluna Nome
        Cursor cursor = database.query(BaseDAO.TBL_AGENDA, colunas, 
                null, null, null, null, BaseDAO.AGENDA_NOME);
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            ContatoVO lContatoVO = cursorToContato(cursor);
            lstAgenda.add(lContatoVO);
            cursor.moveToNext();
        }
        
        //Tenha certeza que você fechou o cursor
        cursor.close();
        return lstAgenda;
    }

    //Converter o Cursor de dados no objeto POJO ContatoVO
    private ContatoVO cursorToContato(Cursor cursor) {
        ContatoVO lContatoVO = new ContatoVO();
        lContatoVO.setId(cursor.getLong(0));
        lContatoVO.setNome(cursor.getString(1));
        lContatoVO.setEndereco(cursor.getString(2));
        lContatoVO.setTelefone(cursor.getString(3));
        return lContatoVO;
    }
}


Arquivo ContatoAdapter.java, classe responsável por apresentar na tela em forma de ListView os contatos que estão memória que foram extraídos do Banco de Dados SQLite:
package br.com.Agenda.DAO;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import br.com.Agenda.POJO.ContatoVO;
import br.com.Agenda.UI.R.id;
import br.com.Agenda.UI.R.layout;

public class ContatoAdapter extends BaseAdapter  {
    private Context context;

    private List<ContatoVO> lstContato;
    private LayoutInflater inflater;

    public ContatoAdapter(Context context, List<ContatoVO> listAgenda) {
        this.context = context;
        this.lstContato = listAgenda;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    
    //Atualizar ListView de acordo com o lstContato
    @Override
    public void notifyDataSetChanged() {   
        try{
            super.notifyDataSetChanged();
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }
    } 
        
    public int getCount() {
        return lstContato.size();
    }

    //Remover item da lista
    public void remove(final ContatoVO item) {
        this.lstContato.remove(item);
    } 
    
    //Adicionar item na lista
    public void add(final ContatoVO item) {
        this.lstContato.add(item);
    }    
    
    public Object getItem(int position) {
        return lstContato.get(position);
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup viewGroup) {
        try
        {
            
            ContatoVO contato = lstContato.get(position);

            //O ViewHolder irá guardar a instâncias dos objetos do estado_row
            ViewHolder holder;
            
            //Quando o objeto convertView não for nulo nós não precisaremos inflar
            //os objetos do XML, ele será nulo quando for a primeira vez que for carregado
            if (convertView == null) {
                convertView = inflater.inflate(layout.contato_row, null);
                
                //Cria o Viewholder e guarda a instância dos objetos
                holder = new ViewHolder();
                holder.tvNome = (TextView) convertView.findViewById(id.txtNome);
                holder.tvEndereco = (TextView) convertView.findViewById(id.txtEndereco);
                holder.tvTelefone = (TextView) convertView.findViewById(id.txtTelefone);
                
                convertView.setTag(holder);
            } else {
                //pega o ViewHolder para ter um acesso rápido aos objetos do XML
                //ele sempre passará por aqui quando,por exemplo, for efetuado uma rolagem na tela 
                holder = (ViewHolder) convertView.getTag();
            }

            holder.tvNome.setText(contato.getNome());
            holder.tvEndereco.setText(contato.getEndereco());
            holder.tvTelefone.setText(contato.getTelefone());

            return convertView;            
            
        }catch (Exception e) {
            trace("Erro : " + e.getMessage());
        }
        return convertView;
    }


    public void toast (String msg)
    {
        Toast.makeText (context, msg, Toast.LENGTH_SHORT).show ();
    } 
    
    private void trace (String msg) 
    {
        toast (msg);
    } 
    
    //Criada esta classe estática para guardar a referência dos objetos abaixo
    static class ViewHolder {
        public TextView tvNome;
        public TextView tvEndereco;
        public TextView tvTelefone;
    }    
}

Agora vamos ao resultado final desse projeto, abaixo serão apresentadas alguma figuras do comportamento do sistema.

1. Quando o sistema for executado pela primeira vez, ele deverá apresentar uma tela preta e apenas o botão para adicionar Contato.
2. Clicando no botão Adicionar o sistema deverá abrir a tela para inclusão de Contato, o usuário deverá informar os dados para cadastro e clicar em Confirmar.
3. Confirmando a inclusão do Contato, ao voltar para a primeira tela o sistema deverá apresentar automaticamente o contato cadastrado.
4. Clicando na primeira tela sobre um Contato cadastrado o sistema deverá apresentar uma caixa com as opções de Editar e Excluir o Contato ficando a cargo do usuário escolher a opção desejado.

Abaixo seguem as telas:

 




Vou terminando por aqui, caso tenham dúvidas, postem comentários, espero que tenham gostado do projeto apresentado ele é bem simples mas será a base para você criar projetos mais sofisticados.


Outras opções sobre ListView:

Parte 7 : http://escoladeandroid.blogspot.com.br/2012/03/android-listview-parte-7.html
Parte 8 : http://escoladeandroid.blogspot.com.br/2012/03/android-listview-parte-8.html

Carregando ListView com SqLite : http://escoladeandroid.blogspot.com.br/2012/04/android-carregando-listview-com-sqlite.html


Desenvolvi o jogo Circus Puzzle Free ele é um quebra-cabeça e está publicado no Android Market, agradeço a todos que baixarem, jogarem e comentarem.


Desenv com qualidade!

30 comentários:

Aquiniti disse...

Parabéns Márcio... muito bom o blog... ótimos conteudos e bem didatico... continue assim q seremos sempre gratos pelas dicas.
Você poderia postar em ZIP o fonte dos exemplos que você cria, seria legal para rodarmos com mais facilidade.

Abraço

Marcio de Souza disse...

Aquiniti,

Primeiramente, muito obrigado.

Eu preciso agora é aprender como postar um ZIP, tô muito cru ainda nessa parte do blog ...

Valeu e até mais...

Gabriela disse...

Muito bom o tutorial. Deu quase tudo certo, tive um problema com a classe "main.java"


nessa linha:

String[] menuItems = getResources().getStringArray(R.array.menu);


a mensagem de erro: "Create field Array in type R."


mas foi criado o array.xml
o que mais devo criar?

Marcio de Souza disse...

Gabriela,

Fiz uns testes aqui, e acho que você deve ter colocado o arquivo "arrays.xml" na pasta errada.

O local certo dele é dentro da pasta res\values, junto com o arquivo "string.xml" conforme a primeira figura do post.

Qualquer dúvida é só avisar.

Gabriela disse...

Sim, foi isso mesmo, tinha colocado na pasta de layout, e faltava fazer as alterações no Manifest. Agora deu tudo certo, rodou certinnho. Obrigada :D

Valtuir Jr. disse...

Excelente post Marcio, muito bom mesmo. Implementei este banco de dados e deu tudo certo. Só que agora preciso efetuar uma consulta como esta que vc postou manipulando o listView:

http://escoladeandroid.blogspot.com.br/2011/12/trabalhando-com-listview-parte-4.html

Só que com a base de dados que está no adapter:

lClienteDAO = new ClienteDAO(this);
lClienteDAO.open();
lstCliente = lClienteDAO.Consultar();
adapter = new ClienteAdapter(this, lstCliente);
setListAdapter(adapter);

Tem como dar uma ajuda

Marcio de Souza disse...

Valtuir,

Obrigado pela visita.

Você pode efetuar a consulta como o exemplo do projeto Agenda e depois converter em um array de string como no exemplo ListView 4.

Segue mais ou menos uma dica:

lstEstados = new String[lstCliente.Size]
Em seguida você faz um loop na lista de cliente e carrega o array e depois set o adapter.

Talvez não tenha ficado muito, mas em breve monto um post sobre isso.

Até mais...

Trabalhe e Ganhe disse...

Marcio

nao encontrei seu email então estou deixando o meu para a gente trocar algumas idéias, preciso fazer um programa de vendas simples, e gostaria de fazer alguma parceria, se tiver interesse meu email é cgsuporte arroba hotmail ponto com. Abraço.

Valtuir Jr. disse...

Marcio obrigado, mas como eu faço esse loop? poderia dar um passo a passo?

Quando eu efetuo a consulta e

Marcio de Souza disse...

Valtuir,

Fica meio difícil responder aqui pelo comentário, mas vou ver se sobra um tempinho e monto um exemplo para publicar como post, continue visitando que logo eu posto.

Obrigado

Paulo disse...

Oi. E possivel vc mandar um link do projeto zipado ?

Marcio de Souza disse...

Paulo,

Vou ficar devendo essa por enquanto, pois só tenho o fonte que está ai no blog, daqui alguns meses vou providenciar um site com repositório de dados.

Obrigado pela visita.

Paulo disse...

Blz Não esquenta... consegui seguindo seu tutorial.. Muito obrigado mesmo .. estava querendo apreender como criava uma camada de persistência para projeto android e esta me ajudando muito

Paulo disse...

Quem quiser o zip e importar segue abaixo o link
https://skydrive.live.com/?cid=2bb394695ae40ae8&resid=2BB394695AE40AE8!1007&id=2BB394695AE40AE8%211007

Marcio de Souza disse...

Paulo,

Obrigado pelo .zip!

Rodrigo Ramos disse...
Este comentário foi removido pelo autor.
Danilo Pinto da Silva disse...

Olá Marcio, parabéns pelo post. Seu blog tem me ajudado e muito!
Uma dúvida simples...

Qual é o significado do "l" em variável, como:
private ContatoDAO lContaoDAO;

E o significado do nome desta variável booleana tbm:
boolean blnShort = false;

Obrigado!

Emanuel Eloy disse...

Muito bom o material, explicativo e direto ao ponto.

Emanuel Eloy disse...
Este comentário foi removido pelo autor.
É De TI disse...

Eu refiz tudo,
não há erros nas classes e tals.
mas porem nao abre. ;/
E meu projeto pra amanha, ja fiz e refiz e ate agr nada. :S quem poder me ajudar, agradeço.
meu email
eduardo_baia@msn.com

Marcos Rosada disse...

Cara, é possível add o contato direto na agenda do Android?
Se sim, como?

Atenciosamente!

Diego Henrique disse...

ola marcio, segui seu tutorial, mas esta ocorrendo um erro que nao consigo arrumar

Description Resource Path Location Type
The project was not built due to "A resource exists with a different case: '/Agenda/bin/classes/br/com/agenda'.". Fix the problem, then try refreshing this project and building it since it may be inconsistent Agenda Unknown Java Problem

Alex Passos disse...

Olá.. fiz o exemplo e deu tudo certo... esta abrindo, salvando, excluir e alterando em parte.. ele alterar na hora mais quando fecho o aplicativo e volto as alterações sumiram

hayatu-123 disse...

Tente uma ferramenta gratuita - Valentina Studio. Produto surpreendente! IMO é o melhor SQLite Manager, para todas as plataformas. http://www.valentina-db.com/en/valentina-studio-overview

Paulo Cezar Bindaco disse...

Estou com o mesmo problema do Diego Henrique

Description Resource Path Location Type
The project was not built due to "A resource exists with a different case: '/Agenda/bin/classes/br/com/agenda'.". Fix the problem, then try refreshing this project and building it since it may be inconsistent Agenda Unknown Java Problem, como devo proceder para esta resolvendo esse problema ? desde de já agradeço a todos.

Diário de uma vida disse...

Olá Paulo,


Não sei se estou enviando esse um Email para seu endereço correto, mas se for você quero agradecer pelo Link abaixo, que me salvou, pois o projeto em questão estava dando erro para mim no seguinte:

Meu Eclipse não aceitava os "import" tipo: import br.com.Agenda.UI.R;

do arquivo ContaoUI.java, classe que irá controlar a tela de cadastro de contato e nem os
import br.com.Agenda.UI.R.id;

import br.com.Agenda.UI.R.layout;

de Arquivo ContatoAdapter.java, classe responsável por apresentar na tela em forma de ListView os contatos que estão memória que foram extraídos do Banco de Dados SQLite:





package br.com.Agenda.DAO;



Aí te pergunto se sabes porque isso ocorre. Será que o Ecliplse Juno é diferente nesse aspecto. Abaixo as informações do projeto:

Android - Criando uma Agenda de Contatos com SQLite



http://escoladeandroid.blogspot.com.br/2012/02/android-criando-uma-agenda-de-contatos.html


Teu comentário no Blog de Marcio de Souza da Escola Android:

"Paulo disse...
Quem quiser o zip e importar segue abaixo o link
https://skydrive.live.com/?cid=2bb394695ae40ae8&resid=2BB394695AE40AE8!1007&id=2BB394695AE40AE8%211007
19 de abril de 2012 05:12 "
Abs..... elfogaca@gmail.com

Deputado Marcos Santiago disse...

ola!,
gostaria de saber se é possível seguindo este modelo de aplicativo criar campo que digito um nome e ele me retorna os dados do contato.

Exatamente seria como a procv do excel
, eu digito um nome e clico em procurar, ai ele me retorna 8 informações do contato.

Maxwell Ferreira disse...

Márcio parabéns pelo post.

Seria possível melhorar o app adicionando a foto do contato?

Você poderia fazer um exemplo?

Desde já agradeço.

josue pereira disse...
Este comentário foi removido pelo autor.
Gabby Alegrim disse...

Olá, estou seguindo esse projeto, mas me falta apenas uma coisa, na ListView quero que fique o nome da pessoa na Lista, no meu não esta acontecendo isso, esta apenas o nome que coloquei lá no xml do contato_row, poderia me ajudar com isso ? Aguardo