quarta-feira, 26 de outubro de 2011

Android: Tratando mudança de orientação

Olá povo,

Chegaram 4 novos estagiários aqui no projeto e coube "a minha pessoa" orientá-los. Durante a aula, o assunto enveredou para tratamento de mudança de orientação do aparelho (portrait e landscape | retrato e paisagem).
Por padrão, ao girar o telefone o método onCreate é chamado novamente e com isso, dados que foram carregados dinamicamente (como itens de um ArrayList que estão preenchendo um ListView) são perdidos.

Vejamos o exemplo abaixo:

Arquivo de layout.
<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="vertical"
 android:background="#0000FF">

 <TextView
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Digite o nome" />

 <EditText
   android:id="@+id/editText1"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"/>

 <Button
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:onClick="meuBotaoClick"
   android:text="Adicionar" />

 <ListView
   android:id="@+id/listView1"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent" />
</LinearLayout>

Classe da Activity
public class TelaPrincipalActivity
 extends Activity {

 EditText edt;
 ArrayList<String> nomes;
 ArrayAdapter<String> adapter;

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

   edt = (EditText) findViewById(R.id.editText1);

   ListView listView = (ListView) 
     findViewById(R.id.listView1);
   adapter = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, nomes);

   listView.setAdapter(adapter);
 }

 public void meuBotaoClick(View v) {
   nomes.add(edt.getText().toString());
   edt.setText("");
   adapter.notifyDataSetChanged();
 }
}

No código acima, ao clicar no botão, o texto digitado no EditText é inserido na lista, e em seguida o adapter é atualizado através do método notifyDataSetChanged.
O exemplo é bem simples, mas se você girarmos o aparelho os dados que estão sendo exibidos são perdidos, uma vez que a lista inicia vazia e (como já falei) o onCreate é chamado novamente.

Para isso não acontecer você tem 3 alternativas:

1)Forçar uma orientação
Na declaração da sua Activity, no AndroidManifest.xml, você pode usar a propriedade android:screenOrientation para manter sua aplicação em uma orientação específica (portrait/landscape).
<activity
 android:name="TelaPrincipalActivity"
 android:screenOrientation="portrait" />


2)Avisar o Android para não chamar o onCreate
Essa é a melhor opção quando você quer utilizar a tela nas duas orientações e impedir que o Android chame o onCreate cada vez que você girar o aparelho. Para isso basta dizer que ao mudar a configuração (configChanges) de orientação, ele não recrie a Activity.
<activity
 android:name="TelaPrincipalActivity"
 android:configChanges="orientation|keyboardHidden|screenSize"/>


3) Salvar o estado da Activity
A opção número dois é perfeita quando você quer usar o mesmo layout nas duas orientações. Mas, e se eu quiser usar layouts diferentes? Neste caso você dever permitir que a Activity seja recriada. Entretanto, devemos salvar seu estado com o método onSaveInstanceState e no onCreate recuperar esse estado.

No exemplo que fiz os "estags", criei a pasta "layout-land" com o mesmo layout definido acima, só mudando o background (apenas para notarmos os diferentes layouts). Depois implementamos o método onSaveInstanceState para salvar os itens da lista.
@Override
protected void onSaveInstanceState(Bundle outState) {
 super.onSaveInstanceState(outState);
 outState.putStringArrayList("nomes", nomes);
}

Se vocês notaram, o método onSaveInstanceState usa um objeto da classe Bundle para salvar o estado. Esse estado é passado para o método onCreate da Activity como parâmetro, que utilizaremos para restaurar o seu estado.
@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 // Código já apresentado
 if (savedInstanceState != null) {
   nomes = savedInstanceState.
    getStringArrayList("nomes");

 } else {
   nomes = new ArrayList<String>();
 }
}


Com isso podemos ter, para a mesma tela, um layout diferente para cada orientação, desde que tenhamos os mesmos componentes (e com os mesmos ids).

Ficou com dúvida? Deixe seu comentário.

4br4ç05,
nglauber

segunda-feira, 24 de outubro de 2011

Android: utilizando layout_weight

Olá povo,

Em todas as minhas turmas de Android, noto uma certa dificuldade em entender como funciona a propriedade layout_weight utilizada nos componentes inseridos dentro do LinearLayout. Por padrão, essa propriedade serve para definir como o espaço restante do layout deve ser distribuído pelos componentes. Mas esse comportamento pode variar de acordo com o valor definido na propriedade layout_width e layout_height. Observemos a figura abaixo:




