sábado, 23 de maio de 2015

Carregando um banco de dados SQLite a partir de um arquivo CSV.

Em nosso primeiro artigo sobre banco de dados no Android, vimos como criar uma base de dados e inserimos alguns registros programaticamente. Mas no mundo real, ficaria inviável carregarmos uma grande massa de dados pelo método apresentado no tutorial.

Uma solução muito útil é obter os dados a partir de um arquivo texto, no formato CSV, ou seja, onde os campos são delimitados por vírgulas.

Para exemplificar, vamos, utilizando o "projetoBancoDeDados", criar um arquivo texto na pasta "assets", denominado "funcionarios.csv". O conteúdo do arquivo deve ser como o definido abaixo:

1,JOSÉ SILVA,Auxiliar administrativo,985.63
2,MARIA JOSÉ,Cozinheira,627.35
3,HENRIQUE ARIAS,Motorista,1239.56
4,ASTOLFO GOMES,Zelador,832.57
5,MARINA ZENAIDE,Gerente,5342.00
6,PEDRO TELEZ,Porteiro,854.34
7,AMÉRICO TOLEDO,Balconista,700.88
8,ANTONIO FARIA,Recepcionista,800.00
9,ANA JARDIM,Gerente,6543.36
10,MARCELE PEREIRA,Diretora,9834.23
11,DANIELE GOMEZ,Coordenadora,3454.67
12,AMANDA AGUIAR,Faxineira,643.36
13,ROBERTO DANTAS,Programador,2345.65
14,ANDERSON CLEITON,Analista de sistemas,4534.87
15,CLÉCIO GOMES,Instrutor,3234.88
 


A ideia é obter cada linha do arquivo CSV acima e extrair os campos um a um (id, nome, cargo e salário). E a partir daí gravar os dados no banco.

Para isto, vamos agora abrir nossa classe "MainActivity.java" e substituir seu conteúdo integralmente por:

package com.example.projetobancodedados;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

public class MainActivity extends Activity
{
     
   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      // Realiza uma carga inicial do banco de dados, à partir de um arquivo texto CSV.
      fazerCargaInicial(this);

      // Obtem lista de todos os funcionários da base.
      ArrayList<FuncionarioDTO> listaDeFuncionarios = new ArrayList<FuncionarioDTO>();
      FuncionarioDAO funcionarioDAO = new FuncionarioDAO(this);
      funcionarioDAO.open();
      listaDeFuncionarios=funcionarioDAO.getAll();
      funcionarioDAO.close();
     
      // Mostra no LogCat todos os funcionários da base.
      for(FuncionarioDTO f: listaDeFuncionarios)
      {
        
         Log.i("debug", f.getId() + "");
         Log.i("debug", f.getNome());
         Log.i("debug", f.getCargo());
         Log.i("debug", f.getSalario() + "");
        
      }
     
   }

   @Override
   protected void onResume()
   {
      super.onResume();
   }

   @Override
   protected void onPause()
   {
      super.onPause();
   }

  
   public void fazerCargaInicial(Context ctx)
   {
      // Obtém DAO do funcionário.
      FuncionarioDAO db = new FuncionarioDAO(ctx);
      db.open();

      try
      {
                 
         AssetManager assetManager= ctx.getAssets();
         InputStreamReader is=new InputStreamReader(assetManager.open("funcionarios.csv"), "ISO8859-1");
         BufferedReader reader=new BufferedReader(is);

         String linha;
         String id, nome, cargo, salario;

         while ((linha=reader.readLine()) != null)
         {
            String[] dadosDaLinha=linha.split(",");
            FuncionarioDTO funcionarioDTO = new FuncionarioDTO();
           
            funcionarioDTO.setId(Integer.parseInt(dadosDaLinha[0]));
            funcionarioDTO.setNome(dadosDaLinha[1]);
            funcionarioDTO.setCargo(dadosDaLinha[2]);
            funcionarioDTO.setSalario(Double.parseDouble(dadosDaLinha[3]));
           
            db.save(funcionarioDTO);
           
         }
         is.close();
      }
      catch (IOException ex)
      {
         Log.i("debug", "erro" + ex.getMessage() );
      }

      db.close();
     
   }

}


O pulo do gato aqui está no método "fazerCargaInicial()", que lê o arquivo CSV da pasta "Assets" e extrai os dados, gravando-os posteriormente na base. Simples.

Dica boa, hein? Gostou? Comente!

