quarta-feira, 30 de março de 2011

Eventos Simples no iOS

Olá povo,

Dando continuidade ao post sobre iPhone, mostrarei nesse como tratar eventos simples na plataforma iOS. Nosso exemplo constará de uma tela com uma caixa de texto e um botão. Ao clicar nesse botão, será exibida uma mensagem com o conteúdo digitado.

Inicie um novo projeto no Xcode, selecione a opção View-based Application e indique o local onde deseja salvar seu projeto juntamente com seu nome (utilizei ExemploBlog2). Um novo projeto será iniciado e começaremos nosso exemplo desenhando a tela da aplicação. Dê um duplo clique sobre o arquivo ExemploBlog2ViewController.xib que está dentro da pasta Resources. O Interface Builder será aberto para que possamos editar o arquivo.
Arraste da janela Library (se ela não estiver sendo exibida pressione Shift-Command-L): um Label, um TextField e um RoundRectButton. Altere a texto do Label e do botão dando um duplo-clique sobre eles. Na janela de propriedades (se não estiver ativa, pressione Command-1) altere a propriedade Text Input Traits > Return Key para Done. Isso servirá para quando terminarmos de digitar o texto desejado, possamos fechar o teclado virtual. Por fim, arraste os componentes de na tela deixando-os similar a figura abaixo.
Salve o arquivo e volte ao Xcode. Vamos agora declarar esses componentes no código da classe que tratará os eventos da UI. Abra o arquivo ExemploBlog2ViewController.h e deixe-o conforme abaixo.

#import <UIKit/UIKit.h>

@interface ExemploBlog2ViewController :
UIViewController {

IBOutlet UITextField* txtNome;
}

- (IBAction) doneDoTeclado;
- (IBAction) cliqueDoBotao;

@end


No arquivo .h é onde declaramos os atributos da classe (que ficam entre as chaves) e as assinaturas de métodos (após a chave de fechamento e a diretiva @end). No código acima, declaramos um UITextField que representará a caixa de texto que definimos no InterfaceBuilder. Notem que antes da declaração, colocamos o texto IBOutlet, que é necessário para que possamos interligar essa variável ao componente definido no Interface Builder.

Logo abaixo declaramos dois métodos, o primeiro servirá para fechar o teclado virtual e o segundo servirá para tratar o clique do botão. A implementação desses métodos fica no arquivo com exetnsão .m. Abra o arquivo ExemploBlog2ViewController.m e deixe-o de acordo com o código abaixo:

#import "ExemploBlog2ViewController.h"

@implementation ExemploBlog2ViewController

- (IBAction) doneDoTeclado {
[txtNome resignFirstResponder];
}

- (IBAction) cliqueDoBotao{
[self doneDoTeclado];
NSString *mensagem = [[NSString alloc]
initWithFormat:@"Você digitou %@", txtNome.text];

UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Informação"
message:mensagem
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
[mensagem release];
}

- (void)dealloc {
[txtNome release];
[super dealloc];
}

@end