O layout acima é um LinearLayout vertical (orientation=vertical) com vários LinearLayout horizontal iguais, com 3 botões cada, e com os respectivos textos: "Um", "exemplo" e "de peso".

Vamos ver como da linha está configurada:

Linha1 - Nessa linha não fizemos nenhuma modificação, ou seja, está sem peso e com a largura e altura (layout_width e layout_height) definidos como wrap_content.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="Exemplo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="de peso"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

Linha 2 - Nesse linha, definimos pesos iguais para todos os botões. Notem que apesar dos pesos estarem iguais, os componentes não têm tamanhos iguais. Essa é a aplicação clássica do layout_weight. Como vimos na linha 1, sobrou um espaço entre o terceiro botão e a margem direita. Com os pesos iguais, o espaço restante é distribuído igualmente entre os componentes. Uma vez que o texto de um botão é maior que o outro, os botões ficam com larguras diferentes.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="Exemplo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="de peso"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>

Linha 3 - Essa linha é similar a anterior, a diferença fica por conta da propriedade layout_width definida como 0dp. Como assim? Se setarmos a largura para zero, o componente não deveria aparecer certo? A resposta é sim, mas como estamos usando o peso, ele vai distribuir o espaço restante da tela igualmente para os 3 botões. Como os 3 botões estão com tamanho zero, o espaço restante (e que será dividido entre os componentes) é toda a tela.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="Exemplo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="de peso"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>

Linha 4 - Igual a anterior, mas modificamos a proporção dos componentes. Os dois primeiros ocupam metade da tela, e o terceiro ocupa a outra metade.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="Exemplo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="de peso"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
</LinearLayout>

Linha 5 - Essa linha usa uma outra utilidade do layout_weight, a de prioridade. No código abaixo os dois primeiros botões estão com a propriedade layout_width setada para match_parent (que é o mesmo que fill_parent), ou seja, devem ocupar toda a largura do seu pai. Se esses componentes não tivessem o peso setado, o primeiro botão ocuparia toda a tela, e os demais não apareceriam. Uma vez que definimos o peso, cada um deles divide a tela, mas e o terceiro? Como não foi definido peso para ele, ele tem peso zero, então, o componente com MENOR peso, tem MAIOR prioridade, então ele passa a aparecer. Note que o layout_width do terceiro botão está como wrap_content, se estivesse fill_parent, ele ocuparia toda a tela, uma vez que ele tem peso menor (zero).
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="Exemplo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="de peso"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

Linha 6 - Conforme explicado acima, todos os botões estão com a propriedade layout_width definidos como fill_parent, entretanto, o primeiro está com peso 2, enquanto os demais tem peso 1. Logo os com peso menor tem prioridade.
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:text="Um"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="2"/>
<Button
android:text="Exemplo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:text="de peso"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>


Espero que agora fique mais claro a utilização do peso. Na dúvida, deixem seus comentários.

4br4ç05,
nglauber

quinta-feira, 20 de outubro de 2011

Android: Dicas 5

Olá povo,

Esse é mais um post da série de dicas de Android. Aproveitem e deixem seus comentários :)

Dica 1 - Alterando a fonte dos componentes
O Android, por padrão, tem apenas 3 fontes que podem ser utilizadas nos componentes visuais: sans, serif e monospace. Para utilizar cada uma delas, basta atribuir o valor desejado à propriedade android:typeface. Mas se quiser utilizar uma nova fonte, basta adicionar o arquivo *.ttf na pasta assets e carregá-la utilizando o código abaixo.
Typeface typeface =
Typeface.createFromAsset(getAssets(), "Aliens.ttf");

TextView txt = (TextView)findViewById(R.id.textView1);
txt.setTypeface(typeface);

Infelizmente, não achei uma forma de setar essa fonte no XML. Se alguém souber, deixe um comentário. Ah! A fonte que usei nesse exemplo foi baixada do site http://www.webpagepublicity.com/free-fonts.html.

Dica 2 - Converter DIP (Density Independent Pixel) para Pixel
Após ministrar duas aulas seguidas (em turmas diferentes) sobre Views personalizadas no Android, uma dúvida frequente foi como converter DIP para PX (pixel). Isso é especialmente útil para que a View rode corretamente em diferentes densidades de tela (LDPI, MDPI, HDPI e agora XHDPI). Para tal, pode-se utilizar o método applyDimension da classe TypeValue.
Resources r = getResources();
float valorEmDp = 14;
float valorEmPixels = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, valorEmDp,
r.getDisplayMetrics());