sexta-feira, 22 de maio de 2015

Como fazer o banco de dados residir no SDCARD do Android.

Alô pessoal! Estive um pouco ocupado nos últimos dias e por isso não tive tempo para escrever no blog. No entanto, trago hoje um assunto deveras interessante e útil:

Como fazer o banco de dados de nosso app residir no SDCARD, isto é, na memória externa do dispositivo.

A primeira pergunta é: por quê?

A resposta é simples: O Android possui uma memória interna, inerente ao aparelho, e, opcionalmente, uma memória externa, provida através da adição de um cartão extra, conhecido como SDCARD.

Ocorre que, via de regra, a memória interna é menor do que a externa. Principalmente nos aparelhos mais antigos, onde ela pode ser de alguns poucos megabytes. Quando muito 1 ou 2 gigas.

Certamente o leitor em algum momento já se deparou com a mensagem de erro "Internal memory full" ou "Database Storage full", ou algo similar, na tela de seu Android. Isso acontece porque a grande maioria dos aplicativos usa a localização padrão para criar seus bancos de dados: a pasta "/data/data/nomeDoApp". Afinal, se não informarmos nada na classe SQLiteHelper, ela criará o banco nesse local.

Pois bem, é uma prática inteligente priorizarmos o SDCARD como local de criação do banco de dados de nosso app, tomando o cuidado, é claro, de verificarmos primeiro se ele está presente.

Isto é feito da seguinte forma:

import android.os.Environment;
// Verifica se existe SDCARD montado no sistema.
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
   // Realiza alguma operação em caso afirmativo.
}

Agora que já sabemos como verificar se há SDCARD instalado no sistema, vamos modificar nosso projetoBancoDeDados para que os construtores das classes que criam nosso banco façam esta checagem e, no caso da existência do SDCARD, gravar o banco de dados nele.

Primeiro vamos alterar o construtor da classe "FuncionarioDAO", para que fique assim:

  public FuncionarioDAO(Context context)
   {
      String path="";
      String FILE_DIR="projetoBancoDeDados";

      // Se houver SDCARD montado, então cria a base no SDCARD. Caso contrário,
      // na memória interna.
      if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
      {
         path=Environment.getExternalStorageDirectory().toString() + File.separator + FILE_DIR + File.separator;
      }
     
      dbHelper=new MySQLiteHelper(context, path);
   }

Como estamos agora passando mais um parâmetro para o construtor da classe "mySQLiteHelper", ou seja, além do contexto, estamos também passando o caminho onde o banco deve ser criado (path) então temos que mudar o construtor desta última classe também:

   public MySQLiteHelper(Context context, String path)
   {
      super(context, path + DATABASE_NAME, null, DATABASE_VERSION);
   } 

A mudança é bastante simples. Só estamos acrescentando o caminho, informando assim, onde o banco deve ser criado.

Precisaremos também, informar no arquivo "AndroidManifest.xml" que utilizaremos a permissão de escrita no armazenamento externo:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />



Executando novamente o código será possível notar que o banco agora está residindo na pasta "/mnt/sdcard/projetoBancoDeDados". E economizando um bocado de memória interna!

That´s it. Stay tuned!

segunda-feira, 18 de maio de 2015

Trabalhando com banco de dados SQLite no Android

Uma das coisas que me deixaram de queixo caído quando comecei a aprender a desenvolver para Android foi saber que todo celular com esse sistema operacional vem de fábrica com um servidor de banco de dados relacional completo embutido, gratuito e pronto para o uso. Quem trabalha com informática há muitos anos e já teve experiência com grandes servidores de banco de dados Oracle ou SQL Server compartilhará da mesma opinião.

O Android vem com um banco de dados conhecido como "SQLite". Ele está embutido em todo dispositivo Android. É um banco de dados "open source" e suporta as ferramentas relacionais tradicionais, queries em SQL e transações. Requer pouquíssima memória para executar (cerca de 250KB). 

Para que o desenvolvedor utilize SQLite não é necessário nenhum procedimento de instalação ou mesmo privilégios de administrador de banco de dados. O banco apenas está lá, aguardando para ser utilizado.

Só é preciso definir algumas instruções SQL para criar e atualizar a base de dados. À partir daí a plataforma Android administra o banco automaticamente.

Acessar o banco de dados SQLite envolve a utilização do sistema de arquivos, o que pode trazer algum overhead. Portanto, acessos à base de dados em aplicativos reais devem ser feitos de forma assíncrona.

