domingo, 24 de abril de 2011

Curso de Android no Ceará

Olá povo,

Em parceria com a Cassic ministrarei os cursos intermediário e avançado de Android. O curso com duração de 40 horas/aula acontecerá nos dias 21 e 22 de maio, e 04 e 05 de junho em Juazeiro do Norte, região do Cariri no Ceará.
Os interessados podem garantir sua vaga aqui com um ótimo desconto.






4br4ç05,
nglauber

domingo, 17 de abril de 2011

Background gradiente no Android

Olá povo,

Post rápido do domingo: como colocar um background gradiente no Android. No diretório res/drawable de um projeto Android podemos colocar arquivos XML também. Esses arquivos devem fazer referências a imagens ou definir formas geométricas como círculos e retângulos. Vejam abaixo como definir um "quadrado" com bordas arredondadas e com cor gradiente.

<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#0000FF"
android:endColor="#000000"
android:angle="90"
android:type="linear" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
<corners
android:radius="30dp" />
</shape>


O arquivo acima, como já dissemos, deve ser criado dentro da pasta res/drawable do seu projeto. Nós o nomeamos como meubg.xml, e nele estamos definindo um retângulo de cor gradiente, começando de azul para preto. O angulo do gradiente é de 90 graus do tipo linear. Determinamos um espaçamento das borda para o componente que utilizar esse arquivo como background. Por fim, determinamos uma borda arredeondada com raio de 30dp.
Se colocarmos esse drawable como background (android:background="@drawable/meubg) de um LinearLayout por exemplo, o resultado ficará como abaixo:



Dúvidas? Consultem a documentação do Android aqui.

4br4ç05,
nglauber

segunda-feira, 11 de abril de 2011

Android: Dicas 2

Olá povo,

Estar à frente de uma sala de aula me propicia o benefício da "ensinagem" (ensino + apredizagem) onde ao mesmo tempo em que ensino, aprendo bastante com meu alunos. Esse post foi totalmente formado por dúvidas dos meu alunos do TECDAM, e que foram resolvidos por eles próprios. Bom para todos, que aprendemos juntos.

Dica 1 (crédito para Gustavo - olha ele aí - Coutinho)
O Android tem um sistema de i18n (internacionalização) muito bacana, onde basta prover recursos através de notações pré-definidas pelo próprio Android em pastas do projeto. Inclusive falei disso nesse post. Os recursos são selecionados automaticamente de acordo com configurações do aparelho. Mas, e se eu quiser determinar o idioma da aplicação? Segue abaixo o código que força um idioma para nossa aplicação.


String languageToLoad = "es"; // espanhol
Locale locale = new Locale(languageToLoad);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
Resources res = getBaseContext().getResources();
res.updateConfiguration(config, res.getDisplayMetrics());
setContentView(R.layout.main);


Dica 2 (crédito para Rogério Casal, aluno do Unibratec)
Nesse post, mostrei como limitar os caracteres utilizando expressões regulares. Porém, podemos fazer a mesma coisa utilizando a propriedade digits do componente EditText, basta colocar os caracteres que você quer permitir.

Dica 3 (crédito para Augusta "Nariquinha")
É comum colocarmos os identificadores dos componentes adicionados nos arquivos de layout utilizando @+id/idDoComponente. Entretanto, quando ficamos com muitas telas ou quando começamos a importar layouts pode haver uma confusão ou até um conflito com esses identificadores. Uma abordagem legal, seria substituir o "id" por algo mais inteligível como "cadpessoa". Por exemplo a definição do EditText abaixo seria perfeitamente válida:
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+cadpessoa/edtNome"/>

E no código Java, poderíamos acessar esse id usando:
EditText edt = 
(EditText)findViewById(R.cadpessoa.edtNome);


Dica 4 (crédito para Leonardo Santos)
Essa eu não arrumei uma justificativa plausível. Quando fui executar um arquivo MP3 no emulador o código abaixo funcionou perfeitamente.
MediaPlayer player = new MediaPlayer();
player.setDataSource("file:///mnt/sdcard/musica.mp3");
player.prepare();
player.start();

Porém, quando coloquei no telefone não funcionou. O erro "Playing Audio - error: PVMFErrNotSupported (Prepare failed.: status=0x1)" era exibido. Após uma busca rápida no Google, a solução foi simples. Ao invés de passar a String diretamente, é passado um objeto FileDescriptor obtido através de um FileInputStream como abaixo.
MediaPlayer player = new MediaPlayer();
FileInputStream fis =
new FileInputStream("/sdcard/musica.mp3");
player.setDataSource(fis.getFD());
player.prepare();
player.start();

Como disse acima, não sei o porquê do erro.

Dica 5 (crédito para Pedro CC)
Para gerar a chave para utilizar a API do Google Maps no Android é preciso usar a ferramenta keytool que vem no JDK (Java Development Kit). Porém, um problema que acontecia constantemente quando ia gerar essa chave era a mensagem "erro de keytool: java.lang.IllegalArgumentException: unknown format type at". Ou seja, erro sem explicação... Esse problema ocorre aparentemente por conta das configurações regionais. Quando elas estão em português, esse erro acontece, em inglês não.
No Windows, vá até o Painel de Controle, e em Configurações Regionais altere o formato das informações para inglês. No Mac vá até System Preferences, em seguida selecione Language e Text, na aba Formats selecione Inglês. [Editado com o comment de Eric Braga] No Linux, basta setar a variável de ambiente LC_ALL usando (export LC_ALL="en_US").

Dica 6 (crédito para Ricardo Gilson)
Se quiser fazer com que cada atributo de cada tag de um arquivo XML fique em uma linha separada no Eclipse, acesse o menu
Windows>Preferences>XML>XML Files>Editor e marque a opção "Split multiple attributes each on a new line". Depois é só abrir seus arquivos de Layout e pressionar Ctrl+Shift+F para que o arquivo fique bem mais agradável.

Agradeço a todos pelas dicas e se dei os créditos errado ou faltou alguém é só avisar :)