Dica 3 - Evitando acumular Toasts
Um "bug" clássico que os engenheiros de teste do projeto em que eu trabalho levantam é sobre a utilização do Toast. Se você chamar o método show() dessa classe várias vezes seguidas, essas mensagens são acumuladas e ficam sendo exibidas sequencialmente. Um recurso que utilizo é criar um método separado, que verificará se já existe um Toast aberto, em caso positivo, ele o cancela para exibir um novo. Para tal, você deve criar um atributo da classe Toast (que abaixo chamo de 'toast') e utilizar o seguinte código:
// Declare o atributo
private static Toast toast;

// Método
public static void showToast(Context ctx, int res){
if (toast != null){
toast.cancel();
toast.setText(res);
} else {
toast = Toast.makeText(ctx, res, Toast.LENGTH_LONG);
}
toast.show();
}


Dica 4 - Definindo onde instalar sua aplicação
A partir da versão 2.2 (Froyo) é possível especificar onde sua aplicação pode ser instalada: cartão de memória ou memória interna. Por padrão, a aplicação será instalada na memória interna, mas você pode utilizar a propriedade android:installLocation da tag <manifest> do AndroidManifest.xml para um dos valores abaixo:
internalOnly - A aplicação só poderá ser instalada na memória interna (valor padrão).
auto - A aplicação é instalada na memória interna, mas poderá ser movida para o cartão de memória posteriormente ou quando a memória interna estiver cheia.
preferExternal - A aplicação deve ser instalada no cartão de memória preferencialmente, mas isso não é garantido.

Exemplo:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="preferExternal"
... >


Dica 5: Ativar/Acender a tela do aparelho
Neste post eu mostrei como criar notificações no Android. Um recurso que pode ser utilizado juntamente com as notificações é acender/ligar a tela do aparelho. Para fazer isso, deve-se utilizar a classe PowerManager.

// Obtém a instância do PowerManager
PowerManager pm = (PowerManager)c.getSystemService(
Context.POWER_SERVICE);

// Liga o display do aparelho
WakeLock wakeLock = pm.newWakeLock(
// Liga a tela
PowerManager.SCREEN_DIM_WAKE_LOCK |
// Após liberar a tela para apagar,
// mantém a tela ligada por um pouco
// mais de tempo
PowerManager.ON_AFTER_RELEASE,
// Tag para debug
"tag_para_debug");

// Liga a tela por 10 segundos
wakeLock.acquire(10000);


Para usar o código acima, deve-se adicionar a permissão WAKE_LOCK no AndroidManifest.xml.
<uses-permission name="android.permission.WAKE_LOCK"/>


Dica 6: debug.keystore expirou

Essa dica foi enviada pelo meu colega Ricardo Gilson.
Toda aplicação Android é assinada para ser instalada no dispositivo, por padrão, ela é assinada com uma assinatura de debug através do arquivo debug.keystore que fica na subpasta ".android" no diretório do usuário (C:\Users\usuario\.android ou /Users/usuario/.android). Depois de um certo tempo sem utilizar a máquina, a mensagem abaixo pode ser exibida ao tentar rodar uma aplicação:

Error generating final archive: Debug Certificate expired on 24/09/2011.

Para resolver o problema é só apagar o arquivo debug.keystore, dar um clean no projeto do Eclipse (menu Project > Clean) e tentar rodar novamente.

Dica 7: Habilite a aceleração de Hardware (por Fernado Fragoso)
Habilitar a aceleração de hardware quando necessitar de manipulação de imagens, adicionar na tag application do AndroidManifest.xml.

android:hardwareAccelerated="true"

Faz uma diferença gigantesca! Valeu Fernando!

Por hoje é só, qualquer dúvida ou sugestão, deixem seus comentários.

4br4ç05,
nglauber

terça-feira, 18 de outubro de 2011

Ice Cream Sandwich: O Android 4.0

Olá povo,