Quando uma aplicação cria um banco de dados, por padrão ele será armazenado no seguinte diretório (da memória interna do aparelho):

data/data/[nomeDoApp]/databases/[nomeDoArquivo.db]

A criação do banco de dados em aplicativos Android é feita utilizando-se a classe "SQLiteOpenHelper".

Vamos então, começar o trabalho:

Primeiro vamos criar um projeto chamado "projetoBancoDeDados". Então, na pasta "src" vamos criar uma classe chamada "MySQLiteHelper", que será responsável por criar a base (caso ela ainda não exista) ou atualizá-la (caso tenhamos modificado o valor da versão do banco de dados), com o seguinte conteúdo:

package com.example.projetobancodedados;

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

public class MySQLiteHelper extends SQLiteOpenHelper
{

   // Define o nome da base de dados e a versão.
   private static final String DATABASE_NAME="banco.db";
   private static final int DATABASE_VERSION=1;

   // Script de criação do banco de dados.
   private static final String DATABASE_CREATE = " create table if not exists funcionario (" +
                                                                        " id integer primary key autoincrement," +
                                                                        " nome varchar(100), " +
                                                                        " cargo varchar(50), " +
                                                                        " salario real " +
                                                                        " );";


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

   @Override
   public void onCreate(SQLiteDatabase database)
   {
      // Cria o banco de dados, executando o SQL.
      database.execSQL(DATABASE_CREATE);
   }

   @Override
   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
   {
      // Elimina e recria a base, caso o valor da versão tenha sido alterado.
      db.execSQL("DROP TABLE IF EXISTS funcionario ");
      onCreate(db);
   }

}


O próximo passo é criar uma classe "FuncionarioDTO", que definirá a estrutura que utilizaremos para armazenar um funcionário no banco de dados e também recuperá-lo:


package com.example.projetobancodedados;

public class FuncionarioDTO
{
   private int id;
   private String nome;
   private String cargo;
   private Double salario;

   public int getId()
   {
      return id;
   }
   public void setId(int id)
   {
      this.id=id;
   }
   public String getNome()
   {
      return nome;
   }
   public void setNome(String nome)
   {
      this.nome=nome;
   }
   public String getCargo()
   {
      return cargo;
   }
   public void setCargo(String cargo)
   {
      this.cargo=cargo;
   }
   public Double getSalario()
   {
      return salario;
   }
   public void setSalario(Double salario)
   {
      this.salario=salario;
   }   
}


Agora vamos criar a classe mais importante, que será o nosso ponto de acesso ao banco de dados (DAO). Vamos nomear essa classe "FuncionarioDAO". Definiremos aqui os métodos "save", "delete" e "getAll", que serão responsáveis em gravar e apagar um funcionário, e obter todos os registros de funcionário:



package com.example.projetobancodedados;


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

public class FuncionarioDAO
{

   private SQLiteDatabase database;
   private MySQLiteHelper dbHelper;

   public FuncionarioDAO(Context context)
   {
      dbHelper=new MySQLiteHelper(context);
   }


   // Abre o banco de dados.
   public void open() throws SQLException
   {
      database=dbHelper.getWritableDatabase();
   }

   
   // Fecha o banco de dados.
   public void close()
   {
      dbHelper.close();
   }

   
   // Insere o funcionário, caso ainda não exista. Ou atualiza.
   public boolean save(FuncionarioDTO funcionario)
   {
      ContentValues values=new ContentValues();
      values.put("id", funcionario.getId());
      values.put("nome", funcionario.getNome());
      values.put("cargo", funcionario.getCargo());
      values.put("salario", funcionario.getSalario());

      Cursor cursor=database.rawQuery("SELECT 1 from funcionario where id = " + funcionario.getId(), null);

      if (cursor.getCount() == 0)
      {
         database.insert("funcionario", null, values);
      }
      else
      {
         database.update("funcionario", values, " id = ? ", new String[] {  String.valueOf(funcionario.getId()) });
      }

      return true;

   }

   
   // Apaga um registro de funcionário.
   public void delete(FuncionarioDTO funcionario)
   {
      int id=funcionario.getId();
      database.delete("funcionario", " id = " + id, null);
   }

   
   // Retorna uma lista com todos os funcionários.
   public ArrayList<FuncionarioDTO> getAll()
   {
      ArrayList<FuncionarioDTO> funcionarios=new ArrayList<FuncionarioDTO>();

      Cursor cursor=database.rawQuery("SELECT * from funcionario;", null);

      cursor.moveToFirst();
      while (!cursor.isAfterLast())
      {
         FuncionarioDTO funcionario=cursorToFuncionario(cursor);
         funcionarios.add(funcionario);
         cursor.moveToNext();
      }

      // Fecha o cursor.
      cursor.close();

      // Retorna lista de funcionários.
      return funcionarios;

   }