4br4ç05,
nglauber

sábado, 9 de abril de 2011

Artigo "Comunicação via Bluetooth no Android"

Olá povo,

A revista Web Mobile Magazine edição 35 traz em sua capa mais um artigo de minha autoria para a publicação da DevMedia: "Comunicação via Bluetooth no Android".

Nesse artigo explico o funcionamento da Bluetooth API através de um exemplo simples e prático: uma aplicação de chat. Através desse aplicativo podemos demonstrar como estabelecer a conexão e trocar informações entre os dispositivos utilizando esta tecnologia.

Espero que vocês gostem.

4br4ç05,
nglauber

quarta-feira, 6 de abril de 2011

Artigo "Gestures no Android"

Olá povo,

A revista Java Magazine edição 90, trás uma matéria sobre Reconhecimento de Gestos no Android. Ela foi escrita por mim, em parceria com o meu colega Bruno Vinícius.

Neste artigo, apresentamos através de um exemplo prático, como reconhecer gestos usando a Gestures API do Android. Serão apresentados os conceitos e as ferramentas necessárias, assim como os desafios de sua utilização.

Espero que vocês gostem.

4br4ç05,
nglauber

sábado, 2 de abril de 2011

Animações no Android (2)

Olá povo,

Nesse post vou mostrar como utilizar algumas animações prontas que o Android disponibiliza em sua API no pacote android.view.animation. Começe criando um novo projeto e deixe o seu arquivo de layout conforme abaixo:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<Spinner
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/spnAnimation"/>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play"/>

<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="fill">

<ImageView
android:layout_width="wrap_content"
android:id="@+id/imageView1"
android:layout_height="wrap_content"
android:src="@drawable/icon"/>
</FrameLayout>
</LinearLayout>


Nosso arquivo de layout é bem simples. Ele consta de um Spinner (combobox) que vai listar as animações disponíveis na API. Em seguida temos um botão que iniciará a animação. Logo abaixo, temos uma ImageView dentro de um FrameLayout, utilizamos esse layout pois uma das animações permite que posicionemos uma View em uma posição absoluta da tela (X e Y). Mas você pode estar se perguntando: se vai usar posição absoluta, porque não usar o AbsoluteLayout?
O AbsoluteLayout é considerado obsoleto (deprecated) uma vez que podemos utilizar o FrameLayout que faz a mesma função, só que ao invés de usarmos as propriedades layout_x e layout_y, utilizamos layout_marginLeft e layout_marginTop respectivamente.

Explicado o layout, vamos agora ao código que reliza as animações. Abra a activity da aplicação e deixe-a de acordo com a classe abaixo:


public class ExemploAnimationActivity
extends Activity
implements OnClickListener, AnimationListener {

private Spinner spnAnimation;
private ImageView imgView;

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

String[] animations = {
"Alpha", "Rotate", "Scale", "Translate"};

ArrayAdapter<String> adapter =
new ArrayAdapter<String>(
this,
android.R.layout.simple_spinner_item,
animations);
spnAnimation =
(Spinner)findViewById(R.id.spnAnimation);
spnAnimation.setAdapter(adapter);

findViewById(R.id.button1).
setOnClickListener(this);

imgView =
(ImageView)findViewById(R.id.imageView1);
}