Fiquei acordado até as 02:00 para ver o lançamento do Android "Sanduíche de Sorvete". A apresentação mostrou as novidades da nova versão do S.O. no Galaxy Nexus da Samsung. Ao terminar a apresentação, foi anunciado que o SDK já estaria disponível. Então, não perdi tempo e já o atualizei juntamente com o plugin ADT (Android Development Tools).
Sendo assim, antes de dormir, vou deixar uns screenshots de algumas novidades que tirei com o emulador novo :)

Na tela de lock, é possível ir direto para câmera.


No Launcher, agora podemos adicionar Widgets. Inclusive os do Honeycomb.


Um Hello World pra ver se funciona mesmo :)



O SDK Manager ficou de cara nova. Bem melhor! Ah! O Assistente de novo projeto do Eclipse também melhorou.

Vou tentar atualizar esse post mais tarde.

4br4ç05,
nglauber

sexta-feira, 7 de outubro de 2011

20 dicas para programadores

Olá povo,

Conviver com pessoas mais experientes é uma coisa muito boa, pois você pode explorar o know-how dessas pessoas e crescer profissionalmente (e às vezes pessoalmente). Eu tenho a satisfação de ter trabalhado, tido aulas e ter sido orientando por Luiz Eugênio Fernandes Tenório (left), um excelente profissional de desenvolvimento de software (senão o melhor que conheço).
Em 21 de agosto de 2007, ele me mandou um email com um texto de Jonathan Danylko que a muito tempo queria postar aqui no blog. É um conjunto de boas práticas, que um desenvolvedor de software pode seguir para melhorar a produtividade e a qualidade do seu trabalho. Adotei uma boa parte dessas dicas, e notei bons resultados através de feedbacks de gestores e colegas de trabalho. E por causa disso, resolvi compartilhar com vocês.