   // Converte de cursor para FuncionarioDTO.
   private FuncionarioDTO cursorToFuncionario(Cursor cursor)
   {
      FuncionarioDTO funcionario=new FuncionarioDTO();
      funcionario.setId(cursor.getInt(0));
      funcionario.setNome(cursor.getString(1));
      funcionario.setCargo(cursor.getString(2));
      funcionario.setSalario(cursor.getDouble(3));

      return funcionario;
   }

}


Por último, vamos alterar a classe "MainActivity" para que fique como o código descrito abaixo. O que estamos fazendo aqui é incluir três funcionários (JOSÉ, MARIA E HENRIQUE) e, logo em seguida, apagar o registro de um deles (MARIA), para exemplificar as operações. Você poderá modificar o código para experimentar livremente a inclusão de outros funcionários e a alteração dos dados dos já existentes.

Também colocamos a funcionalidade de listar todos os registros existentes. Sendo que, neste exemplo, a saída é realizada no LogCat (devendo existir uma tag "debug" para visualização no Eclipse).

Observe com especial atenção os métodos onResume() e onPause(). É muito importante fechar o banco e reabri-lo nos momentos corretos.


package com.example.projetobancodedados;

import java.util.ArrayList;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;

public class MainActivity extends Activity
{

   // Define o ponto de acesso ao banco (DAO) e o objeto da classe descritiva de funcionário.
   private FuncionarioDAO funcionarioDAO;
   private FuncionarioDTO funcionario;
   
   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      // Abre o banco de dados de funcionário.
      funcionarioDAO=new FuncionarioDAO(this);
      funcionarioDAO.open();

      // Instancia em memória um novo funcionário e preenche seus dados.
      funcionario = new FuncionarioDTO();
      funcionario.setId(1);
      funcionario.setNome("JOSÉ SILVA");
      funcionario.setCargo("Auxiliar administrativo");
      funcionario.setSalario(985.63);
      // Grava no banco (insere ou atualiza) o funcionário.
      funcionarioDAO.save(funcionario);

      
      // Instancia outro funcionário.
      funcionario = new FuncionarioDTO();
      funcionario.setId(2);
      funcionario.setNome("MARIA JOSÉ");
      funcionario.setCargo("Cozinheira");
      funcionario.setSalario(627.35);    
      // Grava no banco (insere ou atualiza) o funcionário.
      funcionarioDAO.save(funcionario);


      // Instancia outro funcionário.
      funcionario = new FuncionarioDTO();
      funcionario.setId(3);
      funcionario.setNome("HENRIQUE ARIAS");
      funcionario.setCargo("Motorista");
      funcionario.setSalario(1239.56);
      // Grava no banco (insere ou atualiza) o funcionário.
      funcionarioDAO.save(funcionario);
      
      
      // Apaga um funcionário.
      funcionario.setId(2);
      funcionarioDAO.delete(funcionario);
      

      // Obtem lista de todos os funcionários da base.
      ArrayList<FuncionarioDTO> listaDeFuncionarios = new ArrayList<FuncionarioDTO>();
      listaDeFuncionarios=funcionarioDAO.getAll();
      
      // Mostra no LogCat todos os funcionários da base.
      for(FuncionarioDTO f: listaDeFuncionarios)
      {
         
         Log.i("debug", f.getId() + "");
         Log.i("debug", f.getNome());
         Log.i("debug", f.getCargo());
         Log.i("debug", f.getSalario() + "");
         
      }
      
   }

   @Override
   protected void onResume()
   {
      // Reabre o banco.
      funcionarioDAO.open();
      super.onResume();
   }

   @Override
   protected void onPause()
   {
      // Fecha o banco.
      funcionarioDAO.close();
      super.onPause();
   }

}


Depois que executarmos o código pela primeira vez, poderemos visualizar as tabelas do banco de dados e seu conteúdo através do próprio Eclipse (disponível apenas para quem baixou nosso ambiente de desenvolvimento), utilizando o "Questoid SQLite browser", que é um plugin que já incluímos no nosso ambiente.