@Override
public void onClick(View v) {
Animation animation = null;
Interpolator interpolator = null;

switch (spnAnimation.getSelectedItemPosition()) {
case 0: // Alpha
animation = new AlphaAnimation(1, 0);
break;

case 1: // Rotate
interpolator = new OvershootInterpolator(2);
animation = new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setRepeatCount(3);
break;

case 2: // Scale
interpolator = new AccelerateInterpolator(4);
animation = new ScaleAnimation(
1, 3, 1, 3,
Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, 0);
break;

case 3: // Translate
interpolator = new BounceInterpolator();
animation = new TranslateAnimation(
Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, 200,
Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, 200);
}
animation.setInterpolator(interpolator);
animation.setAnimationListener(this);
animation.setDuration(3000);
imgView.setAnimation(animation);
animation.start();
}

@Override
public void onAnimationEnd(Animation animation) {

switch (spnAnimation.getSelectedItemPosition()) {
case 0: // Alpha
animation = new AlphaAnimation(0, 1);
break;

case 1: // Rotate
return;

case 2: // Scale
animation = new ScaleAnimation(3, 1, 3, 1);
break;

case 3: // Translate
animation = new TranslateAnimation(
Animation.ABSOLUTE, 200,
Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, 200,
Animation.ABSOLUTE, 0);
}
animation.setDuration(3000);
imgView.setAnimation(animation);
animation.start();
}

@Override
public void onAnimationRepeat(Animation animation) { }

@Override
public void onAnimationStart(Animation animation) {}
}


Nossa classe implementa as interfaces OnClickListener e OnAnimationListener, a primeira nós já conhecemos, a segunda serve para notificar das mudanças de estado da animação. Com ela é possível saber quando a animação começou, terminou e quando foi repetida. Esses método estão declarados no final da nossa classe.
No método onCreate criamos um adapter para preencher nosso spinner com as 4 animações do pacote de animações (existe mais uma que é AnimationSet que não usarei aqui) . Depois setamos o evento de clique do botão e por fim obtemos a referência da ImageView que iremos animar.
No método onClick é que as coisas começam a ficar animadas (entederam o trocadilho? animações no Android... coisas animadas... dãããã... :)

Começamos declarando um obejto Animation que será atribuída mais adiante à nossa ImageView. Logo abaixo, declaramos um Interpolator. Esse objeto (opcional) serve para aplicar um efeito mais legal à nossa animação.

A instrução switch inicializa a animação de acordo com a opção selecionada no Spinner. A primeira opção cria um AlphaAnimation que recebe como parâmetros a transparência inicial e final (1 quer dizer opaco, 0 transparente). Nesse caso, essa animação fará a ImageView desaparecer completamente.
A segunda opção cria uma RotateAnimation que fará a View girar. Os parâmetros são o ângulo inicial e final, e uma posição X e Y para servir de eixo para a rotação. No nosso exemplo passamos uma posição relativa à imagem (RELATIVE_TO_SELF) baseada na metade do seu tamanho (0.5). Para essa animação criamos um interpolator que faz com que a animação faça um efeito antes parar (ficou difícil de explicar esse interpolator - vocês testando é melhor :).
A terceira animação é a ScaleAnimation que fará com que a ImageView aumente de tamanho. Ela recebe como argumento a proporção inicial e final em X, depois a proporção inicial e final em Y, e assim como a RotateAnimation, pede uma âncora para basear o aumento da imagem. No nosso caso colocamos a posição absoluta 0,0... O Interpolator usado aqui fará a animação acelerada.
A última animação é a TranslateAnimation que serve para movimentar a nossa ImageView para uma nova posição X e Y. Aqui informamos que ele deve se movimentar da posição X inicial 0 para 200 final, e Y inicial 0 e final 200. O Interpolator utilizado é o BounceInterpolator que fará o efeito de quicar.
Após iniciar o objeto animation, setamos seu interpolator, sua duração para 3 segundos (3000 milisegundos) e seu listener. Depois associamos a animação à ImageView e mandamos iniciar a animação. Quando a animação terminar, o método onAnimationEnd será chamado e fará a animação ao contrário. Analisem o código baseado na explicação acima que eu acho que vocês vão entender :)

Testem esse exemplo, explorem os Intrpolators com outras animações e não esqueçam de deixar seus comentários.

4br4ç05,
nglauber