O iOS (sistema operacional do iPhone/iPad/iPod Touch) utiliza uma cadeia de "respondedores" de eventos. Ao entrar em uma caixa de texto, o teclado virtual passa a tratar os eventos da caixa de texto. Para reatribuir o controle ao TextField chamamos o método resignFirstResponder.
No método do clique do botão, chamamos o método doneDoTeclado, para que, se o usuário clicar no botão sem clicar no Done do teclado, o teclado virtual desapareça. Em seguida criamos uma string (representada pela classe NSString) para concatenar a mensagem que desejamos com o texto digitado.
Notem que foi utilizado o método alloc seguido pelo método initWithFormat. Diferentemente de Java, no Objective-C temos que alocar e desalocar a memória utilizada pelo objeto. O método alloc chamado a partir da classe faz a alocação e o método release (como usamos mais abaixo) faz a liberação. Objective-C não tem o conceito de construtores, por isso são usados métodos com prefixo init para inicializar o objeto.
A classe que exibirá a mensagem é a UIAlertView, a qual inicializamos um objeto passando o título do diálogo, a mensagem, um objeto que trate o clique do botão do diálogo, o texto do primeiro botão, e por último o texto de mais botões que você desejar.
Como você deve ter notado, chamadas de método são feitas usando a notação diferente das linguagens basedas em C (como Java, C#, C++). Ele utiliza a notação In-Fix que coloca palavras antes de cada parâmetro para explicar para que serve. No código acima é fácil identificar para que serve cada parâmetro para criar o UIAlertView. Criado o diálogo, o exibimos chamando o método show.

Por fim, o método dealloc deve liberar todos os recursos alocados pela tela. No nosso caso, estamos liberando a memória alocada para o TextField.

A parte de código está pronta. Falta agora fazer a ligação entre o arquivo .xib do InterfaceBuilder com esse código. Volte para o InterfaceBuilder e vamos fazer essa ligação. Selecione o TextField e a partir da janela Connections (se não estiver sendo exibida, Command-2) clique em New Referencing Outlet e arraste para o File's Owner conforme a figura abaixo. Na lista que for exibida, selecione txtNome.



Faça agora a ligação arrastando o evento Did End on Exit para o File's Owner e na lista que será exibida selecione doneDoTeclado. Agora selecione o botão e faça agora a ligação arrastando o evento Touch Up Inside para o File's Owner, e na lista que for exibida selecione cliqueDoBotao.

Feito isso, volte para o Xcode e pressione o botão Build and Run. O resultado será semelhante as figuras abaixo.




Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

quinta-feira, 24 de março de 2011

Adapter Eficiente no Android

Olá povo,

Para preencher diversos componentes de UI como: ListView, Spinner (combo box) e GridView, o Android utiliza o conceito de Adapters. Esse é um padrão de projeto muito útil, e serve (como o próprio nome diz) para adaptar duas interfaces incompatíveis. No caso do Android, esse padrão serve para adaptar uma lista de objetos para uma lista elementos de interface gráfica, como as linhas de uma ListView.
Por exemplo, imagine uma lista de objetos da classe Pessoa, e você quer exibi-los na tela. O Android, através da classe ListView (que é um componente que exibe os componentes em forma de lista) solicita, para cada linha da lista um novo objeto View (que é a classe mãe de todos os elementos visuais do Android). Cabe ao Adapter, criar um objeto View que represente visualmente o objeto da posição específica da lista de objetos. Vamos detalhar mais como isso funciona na prática.
Crie um novo projeto no Eclipse marcando a opção para criar uma activity e nomeando-a como TelaListaActivity. Antes de mexer na Activity vamos criar a classe de objetos que queremos listar. No nosso caso, será a classe Pessoa que citamos acima.
public class Pessoa {
  private long id;
  private String nome;
  private String estado;

  public Pessoa(long id, String nome, String estado) {
     this.id = id;
     this.nome = nome;
     this.estado = estado;
  }

  public long getId() {
     return id;
  }
  public void setId(long id) {
     this.id = id;
  }
  public String getNome() {
     return nome;
  }
  public void setNome(String nome) {
     this.nome = nome;
  }
  public String getEstado() {
     return estado;
  }
  public void setEstado(String estado) {
     this.estado = estado;
  }
}

A classe acima não passa de um POJO (Plain Old Java Object), ou seja, apenas uma classe com métodos gets e sets. Agora vamos criar o arquivo de layout que representará cada linha a ser exibida na ListView. Crie o arquivo linha.xml dentro do diretório res/layout e deixe-o conforme abaixo:
<LinearLayout android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
   android:id="@+id/textView1"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:textsize="26dp"
   android:textcolor="#00FF00"/>
<TextView
   android:id="@+id/textView2"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
</LinearLayout>

Nosso layout também é bem simples. Consta apenas de um LinearLayout com dois TextViews, onde deixamos primeiro com o texto um pouco maior que o segundo e com a cor verde. Vamos agora criar nosso adaptador. Crie uma nova classe chamada PessoaAdapter e deixe-a conforme abaixo.
public class PessoaAdapter extends BaseAdapter {

   private Context ctx;
   private List<Pessoa> pessoas;

   public PessoaAdapter(Context ctx, 
      List <Pessoa> pessoas) {
      this.ctx = ctx;
      this.pessoas = pessoas;
   }

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

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

   @Override
   public long getItemId(int position) {
      return pessoas.get(position).getId();
   }

   @Override
   public View getView(int position, View convertView,
      ViewGroup parent) {
      
      Pessoa p = pessoas.get(position);

      View v = LayoutInflater.from(ctx).inflate(
         R.layout.linha, null);

      TextView txt1 = (TextView) v.findViewById(
         R.id.textView1);
      TextView txt2 = (TextView) v.findViewById(
         R.id.textView2);

      txt1.setText(p.getNome());
      txt2.setText(p.getEstado());

      return v;
   }
}

Nossa classe herda BaseAdapter e tem dois atributos que são inicializados no construtor da classe. O atributo da classe Context será utilizado para carregar o arquivo de layout que acabamos de criar. Já o segundo é a lista de objetos da classe Pessoas que queremos adaptar.
Quando criamos um adapter, devemos implementar quatro métodos: getCount, getItem, getItemId e getView. O primeiro retorna a quantidade de linhas que o adaptador representa, se estamos adaptando a lista de pessoas, basta retornar o tamanho da lista de pessoas.
O método getItem serve para acessarmos o objeto que o adaptador está representando. No nosso caso, basta retornar o objeto da posição da lista.
Já o método getItemId serve pra retornar um identificador do objeto da posição passada como parâmetro. Esse método é opcional, mas como temos um atributo chamado id na classe pessoa, ele passa a ser um bom candidato. Então obtemos o objeto da classe Pessoa da posição especificada pelo parâmetro e retornamos o seu id.
O último método é o mais importante. É ele que vai pegar um objeto da lista de pessoas, carregar o arquivo de layout e atribuir os valores dos atributos do objeto pessoa para a view que representará a linha da lista.
Para carregar o arquivo de layout, utilizamos a classe LayoutInflater. Uma vez que esse layout é carregado, é retornado um objeto View que representa a árvore de Views definida no arquivo XML, que no nosso caso, tem um LinearLayout e dentro dele, dois TextViews. Uma vez de posse desse objeto View, obtemos as referências para os TextViews para podemos passar os valores dos atributos da classe Pessoa para eles. No final retornamos a View que carregamos e modificamos.

Vamos agora para atividade da aplicação. Nela vamos criar uma lista de objetos pessoa, criar o uma instância do nosso adapter e defini-lo como "preenchedor" da lista.
public class TelaListaActivity extends ListActivity {

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      ArrayList<Pessoa> pessoas = 
         new ArrayList<Pessoa>();

      pessoas.add(
         new Pessoa(1, "Nelson", "Pernambuco"));
      pessoas.add(
         new Pessoa(2, "Glauber", "São Paulo"));
      pessoas.add(
         new Pessoa(3, "José", "Minas Gerais"));
      pessoas.add(
         new Pessoa(4, "João", "Rio de Janeiro"));
      pessoas.add(
         new Pessoa(5, "Silvio", "Pernambuco"));
      pessoas.add(
         new Pessoa(6, "Marta", "Pernambuco"));
 
      PessoaAdapter adapter = 
         new PessoaAdapter(this, pessoas);

      setListAdapter(adapter);
   }
}