O texto abaixo é uma tradução/adaptação minha (vixe!) do texto original em inglês disponível aqui.

  1. Defina quanto tempo você acha que deve gastar para resolver um problema. Já vi programadores sentarem na frente de um monitor por oito horas para resolver um problema e nada. Defina uma tabela de tempo para si mesmo de 1 hora, 30 minutos, ou até mesmo 15 minutos. Se você não conseguir descobrir uma solução para seu problema dentro de seu prazo, peça ajuda ou pesquise seu problema na Internet em vez de tentar ser um super-coder.
  2. As linguagens são similares. Com o tempo, você entenderá como uma linguagem funciona, então você vai notar semelhanças com outras linguagens. A linguagem que você escolher deve: te fornecer um nível de "conforto" adequado, a capacidade de produzir código eficiente (e limpo), e acima de tudo, se adequar ao projeto e vice-versa.
  3. Não exagere nos padrões de projeto. Pense duas vezes antes de usá-los, se houver uma solução mais simples (e que funcione), use-a. Um problema que necessita de um padrão de projeto como solução, será identificado naturalmente (obviamente depois de um certo tempo de experiência).
  4. Sempre faça backup do código. Quando eu era mais novo, por uma falha no disco rígido, perdi um monte de código, e me senti horrível por causa do que tinha acontecido. A única vez que você não fizer backup dos dados, pode ser o momento onde você tem um prazo restrito com um cliente, e eles precisam do que você estava fazendo para amanhã. Controle de código fonte/versão (CVS, SVN, GIT,...) também se aplica aqui.
  5. Você não é o melhor em programação. Aceite isso. Eu sempre pensei que eu sabia muito sobre programação, mas sempre há alguém lá fora (ou do seu lado), melhor que você. Sempre! Aprenda com eles.
  6. Aprenda a aprender mais. Como número cinco explicou, eu sempre tive à mão uma revista ou livro sobre computadores/programação  (pergunte aos meus amigos, eles vão confirmar). Verdade, há muita tecnologia lá fora, e manter-se com ela é um trabalho em tempo integral. E se você tem uma maneira inteligente de receber novidades, você vai aprender sobre novas tecnologias a cada dia.
  7. Mudanças são constantes. Seu conhecimento de tecnologia e/ou programação deve ser semelhante à forma como você trata investimentos: Diversifique. Não fique muito confortável com uma tecnologia particular. Se não houver suporte suficiente para aquela linguagem ou tecnologia, é bom você pode começar a atualizar o seu currículo imediatamente, e começar o seu período de treinamento. Minha regra geral: Conheça pelo menos duas ou três linguagens, assim, se uma morre, você tem outra para trabalhar enquanto você estuda uma nova para colocar no lugar.
  8. Ajude os novatos. Oriente os desenvolvedores iniciantes sobre as boas técnicas de programação. Você nunca sabe... mas você pode ganhar pontos com isso, e vai se sentir mais confiante de ter, pessoalmente, treinado e preparado uma pessoa para seu próximo trabalho.
  9. Simplifique o algoritmo. Codifique como um demônio, mas quando estiver tudo pronto, volte para otimizá-lo. Uma pequena melhoria no código aqui e ali, e você terá uma manutenção de código mais feliz no longo prazo.
  10. Documente seu código. Se a documentação é uma API de Web Service ou uma simples classe, documente do mesmo jeito. Eu tenho sido acusado de excesso de comentários no meu código, e isso é algo que eu me orgulho. Leva apenas um segundo para adicionar uma linha de comentário adicional para cada 3 linhas de código.
  11. Teste, teste e teste. Eu sou um fã de testes de caixa preta. Quando sua rotina é concluída, o seu período de "selo de aprovação" começa. Se você tem um departamento de Quality Assurance, você pode estar falando mais para eles do que para seu gerente de projeto sobre os erros em seu código. Se você não testar seu código completamente, você pode desenvolver mais que código, e possivelmente uma má reputação.
  12. Celebre todo sucesso. Se um programador feliz te pedir para vir ver sua obra extraordinária de código, mesmo que você já tenha visto um trecho de código como esse mais de 100 vezes em suas experiências, comemore o sucesso desse desenvolvedor.
  13. Faça revisão de código frequentes. Em projetos e pessoalmente. Na empresa, você sempre terá as revisões de código de quão bem você codificou alguma coisa. Não olhar para isso como pessoas crucificando o seu estilo de codificação. Pense nisso como uma crítica construtiva. Quando estiver na revisão, pergunte: "Como eu poderia ter feito melhor?" Isto irá acelerar o seu aprendizado e torná-lo um programador melhor.
  14. Relembre seu código antigo. Existem duas maneiras de olhar para código antigo: "Eu não posso acreditar que eu escrevi este código" e "Eu não posso acreditar que eu escrevi este código." A primeira afirmação é muitas vezes de nojo e perguntando como você pode melhorá-lo. Você ficaria surpreso como um código antigo pode ser ressuscitado em uma rotina possível e melhor, ou até mesmo um produto inteiro. A segunda declaração é de espanto e realização. Desenvolvedores têm suas conquistas de código, aquelas que eles concluíram e todos tomaram conhecimento.
  15. Humor é necessário. Em meus 20 anos de desenvolvimento, eu nunca conheci um programador que não tem um senso de humor decente. Na verdade, nesta indústria, é uma exigência.
  16. Cuidado com o programador sabe-tudo, programador possessivo e o programador inexperiente. O programador sabe-tudo tenta relegar você em vez de trabalhar como um membro da equipe. O programador possessivo cria o código e não quer partilhar com ninguém. E o programador inexperientes pede ajuda constantemente (a cada dez minutos) ao ponto que o código desenvolvido é seu, não dele.
  17. Jamais um projeto será simples. Se alguém precisa de um site de 3 páginas com o Microsoft Access no início, ele acaba se tornando um site de 15 páginas com o SQL Server, um fórum e um CMS personalizado (Content Management System).
  18. Nunca tome nada como garantido. Se você levar em um projeto simples, você pode pensar que uma determinada seção será fácil de concluir. Não pense, mesmo por um momento. Ao menos que você tenha uma classe, componente, ou um pedaço de código já codificados, testado exaustivamente e em produção. A partir de um projeto existente, não pense que será fácil.
  19. Software nunca está acabado. Um colega programador me disse uma vez que o software nunca está acabado, está "temporariamente concluído". Bons conselhos. Se o cliente ainda está usando um programa que você escreveu e tem resistido ao teste do tempo, existem boas chances de você ainda estar atualizando-o, o que não é uma coisa ruim. Mantém seu emprego.
  20. A paciência é definitivamente uma virtude. Quando os clientes, amigos ou membros da família usar um PC, ficam frustrados e partem para bater no computador. Eu continuo dizendo a todos: "você está controlando o computador, não o contrário". Você precisa ter um certo nível de paciência para programar computadores. Tão logo os programadores entendem o que fizeram de errado, eles dizem: "Ah, é por isso que estava acontecendo isso".


Qualquer sugestão, deixem seus comentários.

4br4ç05,
nglauber