Para isto, devemos utilizar a aba "DDMS" no Eclipse, conforme a imagem:




E então devemos clicar no nome do dispositivo e navegar até o arquivo de banco de dados criado pelo nosso aplicativo na pasta:

"data/data/com.example.projetobancodedados/banco.db"

Clicando no ícone azul (com desenho de um banco de dados) no canto superior direito da tela, teremos acesso ao "Questoid SQLite browser" na parte de baixo da tela. Ele nos permite ver tanto a estrutura do banco de dados quanto os valores, conforme ilustram as imagens abaixo:






Para quem preferir baixar o projeto completo, ei-lo aqui para download.

Com isto, demonstramos com sucesso, de forma introdutória, como utilizar banco de dados local no Android. O artigo, obviamente, não esgota o assunto, mas fornece ao leitor os subsídios necessários para ter uma ideia a respeito do uso do banco neste ambiente.

Isso abre novas perspectivas, permitindo a criação de apps ainda mais úteis. Também significa novas possibilidades de otimização (utilizando o banco local como cache) e, inclusive, o desenvolvimento de aplicativos offline (atualizando seus dados somente na presença de sinal de Internet).

Espero que tenha sido proveitoso. Um forte abraço e até a próxima!

sexta-feira, 15 de maio de 2015

Trabalhando com listViews (Caixas de listas)

Neste tutorial aprenderemos como utilizar as famosas "caixas de listas" (listViews). A ideia é apresentar ao usuário uma série de opções em uma tela, para sua escolha, dispostas na forma de linhas roláveis, conforme ilustrado na imagem:






Primeiro vamos criar um novo projeto em nosso ambiente de desenvolvimento, e nomeá-lo "projetoListView".

Depois, vamos abrir a pasta "res" (resources) e clicar com o botão direito do mouse sobre a pasta "layout", escolhendo a opção "New->Android XML File", ou seja, vamos criar um novo layout de tela.

Na janela que se abre, vamos digitar o nome da nova tela: "selecao_funcionario" e clicar no botão "OK".

Substitua, então, o código do novo arquivo pelo que se segue:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listViewFuncionarios"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:cacheColorHint="#00000000"
        android:padding="0dp" >
    </ListView>

</LinearLayout>


Experimente alterar o modo de visualização do layout para "Graphical Layout" e perceba que já aparece uma prévia de como será disposta a lista. Mas falta ainda configurar a aparência individual das linhas.

Vamos, portanto, criar um novo arquivo xml, clicando com o botão direito do mouse sobre "layout" e escolhendo "New->Android XML File". Vamos nomeá-lo "linha_funcionario" e depois clicar em "OK".

Vamos agora substituir por completo o código do arquivo criado pelo código abaixo:

<?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/linearLayoutLinha"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="5dp"
        android:paddingTop="5dp" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <ImageView
                android:id="@+id/imageViewFoto"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:padding="5dp"
                android:src="@drawable/ic_launcher" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="0dp" >

            <TextView
                android:id="@+id/textViewNome"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:padding="2dp"
                android:text="Nome do funcionário" />

            <TextView
                android:id="@+id/TextViewCargo"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:padding="2dp"
                android:text="Cargo" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>


Com isto definimos o layout de uma tela que conterá uma lista de funcionários. Também criamos o design da linha individual da lista, cada uma sendo composta pela foto do funcionário, nome e cargo.

Precisaremos agora criar uma classe "FuncionarioDTO", descrevendo os campos de um item "funcionário".  Ele será composto pela foto do funcionário, nome e cargo. Então, na pasta "src" (sources), clique sobre o nome do pacote ("com.example.projetolistview") e escolha "New->Class". Então, na nova classe  criada, substitua integralmente pelo código abaixo:

package com.example.projetolistview;

import android.graphics.Bitmap;

public class FuncionarioDTO
{

   private Bitmap foto;
   private String nome;
   private String cargo;
  
   public Bitmap getFoto()
   {
      return foto;
   }
   public void setFoto(Bitmap foto)
   {
      this.foto=foto;
   }
   public String getNome()
   {
      return nome;
   }
   public void setNome(String nome)
   {
      this.nome=nome;
   }
   public String getCargo()
   {
      return cargo;
   }
   public void setCargo(String cargo)
   {
      this.cargo=cargo;
   }
  
}