Se executarmos nossa aplicação teremos um resultado similar à figura abaixo:

Muito bom! Nossa lista de objetos pessoa foi exibido perfeitamente na ListView que é criada automaticamente quando herdamos da classe ListActivity. Mas vamos recaptular um pouco... O método getView é chamado para cada linha da linha. Então se tivermos 10000 linhas na lista carregaremos 10000 layouts? Pois é, e isso não é bom. Mas não se desespere, os engenheiros da Google criaram uma forma padrão de otimizar essas listas. Altere o método getView para que fique conforme abaixo:
@Override
public View getView(int position, View convertView, 
   ViewGroup parent) {
  
   ViewHolder holder;

   Pessoa p = pessoas.get(position);

   if (convertView == null){
      convertView = LayoutInflater.from(ctx).inflate(
         R.layout.linha, null);
 
      holder = new ViewHolder();
      holder.txtNome = (TextView) 
         convertView.findViewById(R.id.textView1);
      holder.txtEstado = (TextView) 
         convertView.findViewById(R.id.textView2);
      convertView.setTag(holder);
   } else {
      holder = (ViewHolder)convertView.getTag();
   }
   holder.txtNome.setText(p.getNome());
   holder.txtEstado.setText(p.getEstado());

   return convertView;
}

static class ViewHolder {
   TextView txtNome;
   TextView txtEstado;
}