Vamos criar também a classe "ArrayListAdapter" (pelo mesmo procedimento apresentado) e substituir pelo código abaixo:


package com.example.projetolistview;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import com.example.projetolistview.R;

public class ArrayListAdapter extends ArrayAdapter<Object>
{

   protected Context context;
   protected ArrayList<?> list=null;
   public int count=20;
   protected ArrayListAdapter.Callback callback=null;
   private int layout=R.layout.linha_funcionario;

   public ArrayListAdapter(Context context, int layout, ArrayList<?> list)
   {
      super(context, 0);

      this.context=context;
      this.list=list;

   }

   public ArrayListAdapter(Context context, ArrayList<?> list)
   {
      super(context, 0);

      this.context=context;
      this.list=list;

   }

   public void setLayout(int resLayoutId)
   {
      this.layout=resLayoutId;
   }

   public int getLayout()
   {
      return this.layout;
   }

   public interface Callback
   {
      View execute(final int position, final View convertView, final ViewGroup parent);
   }

   public void setCallback(Callback callback2)
   {
      this.callback=callback2;
   }

   @Override
   public void notifyDataSetChanged()
   {
      super.notifyDataSetChanged();
   }

   @Override
   public int getCount()
   {
      return list.size();
   }

   @Override
   public Object getItem(int position)
   {
      return list.get(position);
   }

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

   @Override
   public View getView(int position, View convertView, ViewGroup parent)
   {

      LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View view=inflater.inflate(getLayout(), null);

      if (list != null)
      {

         if (callback != null)
         {
            view=callback.execute(position, view, parent);
         }

      }

      return view;
   }

}

Por último, vamos abrir o arquivo "MainActivity.java" e substituir o código por completo, pelo disposto abaixo:


package com.example.projetolistview;

import java.util.ArrayList;

import com.example.projetolistview.R;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.view.ContextMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity
{

   Context ctx;
   private ListView listViewFuncionarios;
   private ArrayListAdapter adapter;

   // Define uma lista de funcionarios.
   private ArrayList<FuncionarioDTO> listaDeFuncionarios;

  
   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.selecao_funcionario);
     
      // Cria a lista de funcionarios e carrega.
      listaDeFuncionarios = new ArrayList<FuncionarioDTO>();
      listaDeFuncionarios = carregarFuncionarios();    
     
      // Obtém o contexto da tela.
      ctx=this;

      // Obtém o elemento ListView para manipulação.
      listViewFuncionarios=(ListView) findViewById(R.id.listViewFuncionarios);

      // Cria o ArrayListAdapter e o configura.
      adapter=new ArrayListAdapter(this, listaDeFuncionarios);
      adapter.setLayout(R.layout.linha_funcionario);
      adapter.notifyDataSetChanged();
      adapter.setCallback(new ArrayListAdapter.Callback()
      {

         @Override
         public View execute(final int position, final View view, final ViewGroup parent)
         {
            String nome="";
            String cargo="";
            Bitmap foto;
           
            if (position <= listaDeFuncionarios.size())
            {
               // Para cada elemento da lista, obtém os campos, para preenchimento posterior.
               nome=listaDeFuncionarios.get(position).getNome();
               cargo=listaDeFuncionarios.get(position).getCargo();
               foto=listaDeFuncionarios.get(position).getFoto();

               // Preenche o conteúdo dos campos do listView.
               final TextView textViewNome=(TextView) view.findViewById(R.id.textViewNome);
               textViewNome.setText(nome);

               final TextView textViewCargo=(TextView) view.findViewById(R.id.TextViewCargo);
               textViewCargo.setText(cargo);
              
               final ImageView imageViewFoto=(ImageView) view.findViewById(R.id.imageViewFoto);
               imageViewFoto.setImageBitmap(foto);
              
               final LinearLayout linha=(LinearLayout) view.findViewById(R.id.linearLayoutLinha);

               // Alterna as cores das linhas.
               if (position % 2 == 0)
               {
                  linha.setBackgroundColor(Color.LTGRAY);
               }
               else
               {
                  linha.setBackgroundColor(Color.WHITE);
               }
            }

            return view;
         }
      });

      listViewFuncionarios.setAdapter(adapter);
      listViewFuncionarios.setFastScrollEnabled(true);

      // Define o comportamento do clique sobre um dos itens.
      listViewFuncionarios.setOnItemClickListener(new ListView.OnItemClickListener()
      {
         @Override
         public void onItemClick(AdapterView<?> arg0, View arg1, int pos, long arg3)
         {
            // Recupera nome e cargo do item selecionado pelo usuário.
            String nome  = listaDeFuncionarios.get(pos).getNome();
            String cargo = listaDeFuncionarios.get(pos).getCargo();
           
            // Mostra mensagem na tela.
            Toast.makeText(getApplicationContext(), "Funcionário " + nome + " (" + cargo +  ") selecionado", Toast.LENGTH_SHORT).show();
           
         }
      });

      listViewFuncionarios.setOnScrollListener(new AbsListView.OnScrollListener()
      {

         int page=1;
         private int currentFirstVisibleItem;
         private int currentVisibleItemCount;
         private int currentScrollState;
         private boolean isLoading;
         private boolean mIsScrollingUp;
         private int mLastFirstVisibleItem;

         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
         {
            this.currentFirstVisibleItem=firstVisibleItem;
            this.currentVisibleItemCount=visibleItemCount;
         }

         public void onScrollStateChanged(AbsListView view, int scrollState)
         {
            this.currentScrollState=scrollState;

            if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE)
            {
              
               if (!isLoading)
               {
                  isLoading=true;
               }
            }
         }

      });

      registerForContextMenu(listViewFuncionarios);
   }

   public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
   {

      final AdapterView.AdapterContextMenuInfo info=(AdapterView.AdapterContextMenuInfo) menuInfo;

   }

  
   private ArrayList<FuncionarioDTO> carregarFuncionarios()
   {
           
      FuncionarioDTO funcionarioDTO;
     
      // Cria um objeto que armazenará a foto do funcionário.
      // Neste exemplo, utilizaremos a mesma foto para todos os funcionários.
      // Aqui estamos obtendo a imagem da foto a partir de um PNG (ic_laucher) da pasta drawable.
      Bitmap foto;
      foto=BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.ic_launcher);
     
      // Preenche os funcionários um a um e os acrescenta à lista.
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("João Silva");
      funcionarioDTO.setCargo("Auxiliar administrativo");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Maria Barbosa");
      funcionarioDTO.setCargo("Cozinheira");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Helena Dias");
      funcionarioDTO.setCargo("Gerente");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Pedro Salas");
      funcionarioDTO.setCargo("Diretor");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Arnaldo Xavier");
      funcionarioDTO.setCargo("Ascensorista");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Luiz Quintela");
      funcionarioDTO.setCargo("Programador");
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Ana Afonso");
      funcionarioDTO.setCargo("Analista de sistemas");  
      listaDeFuncionarios.add(funcionarioDTO);

      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Manoela Farias");
      funcionarioDTO.setCargo("Advogada");  
      listaDeFuncionarios.add(funcionarioDTO);

      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Cesar Alfredo");
      funcionarioDTO.setCargo("Contador");  
      listaDeFuncionarios.add(funcionarioDTO);

      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Marcio Teixeira");
      funcionarioDTO.setCargo("Vendedor");  
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Daniela Dantas");
      funcionarioDTO.setCargo("Advogada");  
      listaDeFuncionarios.add(funcionarioDTO);

      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Otavio Cintra");
      funcionarioDTO.setCargo("Programador");  
      listaDeFuncionarios.add(funcionarioDTO);

      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Dhenis Pereira");
      funcionarioDTO.setCargo("Gerente");  
      listaDeFuncionarios.add(funcionarioDTO);
     
      funcionarioDTO = new FuncionarioDTO();
      funcionarioDTO.setFoto(foto);
      funcionarioDTO.setNome("Erica Montalvo");
      funcionarioDTO.setCargo("Vendedora");  
      listaDeFuncionarios.add(funcionarioDTO);

      // Retorna a lista de funcionários criada.
      return listaDeFuncionarios;
   }
  
}


O que fizemos no MainActivity foi um método que cria uma lista de funcionários fictícios. Esse método devolve a lista, que é então utilizada para configurar o ArrayListAdapter. O resultado é uma tela com uma lista de funcionários rolável. Ao clicar sobre o nome de um funcionário, o usuário verá uma mensagem informando o nome e cargo do item "funcionário" escolhido.

Para os que preferirem obter o código-fonte completo, disponibilizo-o para download. Quem quiser apenas instalar o aplicativo no celular, segue o APK.

Ficamos por aqui. Até a próxima!