A classe ViewHolder vai manter as referências para a Views filhas para evitar chamadas desnecessárias ao método findViewById para cada linha. O parâmetro convertView representa uma linha pode ser reusada, uma vez que nem todas as linhas podem estar sendo vistas na tela. Quando convertView é diferente null, podemos reusá-la, então não será necessário carregar uma nova instância do layout. Se ela for nula, carregamos o layout e instanciamos um novo ViewHolder para armazenar as Views que iremos alterar, e em seguida setamos esse ViewHolder na propriedade tag (que pode armazenar qualquer objeto) da convertView.

O resultado para o número de registros que usamos não é aparente, mas tente adicionar 10000 objetos rodar a lista com o primeiro e o segundo exemplo :) Façam esse teste usando a instrução abaixo pra alimentar a lista;

for (int i = 0; i < 10000; i++) {
   pessoas.add(new Pessoa(i, "Pessoa "+ i, "Acre"));  
}


Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

Fonte: Android Developers e Google IO 2010

segunda-feira, 21 de março de 2011

Palestra de Android

Olá povo,

Foi com grande satisfação que hoje ministrei uma palestra sobre Android para a turma de pós-graduação em Engenharia de Software da UFPE. A apresentação foi feita durante a aula de Engenharia de Software do Professor Silvio Meira e contou com a ilustre presença dos Professores Jones e Vinícius.

O dia começou com uma excelente palestra sobre arquitetura com Luiz Borba. Descontraído como sempre, ele falou sobre definições de arquitetura, padrões de projeto, refactoring, TDD e muitas outras coisas que todo programador (como ele, e agora eu, nos intitulamos) deve saber.

Em seguida foi a vez da "minha pessoa". Fiquei bem nervoso no começo, afinal de contas, são poucos [eu acho] que não ficam nervosos ao falar de tecnologia na frente de srlm. Mas depois foi tranquilo.
Eu falei da revolução dos últimos anos no mercado de smartphones e a parcela do Android nesse contexto. Expliquei a arquitetura e o segredo do sucesso da plataforma do Google, discutimos sobre seus concorrentes e sobre o que podemos fazer com o "robozinho verde".
Em seguida, mostrei as ferramentas necessárias para o desenvolvimento de aplicações e como configurá-las. Ao final, fiz um exemplo explicando como é organizado um projeto e como executá-lo no emulador do Android.

Abaixo estão algumas fotos da apresentação. (Valeu Vinícius! :)









4br4ç05,
nglauber

segunda-feira, 14 de março de 2011

Code Assist do Eclipse Helios lento

Olá povo,

Muitos alunos e colegas meus reclamaram da lentidão do Code Assist do Eclipse Helios. Dei uma pesquisada e eis aqui a solução:

-----
Resumo da página:
http://groups.google.com/group/android-developers/msg/0f9d2a852e661cba?pli=1

O bug está registrado na página do Eclipse e já foi resolvido. https://bugs.eclipse.org/bugs/show_bug.cgi?id=329288

Porém o patch foi aplicado apenas para a versão 3.7. Mas um santo fez a solução e disponibilizou pra 3.6. Basta substituir o plugin ECLIPSE_HOMEe/plugins/org.eclipse.jdt.core_3.6.1.v_A68_R36x.jar por esse aqui. Em seguida, e reinicie o Eclipse. O Assistente de Código estará bem melhor.

Não se esqueça de fazer backup dos plugins originais.

4br4ç05,
nglauber