Animações no Android Part 2 – Property Animation

Standard

Depois do primeiro post desta mini série onde escrevi um pouco sobre como funcionam as animações no android em particular o mecanismo mais básico que permite animar Views, iremos continuar a busca do conhecimento para que possamos criar aplicações que aplicam o interessante principio de “Meaningfull motion” do Material design e oferecer uma experiência extra aos utilizadores.

Este post será focado no segundo mecanismo para poder criar animações disponível no android denominado Property Animation.

O property Animation é um framework disponibilzado por default no android e foi criado com o objectivo de oferecer mais robustez e controle ao aplicar animações. Ao contrário do View animation que vimos no post anterior, o Property Animation permite que qualquer objecto seja animado, seja este visível na UI ou não.

O funcionamento do Property Animation parte do princípio que é possível animar qualquer atributo de um objecto por um certo período de tempo e definir as seguintes características importantes :

  • Duração: O tempo total de execução da animação que se não alterado fica com o tempo default de 300ms.
  • Interpolação do Tempo : Como os valores da animacao serão calculados em função do tempo.
    Assim como na física que podemos ter um corpo a viajar em Movimento rectilíneo uniforme significando que este vai viajar de um ponto ao outro em intervalos de tempos iguais e a velocidade será constante, se definimos uma interpolação linear a uma animação os valores serão constantes e teremos um comportamento similar ao do corpo da física.
    O android dispõe de varias interpolações que se formos ver o código, não passam de formulas que podem ser copiadas e modificadas seguindo para satisfazer qual que seja a nossa vontade.
  • Comportamentos :Podemos definir  o que acontece a aplicação quando esta chega ao fim. Repete? Repete revertendo os valores? Quantas vezes a animação deve repetir?
  • Coreografia: Assim como magia, boas animações são nada mais e nada menos que mover imagens de forma rápida e organizada a uma frequência que possamos enganar o cérebro a ver algo diferente do que realmente esta acontecer.
    Tendo isto em mente, este framework nos permite definir como as animações serão coreografadas.

O Property Animation pode ser um pouco complicado de entender  logo de princípio pois possui vários componentes interligados que podem criar alguma complicação.
Para criar animações, este framework oferece 3 classes importantes que estendem a classe Animator:

  • ValueAnimator: Esta classe apenas serve para calcular os valores da propriedade a animar.
    Esta classe sabe quais são os valores no início, fim e no momento em que esta está a decorrer.
    Apesar desta classe possuir a parte mais importante do sistema de animações, por si só não é suficiente para nos ajudar com a tarefa de implementar animações de forma fácil, pois para actualizar os valores calculados a propriedade do objecto com que estamos a trabalhar, teremos de colocar um listener que fica a escuta das mudanças destes valores e manualmente fazer o trabalho de actualizar estas propriedades.
  • ObjectAnimator: Esta classe vem para oferecer mais facilidade em relação a ValueAnimator pois esta nos permite animar um objecto, especificando de uma única vez, o objecto, a propriedade a animar, os valores de início e fim e o cálculo dos valores como a actualização dos valores da propriedade será feita de forma mágica.
    Tanta facilidade vem com algumas desvantagens e uma delas é  que a classe ObjectAnimator permite-nos animar valores de propriedades de  um certo tipo de dados como int, float, argb (muita das vezes e o que precisamos) que podem não ser o suficiente caso o nosso objecto tenha uma propriedade com forma muito especifica de calcular os valores.
    Nestes casos em que nos encontramos com essas particularidades nos nossos componentes, é  muito melhor utilizar o ValueAnimator e definir um TypeEvaluator que especifica como calcular os valores da propriedade do objecto com que trabalhamos.
  • AnimatorSet: Como escrevi acima, animações assim como magia não passam de movimentos coordenados que enganam o nosso cérebro e nos fazem ver algo que não corresponde a realidade. A classe AnimatorSet como o nome ja sugere, é  a classe responsável por coreografar animações, isto é , sequência do conjunto de animações, duração, etc.

 

Passando para a parte interessante, vamos começar a mostrar como é  possível fazer algumas animações utilizando este framework  e vamos nos focar em 2 exemplos em que o primeiro será basicamente ré-implementar as animações do post anterior utilizando a classe ObjectAnimator e o segundo criar uma animação que poderia ser utilizada em uma app em produção.

Para  começar o código abaixo e um método que nos permite animar a visibilidade da view passada para ele.

public void fadeAnimation(View view){
    ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(view,"alpha",0f,1f);
    fadeAnim.setDuration(ANIM_DURATION);
    fadeAnim.start();
}

Olhando para as diferentes partes deste método, temos apenas um ObjectAnimator em que nos especificamos através do método ofFloat que queremos animar valores de uma propriedade do tipo float  e passamos o objecto que queremos animar, especificamos o nome da propriedade (Visibilidade ou alpha),o valor inicial e final da animação.

Com os valores principais da animação definidos, podemos definir outros valores como a duração e a interpolação. Caso deixemos a animação sem estes atributos adicionais e invocamos o método start, esta será executada por default por 300ms e utilizando uma interpolação linear.

O segundo exemplo ainda na implementação de animações simples é a aplicação de Scale em que diminuímos ou aumentamos o tamanho de uma view.

Este exemplo é particularmente interessante pois para animar o tamanho de uma view temos de animar duas propriedades : a largura (SCALE_X) e o comprimento (SCALE_Y).

Olhando para o exemplo anterior e o que estivemos a falar durante o post, espero que todos tenham tomado atenção e notado  que ate então podemos implementar esta animação criando dois object animators em que cada um anima uma das propriedades e utilizar um Animator Set para coreografar as duas animações para que comecem ao mesmo tempo (Desafio voces a implementar esta animacao desta  forma).

Apesar de ser um método valido, teríamos muito código desnecessário pois o android oferece a class ProperValueHolder que nos permite guardar os valores de cada propriedade que queremos animar  e por fim passar este objecto para um único object animator como mostra o código abaixo.

public void scaleAnimOneObjectAnimator(View view){
    PropertyValuesHolder valueX = PropertyValuesHolder.ofFloat("scaleX",0,1);
    PropertyValuesHolder valueY = PropertyValuesHolder.ofFloat("scaleY",0,1);
    ObjectAnimator scale = ObjectAnimator.ofPropertyValuesHolder(view,valueX,valueY);
    scale.start();
}

Com todos os pontos cobertos ja podemos implementar uma aplicação minimamente interessante para colocar em uma app em produção.

giphy.gif

Como mostra a figura acima, a animacao simplesmente consistem nessas barras horizontais que serão animadas a propriedade alfa para criar um efeito como houvesse um movimento da esquerda para direita( Acredito que no facebook haja uma animacao similar).

Dividindo esta animacao em partes, temos primeiro a UI em que as barras nao passam de Views dentro de um linear layout como mostra o codigo abaixo:

Nota : Foi utilizado o DataBinding neste exemplo e para entender melhor algumas partes do código sugiro que leiam o post deste BLOG e o mais detalhado escrito no MEDIUM.

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        >

        <View
            android:id="@+id/first"
            android:layout_width="8dp"
            android:layout_height="30dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />

        <View
            android:id="@+id/second"
            android:layout_width="8dp"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />

        <View
            android:id="@+id/third"
            android:layout_width="8dp"
            android:layout_height="60dp"
            android:background="@color/colorAccent"
            android:layout_margin="8dp"
            android:alpha="0"
            />

        <View
            android:id="@+id/fourth"
            android:layout_width="8dp"
            android:layout_height="45dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
        />
        <View
            android:id="@+id/fifth"
            android:layout_width="8dp"
            android:layout_height="30dp"
            android:background="@color/colorAccent"
            android:layout_margin="4dp"
            android:alpha="0"
            />

    </LinearLayout>
</layout>

Depois de definirmos o layout, com as views que queremos animar, a parte a seguir não e novidade e simplesmente temos de mapear estas views a instâncias no java para que possamos utilizar ao criar as animações.
Esta animacao foi dividida em 2 métodos em que o primeiro e simplesmente um helper para ajudar a criar um ObjectAnimator que define a animação de cada barra e o segundo método que simplesmente cria e coreografa animação como um todo

public ObjectAnimator animateStripe(View view){
    ObjectAnimator animator = ObjectAnimator.ofFloat(view,"alpha",0f,0.8f);
    return animator;
}

public void startLoadingAnimation(){

    ObjectAnimator firstStripeAnimator = animateStripe(first);
    ObjectAnimator secondeStripe = animateStripe(second);
    ObjectAnimator thirdStripeAniamtor = animateStripe(third);
    ObjectAnimator fourthStripeAnimator = animateStripe(fourth);
    ObjectAnimator fifthStripeAnimator = animateStripe(fifth);
    
    AnimatorSet set = new AnimatorSet();
    set.playSequentially(firstStripeAnimator,secondeStripe,thirdStripeAniamtor,fourthStripeAnimator,fifthStripeAnimator);
    set.setInterpolator(AnimationUtils.loadInterpolator(getActivity(),android.R.anim.accelerate_interpolator));
    set.setDuration(150);
    set.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
                animation.setStartDelay(100);
                animation.start();
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    set.start();
}

O segundo método acima é o mais interessante pois inclui o AnimatorSet para coreografar a animação de todas barras para que estas seja executadas de forma sequencial.
Como podem ver podemos também definir a interpolação e a duração para o conjunto e o AnimatorSet saberá qual o tempo e como deverá calcular os valores de cada animação de forma individual para que estas se encaixem de forma perfeita ao tempo e a interpolação definida.
Por fim, adicionamos um listener ao AnimatorSet que nos permite capturar eventos dos diferentes momentos da animação  que nos permitem fazer varias coisas como ré-iniciar a animação sempre que ela chega ao fim no nosso exemplo.

E com este ultimo exemplo chegamos ao fim de mais um post e até então foi possível cobrir um mecanismo importante e que serve como base para maior parte  das animações no android.
Iremos continuar com a série, explorando o que mais pode ser feito utilizando este mecanismo e brevemente falar sobre Drawable Animations já que estas foram lançadas no novo update da android support library.

Ate a próxima,

DM

 

 

Advertisements

Google Sign In for Android-Recebendo utilizadores sem fricção

Standard

Ao desenvolver apps, normalmente temos 2 objectivos na cabeça em que o primeiro é fazer uma app resolva um problema e segundo fazer com os utilizadores a utilizem constantemente,possam customizar e  ter a mesma experiência com amigos.

Infelizmente atingir o segundo objectivo torna-se muito complicado e um processo que normalmente consome muito tempo pois criar um sistema de login do zero, apesar de ser uma ideia tentadora pois podemos sempre ter a liberdade de colocar os campos que queremos e aumentar o nosso ego como programadores é uma péssima ideia pelos seguintes motivos:

  • Os utilizadores ja tem outros serviços com diferentes contas e aumentar mais um username e password a lista de coisas a memorizar é algo que não lhes interessa(Foram feitos alguns estudos que provam isto)
  • Se o objectivo e apenas criar uma app super cool, com todos os detalhes de encriptação e segurança que serão necessários para que possamos garantir que os dados do utilizador estão seguros, não será possível desenvolver um sistema de login em TEMPO (recurso bastante precisos) útil.

Este e outros problemas podem ser facilmente resolvidos utilizando a API Sign in for android da Google(Antigamente conhecida como Google+ Sign in API) e iremos utilizar este post para falar um pouco sobre o seu funcionamento e como implementar.

Esta API utiliza um protocolo chamado Oauth que de forma simples é definido no site oficial como “Um protocolo que oferece um padrão para autorização segura na web,mobile ou aplicações desktops de forma simples”(Veja este video do Google IO 2012 sobre Oauth).

Indo directo para o ponto do nosso post, para começar a integrar o login utilizando a Google Sign in for android API temos de seguir os seguintes  3 passos:

1.Obter o ficheiro de Configuração

O primeiro passo consiste, na obtenção de um ficheiro de configuração por parte do Google que contem informação como o oauth client id que será utilizado para identificar a nossa app instalada em qualquer dispositivo como um dispositivo autorizado para interagir com esta API.

Para obter este ficheiro basta que simplesmente ir para este LINK e preencher os campos App Name que é o nome do projecto que será criado no google developers console ou se ja tiver um pode seleccionar da lista e  o Package name onde devemos colocar o nome do pacote da aplicação como está no ficheiro androidManifest.xml .

Como dizia acima, o Google tem  varias APIs e como mostra a figura abaixo, desta forma é possível ter o mesmo ficheiro na  mesma app com a configuracao de outras  APIs como a do Google Cloud Messaging(Vira um Post ja ja) e Analytics.

Como para este post queremos só utilizar o Google Sign in, devemos clicar sobre o icon do Google Sign in que nos pede a inserir  o certificado SHA-1 que é gerado pela chave utilizada para certificar a aplicação.(Se estiver só a testar a app o ficheiro de keystore estará na pasta .android->android-debug.keystore e caso estejam para fazer o release final ou actualizar uma app no playstore, utilizem o ficheiro *.keystore que só vocês sabe onde está. Para verficar o SHA-1 utilize o comando keytool no command prompt keytool -list -v -keystore keystore.jks onde o “keystroke.jks” é o caminho relativo ou absoluto ao ficheiro dependendo de onde ir a querer executar este comando.)

Isto porque como disse anteriormente, a Google precisa de formas para identificar o cliente que esta a fazer os pedidos e porque uma aplicação no playstore só pode ter um id único que é o package name e um certificado SHA-1 da chave único utilizado ao gerar o APK estes dois elementos formam a combinação perfeita e única.

Após preencher os campos de seguida devemos clicar no botão no fim da página que diz Generate configuration files e na página que abre de seguida clicar no Download google-services.json.

2.Copiar o ficheiro para o projecto e configurar o projecto

Após baixar o ficheiro de configuração só precisamos de seguida copiar este ficheiro e colocar dentro da raiz do modulo “app” do nosso projecto (No mesmo nível que o ficheiro build.gradle).

Com o ficheiro já no projecto, o próximo passo é primeiro configurar o plugin do Google Play Services e as dependências necessárias no gradle.

Dentro do projecto, existem 2 ficheiros build.gradle em que um encontra-se na raiz do projecto que é o ficheiro de configurações globais do projecto e o segundo dentro do modulo “app” que é o ficheiro de configuração especifica para a android app. (Caso se crie um novo modulo com um projecto do app engine, este também terá o seu build.gradle com as suas configuração especificas)

Devemos definir o plugin do Google Play Services no ficheiro build.gradle a nível do projecto especificando a linha  classpath ‘com.google.gms:google-services:2.0.0-beta7’  dentro da secção dependencies do  ficheiro assim:
dependencies {
classpath ‘com.android.tools.build:gradle:2.0.0-beta7’
classpath ‘com.google.gms:google-services:2.0.0-beta7’

}
}

Notem que a versão, ir a sempre mudar e ir a pedir que altere a medida que novas actualizações do gradle plugin e do android studio vão sendo lançados por isso certifiquem-se de que na altura que estiverem a ler a este post, a versão do gradle plugin como do google play services é igual a esta e caso não seja modifiquem para a versão em utilização no momento.

Depois de definir o plugin, temos de ir ao ficheiro build.gradle do modulo e definir o plugin assim como as bibliotecas que serão necessárias para utilizar a API  Sign in for Android. Para definir o plugin basta simplesmente colocar esta linha apply plugin: ‘com.google.gms.google-services’.

A primeira vez que testei esta nova forma de configuração, levei horas pois foi intuitivo para mim aplicar o plugin no ficheiro logo por baixo da linha que aplica o plugin com.android.application  e tive vários erros. Fui tentando e a solução que encontrei testando e em varias respostas no stack foi que simplesmente esta linha deveria ser a última no ficheiro sem nenhuma explicação aparente.

Porque o Google Sign in for android assim como muitas outras e algumas das APIs mencionadas neste post fazem parte do Google Play Services, e para utilizar qualquer uma delas temos de definir estas bibliotecas no nosso projecto assim como é feito com a design support library e outras. Para definir o Google Play Services nas dependências, podemos fazer de 2 formas em que a primeira nós importamos todas estas APIs para o nosso projecto especificando apenas compile ‘com.google.android.gms:play-services:8.4.0’
e a segunda especificando de forma exacta a API a utilizar, neste caso Auth assim: compile ‘com.google.android.gms:play-services-auth:8.4.0’

A segunda forma é a mais aconselhável pois se utilizamos muitas outras bibliotecas, corremos o risco de adicionar todos os métodos das diferentes APIs dentro do Google Play Services e iremos atingir o limite dos métodos  de 65K  definido pelo DEX  que ir a gerar um erro e não conseguiremos compilar o projecto. Para referência, veja a lista com descrição de outras APIs AQUI.

O último passo na configuração é ir ao ficheior androidManifes.xml e definir as permissões <uses-permission android:name=”android.permission.GET_ACCOUNTS” /> para que a nossa app possa ter permissão para aceder as contas configuradas e disponíveis no dispositivo.

3.Implementar o Google Sign in for Android

Finalmente temos a configuração toda feita e podemos passar para a parte interessante da implementação.
Vamos começar por desenhar uma interface gráfica,  e até para isso esta API facilitou-nos o trabalho ao oferecer um botão todo customizado que vem já com a mensagem “iniciar sessão com o Googe” que é  automaticamente traduzida para qualquer linguagem que esteja o dispositivo como mostra a imagem abaixo.

Screenshot_20160321-202549 (1)

Definir este botão pode ser feito simplesmente da seguinte forma:

<com.google.android.gms.common.SignInButton
    android:id="@+id/sign_in_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|center"
    android:layout_marginBottom="?actionBarSize"
    />

e como podem ver no codigo acima, podemos definir todos atributos que achamos necessários como margens, posicionamento e o id que será utilizado ao criar uma instância deste botão em código para que possamos fazer coisas como definir um listener.

Com o botão colocado na interface gráfica, o próximo passo e fazer com ao clicar realmente comecemos como o processo de login utilizando a API.

Para fazer o login utilizando o Google Sign in é indispensável que utilizemos as classes GoogleApiClient e GoogleSignInOptions em que primeira é  utilizada para conectar ao Google Play Services(Utilizarão esta classe sempre que quiserem utilizar a maioria das APIs dentro do Google Play Services ) e a segunda e utilizada para definir as opções a utilizar com o GOOGLE_SIGN_IN.

Dentro do método onCreate da activity ou fragment ou dentro de um método de conveniência que será invocado no onCreate, vamos cometar por configurar estas duas classes como mostra o código abaixo:

public void setUpGoogleApiClient(){
    
    //GoogleSignInOptions
    gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .requestId()
            .build();


    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this, this /* OnConnectionFailedListener */)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();
}

Começando pelo GoogleSignInOptions, simplesmente utilizamos o Builder para constuir um objecto com as configurações que nós precisamos.

Passar só a variável estática GoogleSignInOptions.DEFAULT_SIGN_IN ao construtor do builder, teremos uma configuração básica que nos ira retornar alguns campos básicos, mas a medida que vamos precisando de mais informação podemos explicitamente invocar os métodos com o prefixo request como o requesScopes  que permite que possamos aceder a diferentes escopos para a conta do utilizador como ficheiros autorizados no Google drive etc.

Logo de seguida configuramos o GoogleApiClient em que passamos o context ao constructor do Builder, definimos que o ciclo de vida será automaticamente gerido passando ao método enableAutoManage o fragment ou activity em que estamos a trabalhar e o listener OnConnectionFailedListener que nos obriga a implementar o método onConnectionFailed para permitir que possamos capturar capturar o momento que o GoogleApiClient falha por algum motivo.

Por fim adicionar uma Api, neste caso definindo que é a API do Google Sign In e passar o objecto GoogleSignInObject criado anteriormente.

Com tudo isto pronto, podemos criar um método signIn() que será invocado ao clicar o botão e dentro dele iniciar o processo de login, criando uma nova Intent onde passamos a Intent de sign in gerada pelo método Auth.GoogleSignInApi.getSignInIntent que recebe o objecto GoogleApiClient que criamos e por fim passar esta mesma Intent ao  invocar o método startActivityForResult sem esquecer de passar um codigo  que sera utilizado para identificar o request no momento de receber a resposta

private void signIn() {
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_SIGN_IN);
}

Até  aqui já é possível ver a API em funcionamento para dispositivos correndo versões do android pré Marshmallow mas teremos o azar de não ver a funcionar para as versões iguais ou posteriores.

Isto porque no Marshmallow foi introduzido um novo modelo de permissões em que o utilizador deve garantir que a app utilize uma certa permissão em tempo de execução dependendo do grau de sensibilidade em relação ao acesso a dados privados do utilizador.

Para este exemplo, criei dois métodos de conveniência para verificar se a permissão foi garantida ou não e o outro para fazer mostrar uma caixa de dialogo ao utilizador a pedir que garanta esta permissão. Este assunto ja tem um post especifico escrito no medium e podem ver mais AQUI.

public boolean isGetAccountsAllowed(){
    return ActivityCompat.checkSelfPermission(this,
            android.Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED;
}

public void requestGetAccountPermission(){
    ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.GET_ACCOUNTS}, PERM_RC);
}

Utilizamos o primeiro método para verificar se a permissão ja foi garantida ao clicar o botão de sign in e fazer o login caso sim ou fazer ao pedido ao utilizador caso não utilizando o segundo método  e depois continuar com o login invocando o metodo signIn(). Assumindo que temos tudo a funcionar bem teremos algo assim:

Screenshot_20160331-044551

Porque acima nos criamos esta caixa dialogo ao invocar o método startActivityForResult, ao seleccionar uma conta, será enviada uma resposta para activity/fragment  e para que possamos capturar devemos implementar o método de callback onActivityResult.

Com estaresposta, utilizando as classes GoogleSignInResult e GoogleSignInAccount, podemos pegar toda informação sobre esta conta que for interessante e que pedimos nas opções GoogleSignInObject que mencionamos mais acima. Este é também o local para mandar o utilizador para uma nova activity que ele só terá acesso depois de fazer o login ou alguma outra acção específica a app em desenvolvimento.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        GoogleSignInAccount signInAccount = result.getSignInAccount();
        String email = signInAccount.getEmail();
    }
}

Sendo assim em apenas alguns passo foi possível implementar o login utilizando a API Google Sign In for Android.

Notem que este post foram omitidos vários aspectos da implementação completa de uma app em produção como verificar em um servidor próprio se um utilizador com por exemplo um e-mail idêntico ja existe, pedir que ele se registe entre outras coisas pois isso pode variar dependendo do caso especifico de cada aplicação e a imaginação de cada desenvolvedor.

Espero que mais uma vez tenha tido uma bom momento lendo este pequeno guia e caso tenham alguma duvida ou sugestão, não hesitem em deixar um comentário para que possamos criar uma conversa em volta do assunto.

Até a próxima!

DM 🙂

 

Animações no Android Part 1 – View Animations

Standard

Com o surgimento do material design no android L vários princípios de design foram criados e dentre eles um dos mais interessantes e o Meaningful Motion.

De forma muito rápida e resumida, Meaningful Motion ou em uma tradução directa, Movimentações com sentindo explica que em uma app, os componentes devem mover-se de forma que dêem uma pista visual ao utilizador sobre o efeito causado pela sua interacção com um determinado componente. Como exemplo disto podemos olhar para a animação que acontece no momento em que fazemos swipe para a direita para marcar  em um e-mail para marcar como lido no inbox, facilmente temos uma pista visual de que aquele elemento esta a sair de cena devido ao movimento do componente para a direita e seu desaparecimento.

A pesar deste principio ter sido muito enfatizado nos últimos meses com a introdução do Material design e boom de apps com animações super interessantes como mostra este showcase das melhores apps com material design em 2015, o android ja possuía mecanismos de animação de componentes bem antes do material design que no fundo consistem na base por detrás dos novos mecanismos utilizados nas super animações em algumas apps no video acima.

Como mencionei acima, o android possui 2 sistemas de animações nomeadamente:

Property Animation que nos permite animar as propriedades de um certo objecto sem que seja necessariamente um componente que ira aparecer na nossa UI(Uma view) e View Animation que é especificamente para animar Views(TextViews,Buttons, etc).

Este post está divido em 3 partes e irá consistir basicamente na explicação dos dois primeiros sistemas de animação mencionados acima e exemplos de como implementar animações cools como as que temos hoje em dia.

View Animation

Para este primeiro post, iremos falar sobre o View animation.

Como brevemente explicado acima, o View Animation System permite-nos animar propriedades de uma View como alpha(visibilidade), scale(tamanho), rotation(rotação), translation(movimentação) de forma individual ou ainda coordenar a animação destas diferentes propriedades de forma sequencial ou simultânea.

A razão pelo qual escolhi este primeiro sistema para iniciar a série, é pelo simples facto deste sistema estar disponível no framework desde as versões mais antigas do framework e acredito que seja a forma mais simples de estabelecer o contacto com um mundo das animações.

Para realizar uma animação sobre uma view utilizando este sistema, podemos fazer de 2 maneiras diferentes:

Definindo a animação no XML

Definir animações pelo XML e super simples e para o fazer basta:

  1. Criar uma pasta dentro da pasta res com o nome anim onde ficaram guardados os ficheiros de xml que definem uma animação.

animfodler

2. Criar um ficheiro XML dentro da pasta anim como mostra a imagem acima. É neste ficheiro que iremos definir qualquer animação que queiramos aplicar sobre uma view. A tag de raiz deste ficheiro pode ser uma animação em especifico como <alpha>(visibilidade), <scale>(tamanho),<rotate>(rotação), <translate>(movimentação horizontal no eixo do X ou vertical no eixo do Y) ou <set> que permite definir e coordenar diferentes animações.

Olhando para a imagem acima e explicando só a animação de visibilidade ou alpha, termos o ficheiro fade_animation.xml com a seguinte estrutura e conteúdo:

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="1"
    android:toAlpha="0"
    android:duration="500"
    >
</alpha>

Como mencionei acima este ficheiro represente uma só animação e temos como a tag de raiz, a animação que pretendemos com atributos específicos que iram definir o seu comportamento. Neste exemplo, os atributos fromAlpha, toAlpha e duration representam a visibilidade  da View no inicio da animação que pode variar de 0(transparente) a 1(totalmente opaco), a visibilidade no fim da animação e a duração  da animação em milisegundos. Cada tipo de propriedade que podemos animar contem atributos diferentes e podem ver com mais detalhes no código disponibilizado no fim do post.

3. No nosso código java, temos só de carregar esta animação utilizando o método loadAnimation disponível na classe AnimationUtils como mostra o sample abaixo:

/*inicia uma animacao sobre uma view passando a view e o id da animacao que e o 
nome do ficheiro xml na pasta anim.*/
public void runAnimationFromXml(View view, int animResId){
    Animation animation = AnimationUtils.loadAnimation(this,animResId);
    view.startAnimation(animation);
}

E com 3 simples passos, vimos como definir e aplicar uma animação sobre uma view.

Uma limitação deste sistema de animações é que ele aplica animação sobre a view mas não modifica ou actualiza os valores no final da animação , isto é, se estivermos a fazer uma animação em que movemos um botão na horizontal 200dp, o botão ira movimentar-se e no fim ao clicar na nova posição em que conseguimos ver o botão,nada ira acontecer pois este não foi realmente desenhado na nova posição que deveria estar.

A forma de resolver isto seria calcular o valor da posição onde o botão estará e actualizar o botão invalidando a posição anterior e desenhando a nova no método onDraw()(Não liguem para isto agora). Este é um dos tópicos que irei abordar com mais detalhes no próximo post sobre o outro sistema de animações e demonstrar porque este é melhor que o que acabamos de ver.

A segunda forma de definir animações directamente pelo código java, passa por utilizar as classes disponíveis como AlphaAnimation, TranslateAnimation dentre outras que seguem o mesmo padrão <nomeDaPropriedade>+Animation para atingir o mesmo efeito. Neste post não irei falar sobre esta forma pois apesar de não ser muito prática, acho que deixa o código muito feio algo que podemos evitar com o primeiro método:)

E de forma muito simples e rápida conseguimos introduzir a um dos tópicos considerado complicado no android(ja foi para mim).
Espero por vocês para as próximas partes e ate la deixem o vosso comentário e partilhem com um amigo que acham que gostaria de ler este post 🙂

Aqui vai um gist com as partes relevantes e mais exemplos : https://gist.github.com/realdm/40ad4b0d121066be499c

DM

DataBinding Library – Part 1

Standard

Hello Devs, com o lançamento do Marshmallow, novas API’s o mundo do android tem sido uma loucura e tem sido difícil escolher de tantas coisas interessantes qual escrever primeiro. Faz algum tempo desde o ultimo post pois estou a trabalhar em um novo look e organização para o blog e muitas outras coisas interessantes que tem acontecido que espero poder partilhar Brevemente.

Voltando ao post, desta vez venho escrever sobre uma biblioteca que particularmente faz muito tempo que estava a espera e agora que chegou e pude trabalhar com ela, decidi partilhar com todos vocês e espero que achem super interessante assim como eu achei.

Para começar a falar da biblioteca, temos de voltar um pouco e olhar para um processo chato sempre que queremos trabalhar com uma View(TextView, EditText, Button, etc) que consiste na criação da view dentro de um ficheiro XML, atribuir um Id a view, ir ao ficheiro de java  fazer o findViewById e no fim podemos então manipular as propriedades destas views. Este processo vem se repetindo milhares e milhares de vezes sempre que fazemos uma aplicação e vezes sem conta olhamos para as nossas classes e descobrimos que há muito código “repetitivo” e chato que poderia não estar ali. Felizmente para resolver este problema e começar a escrever menos código necessário para fazer essa interconexão  entre as interfaces graficas que desenhamos no XML e a lógica da nossa aplicação foi criada a DataBinding Library.

A DataBinding Library permite que possamos definir de forma declarativa uma boa parte da lógica que queremos implementar nas Views directamente do XML.

Esta Biblioteca encontra-se neste momento na versão beta e é compatível com aplicações que pretendem alcançar dispositivos que corram a partir da API 7+ ou versão do android 2.1+.

Passando para a acção , este post estará centrado em 2 pontos:

  1. Configuração inicial da Biblioteca no Projecto
  2. Processo de como fazer a ligação(Bind) de uma View

Indo directo ao primeiro ponto, para começar a utilizar a biblioteca do DataBinding é muito simples e para tal temos de fazer os seguintes passos:

  1. Ir ao SDK Manager e baixar a android support repository.

Support repo

2. Definir a versão do plugin do gradle 1.3.0-beta4 ou superior e o classpath da biblioteca dentro do ficheiro build.gradle do projecto como mostra figura abaixo.

classPath

3. Por fim, temos de fazer o apply do plugin da biblioteca no ficheiro build.gradle do module que queremos utilizar a biblioteca, neste caso o ficheiro build.gradle que se encontra dentro do module app.

Apply Plugin

Com esses 3 passo, temos a Biblioteca do DataBinding configurada e pronta para utilizar na aplicação.

Com tudo configurado, estamos prontos para colocar a mão na massa e vamos começar por abrir um ficheiro de layout , neste caso activity_main.xml que como todos temos visto estaria assim:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ola Nerds"
        />

</LinearLayout>

Em que temos um LinearLayout como o ViewGroup com a raiz da  nossa arvore e com um filho (TextView) dentro.

Para começar a utilizar a biblioteca e escrever as expressões de biding, temos de mudar um pouco a estrutura do ficheiro que nos dará 3 partes importantes:

  • layout: Passa a ser o item mais elevado na estrutura do XML da nossa UI e sem esta tag na poderemos seguir a diante.
  • data : Esta tag encontra-se como um dos filhos de layout e e dentro dela que declaramos as variáveis que iremos precisar de utilizar nos nosso layout.
  • View:  A estrutura que tínhamos no código acima passa a ser um filho de layout. 🙂

A junção destas forma um ficheiro de xml activity_main.xml como o código abaixo:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <!-- Nao foi definida nenhuma variavel, Porque Sera-->
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin">

        <TextView
            android:id="@+id/movieTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Ola Nerds"
            />

    </LinearLayout>


</layout>

Com a estrutura pronta o passo a seguir e criar uma classe seja ela um Bean ou POJO que representa a estrutura dos dados que eventualmente iremos querer fazer a ligação com a nossa UI. Tomemos como exemplo uma forma de definir o titulo de um filme no TextView no layout que desenhamos acima. Primeiro teriamos de criar uma  classe que representasse o filme:

public class Movie {

    private String title;
    private String releaseDate;
    private String backdropPath;
    private int id;

    public Movie(String title, String releaseDate, String backdropPath, int id) {
        this.title = title;
        this.releaseDate = releaseDate;
        this.backdropPath = backdropPath;
        this.id = id;
    }


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getReleaseDate() {
        return releaseDate;
    }


    public void setReleaseDate(String releaseDate) {
        this.releaseDate = releaseDate;
    }

    public String getBackdropPath() {
        return backdropPath;
    }

    public void setBackdropPath(String backdropPath) {
        this.backdropPath = backdropPath;
    }

    public int getId() {
        return id;
    }

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

Com a classe que acabamos de criar, podemos criar uma variável dentro do nosso ficheiro de layout e escrever as expressões que farão a ligação de uma certa view a um objecto da classe criado. Como disse acima, todas as variáveis que precisamos de utilizar dentro do layout, devem ser criadas entre as tags data um item com a tag variable  e definir  o atributo nome que será utilizado para se referir a esta variável(Tal como o nome que damos ao declarar uma variável no java ou outra linguagem) e o atributo type  que representa o tipo da variável que estamos a definir(O nome da classe e o pacote em que esta se encontra) da seguinte forma:

<data>
    <variable name="movie" type="com.app.blog.databindingexample.models.Movie"/>
</data>

Nota : É possível definir N variáveis dentre as tags data .

Logo após definir a variável, ja a podemos utilizar para escrever as nossas expressões. O primeiro ponto a saber ao escrever expressões de ligação nos ficheiros de layout, e que estas são escritas entre “{}” como mostra o código abaixo em que dizemos que o texto do TextView deve ser populado com o titulo do filme.

<TextView
    android:id="@+id/movieTitle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{movie.title}"
    />

Até este ponto nos temos o nosso layout com as expressões escritas  e falta apenas fazer a actual ligação entre o objecto que contem os dados e os componentes no ficheiro e layout. Neste ultimo passo é que nos notamos a magia desta biblioteca pois a partir do XML ela gera uma classe de Ligação(BindingClass) que em poucas linhas de código podemos popular os dados nos nossos componentes. A classe gerada fica com o nome do ficheiro de layout adicionando a palavra Binding e e utilizada para fazer o binding com os seguintes passos:

1.Criar um objecto  da classe gerada para  fazer a ligação, neste caso a classe terá o nome ActivityMainBinding pois foi gerada a partir do ficheiro activity_main.xml.

2. Logo após fazer o setContentView da activity, devemos utilizar a classe DataBindindUtils para pegar a ligação associada a Activity, definir ao objecto criado em 1 utilizando o método estático setContentView que recebe como parâmetros a Activity e o id do layout. Nota:  Esta classe DataBindingUtils oferece vários métodos estáticos que são utilizados para obter as ligações em diferentes casos.

3. Devemos de seguida criar um objecto da classe Movie que queremos passar na ligação.

4.Fazer a tanto falada ligação ou binding. A classe que é gerada apartir do ficheiro de layout mencionada em 1 vem com setters que permitem passar objectos correspondentes a todas as variáveis criadas dentro da tag data. Para o nosso caso, como só definimos a variável movie, o método setMovie foi automaticamente criado.

Com estes 4 classes ja temos a nossa UI totalmente ligada a lógica da nossa aplicação sem a necessidade de ter de fazer todo o processo de findViewById como mostra o código abaixo:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
        Movie movie = new Movie("Maze Runner","2015-10-2","Path",23456);
        binding.setMovie(movie);
    }
}

Feito isto podemos correr a aplicação e podemos ver que auto magicamente, o text view esta definido com o titulo que definimos no objecto que passamos no momento de fazer a ligação.

Screenshot_20151015-221637

Para o post de hoje e tudo sobre o Data Binding mas como vem no titulo estamos só na 1a parte e vem muito mais coisas que vos vão deixar cada vez mais fascinados com a Biblioteca. Como sempre obrigado por tomarem um pouco do vosso tempo para ler o blog e caso surja alguma questão ou queiram deixar alguma sugestão, basta só fazer um comentário na secção abaixo.

PS: Não deixe este conhecimento todo para ti, seja friendly e compartilhe com os teus amigos que possam estar interessados.

Ate a próxima,

DM

Desenvolvendo para Android – Utilizando Loaders

Standard

Hello para todos os devs, espero que todos estejam muito bem e aprendendo com esta série sobre desenvolvimento para android/ Para quem esteja começando a embarcar no mundo do bonequinho verde sugiro que veja o inicio da serie neste POST

Dando continuidade ao que temos vindo a aprender, hoje irei escrever sobre uma API, que vem disponível por defeito na plataforma mas infelizmente é muito pouco utilizada pelos desenvolvedores.

Falo da Loaders API, que é uma API que oferece mecanismos para efectuar o carregamento de dados de forma assíncrona (Para quem quiser, tem uma boa descrição sobre Main / UI thread e background operations AQUI  ).

Como disse acima, a Loaders API foi inicialmente introduzida no framework na versão do Android 3.0 e posteriormente introduzida na android support library v4 para que pudesse ser utilizada em versões inferiores. Esta API vem com varias vantagens comparadas ao AsyncTask e possui as seguintes características:

  • Disponiveis para todos Fragments e Activities: Diferentemente dos AsyncTasks, os Loaders estão ligados ao ciclo de vida das Activities ou Fragments, o que significa que se a Activity é obstruída por outra e vai para o estado onStop, o loader também será parado.
  • Monitoram a fonte de dados: Os loaders, permitem monitorar a fonte de dados a que estão ligados, permitindo que no momento que este verifica uma mudança nos dados, ele ira automaticamente enviar os novos dados para serem actualizados na UI.
  • Carregam a ultimo Loader criado:  Voltando para o primeiro ponto das características dos loaders, porque estas estão ligadas ao ciclo de vida das Activities ou Fragments, se uma Acitivity mudar de configuracao (Orientação de Portrait  para Landscape), no momento que a Activity for recriada, o loader ira pegar a ultima instância que contem os dados carregados antes da Activity ser destruída e voltar a apresentar a UI.

Ao longo deste post, irei demonstrar como e onde implementei os loaders durante o projecto MyMovieApp do nanodegree do udacity.

Durante o desenvolvimento do MyMovieApp do udacity, deparei-me com dois momentos que precisava de carregar dados e apresentar na UI.

  1. Precisava de carregar os filmes mais populares e mais votados a partir da API do themoviedb.com.
  2. Uma vez marcados como favoritos, os filmes eram guardados na base de dados e ao seleccionar  filmes favoritos, estes tinham de ser carregados e apresentados na UI.

A Loaders API oferece a class Loader que pode ser extendida de acordo com a funcionalidade(tipo de dados que pretendemos carregar) que se quer dar ao loader. Tomando como exemplo os 2 pontos citados acima, durante a implementação no projecto utilizei duas classes classes que estendem a classe  Loader (AsyncTaskLoader e CursorLoader)

Começando pela classe AsyncTaskLoader, a definição é que esta classe é um loader que oferece uma AsyncTask para efectuar operacoes em uma background thread. Como nas AsyncTasks, esta classe requer que o metodo doInBackground() seja implementado pois é dentro deste método que devemos realizar as operações que podem levar muito tempo.(descodificar bitmaps, Carregar informação da cloud etc).

A implementacao deste loader é muito simples e para tal basta seguir os passos abaixo:

  1. Criar uma classe e extender a class AsyncTaskLoader.
  2. Especificar o tipo de dados que o loader deve retornar assim que acabar de fazer os seu trabalho.
  3. implementar a logica para realizar o trabalho dentro do método doinBackground.
  4. Retornar o tipo de dados especificado na definicao do loader dentro do método doInBackground.
//Extender a class AsyncTaskLoader e especificar o tipo de dados a ser retornado
public class MovieAsyncLoader extends AsyncTaskLoader<List<Movie>> {

    private Context mContext;
    
    public MovieAsyncLoader(Context context, String queryType) {
        super(context);
        mContext = context;
    }

    @Override
    public List<Movie> loadInBackground() {

        //Implementar toda logica e retornar uma lista de filmes
        List<Movie> movies = new ArrayList<Movie>();

        return movies;
    }

}

Seguidos os passos, temos um AsyncTaskLoader rapidamente criado e com a logica para carregar os dados implementada dentro do metodo doInBackground como mostra o codigo acima. O código completo utilizado no MyMooviApp Encontra-se AQUI.

Os próximos passos a seguir são: inicializar o loader, deixar que este faca o trabalho, mostrar os dados na UI no fim do trabalho.

Como mencionado acima os loaders so podem existir dentro de uma Activity ou Fragment. Este requesito leva-nos a mais uma classe dentro desta API denominada LoaderManager.

O LoaderManager é uma classe abstracta e esta directamente associada as Activities our Fragments para gerir uma ou mais instâncias de Loaders dentro da aplicação. Uma aspecto a tomar em consideracao e que uma Activity ou Fragment só pode ter um LoaderManager. Esta classe Loader Manager, possui uma interface chamada LoaderCallbacks que como o nome sugeri, define métodos de callback que serão utilizados para  interagir com o Loader.

Os três métodos de callback que tem de ser rescritos são:

  • onCreateLoader –  Este método e invocado assim que e especificado o metodo initLoader para  inicializar um loader. A sua implementação é a criação de um Loader que será retornado ao loader manager para ser inicializado logo de seguida pelo LoaderManager.
  • onLoadFinished: Este método é invocado assim que o  loader acaba de fazer o trabalho e os resultados são enviados de volta para UI thread. Neste método podemos interagir com a UI e passar os dados para um adapter  ou qualquer outro componente.
  • onLoadReset : Este método serve para reiniciar o loader e dar o comando para reiniciar o trabalho. fazer o initLoader tambem tem o mesmo efeito entretanto ao faze-lo dessa forma, o loader manager vai pegar a instância antiga do loader e re-apresentar os dados que ja tinham sido carregados anteriormente.

Com os métodos todos implementados, o ultimo passo para executar o loader, temos de  pegar o loader manager e invocar o método initLoader. Este método recebe  3 parâmetros:

  • Loader ID :Um inteiro que será utilizado pelo loader manager para identificar o loader que este tem de iniciar ou fazer restart.
  • Bundle : Este bundle é utilizado para passar um conjunto de informações para o loader. Por exemplo no  MyMovieApp passei um bundle onde coloquei uma string com com o tipo de query a realizar (Popular, top Rated , Favorite);
  • Callback methods : Como ultimo parâmetro temos de passar uma referencia aos métodos de callback que temos implementados. Como normalmente implementamos na definicao da Activity ou Fragment, podemos simplesmente passar este parametro como this.
getLoaderManager().restartLoader(LOADER_ID, bundle, this)
public class BlogLoadersActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Movie>> {

    private GridView moviesGrid;

    private final int LOADER_ID=1;

    BlogCursorAdapter mMoviesAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blog_loaders);

        moviesGrid = (GridView)findViewById(R.id.movies_grid);

        moviesGrid.setAdapter(mMoviesAdapter);

        Bundle b = new Bundle();
        b.putString("QueryType","favorite");
        //inicializa o loader passando o id, o bundle com informacao
        getSupportLoaderManager().initLoader(LOADER_ID, b,this);
    }

    @Override
    public Loader<List<Movie>> onCreateLoader(int id, Bundle args) {

        //Extraimos os dados do Bundle
        String queryType = args.getString("QueryType");

        //Inicializa e o loader e retorna ao loader Manager
        return new MovieAsyncLoader(this,queryType);
    }

    @Override
    public void onLoadFinished(Loader<List<Movie>> loader, List<Movie> data) {

        //verifica se a lista e recebida e null
        if(data!=null){
            //popular os dados na Aqui
            //definir os dados no adapter etc.
            //mAdapter.swapData(data);

        }
    }

    @Override
    public void onLoaderReset(Loader<List<Movie>> loader) {

    }
}

Com o código acima, consegui resolver o primeiro problema de conexão a API utilizando AsyncTasksLoader. A implementacao ompleta do fragment utilizado no projecto encontra-se AQUI.

Passando para o segundo momento em que precisava de carregar dados através de um Content Provider, após aprender sobre AsyncTaskLoaders poderia implementar essa funcionalidade da mesma forma apenas mudando o tipo de dados para Cursor mas o framework ja dispõe de um Loader que retorna um Cursor denominado CursorLoader.

A utilização do CursorLoader  bastante simples e para poder utilizar é preciso garantir os seguintes requisitos:

  • Ter o Content provider devidamente criado – Para quem nao sabe como o fazer pode ver o post anterior AQUI.
  • Ao implementar os LoaderCallbacks na Activity ou fragment, definir o Cursor como tipo de dados a retornar :
    public class BlogLoadersActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>
  • Implementar os métodos de callbacks explicados mais acima.
  • Ao implementar o metodo onCreateLoader, devemos inicializar e retornar um CursorLoader e por fim executar. PS: Os parâmetros passados ao CursorLoader, são similares aos que são passado ao invocar o método query do contentresolver como vimos no POST Anterior quando falávamos de providers.

    Com a simples implementação do AsyncTaskLoader e  CursorLoader, chegamos ao fim de mais um post e deste modo ja podemos ate agora construir uma app que descarrega dados da cloud, armazena a informação no dispositivo, oferece um mecanismo de leitura de dados independente da forma como estes foram armazenados e mais do que tudo consegue realizar todas estas operações de forma eficiente e seguindo as boas praticas.

Espero que tenham gostado do post e ate a próxima !

Links : Documentação oficial sobre Loaders

DM 🙂

Material Goodies – Navigation View

Standard

Hello Malta, espero que estejam bem e dando os toques extra do material design as vossas apps. No post anterior onde comecei a serie Material Goodies, anunciei que re-escreveria o post onde falava sobre o tinting de drawables e o navigation drawer. O motivo que me leva a escrever este novo post, é que com a introdução da design support library, o post anterior fica mesmo a servir de exemplo para quem queira saber como cada parte do Navigation drawer foi feita  e como fazer o Tint (Mudar a cor durante a execução da app ) de drawables que será útil em outras aplicações. Antes do release do Navigation View, uma das formas mais fáceis de implementar o Navigation drawer era através da biblioteca criada pelo grande desenvolvedor Rudson Lima  que pode ser encontrada AQUI

Neste post, iremos ver sobre como utilizar o Navigation View disponível na desing support library para colocar um navigation drawer na nossa app de forma muito simples.

Para começar vamos criar um projecto normalmente como vimos aqui. Com o projecto criado, a primeira coisa que temos de fazer e ir ao ficheiro build.gradle dentro do modulo “app” e adicionar a dependência da design support library adicionando a seguinte linha a secção dependencies:

compile 'com.android.support:design:22.2.0'

De seguida, vamos abrir o ficheiro activity_main.xml e dentro dele colocar colocar os seguinte código :

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawer_layout"
    android:fitsSystemWindows="true">

    <!-- your content layout -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/container"
            />

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:id="@+id/toolbar"
            android:background="?attr/colorPrimary"
            />
    </FrameLayout>

    <android.support.design.widget.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/navigation_view"
        android:layout_gravity="start"
        app:headerLayout="@layout/drawer_header_layout"
        app:menu="@menu/drawer_menu"
        android:theme="@style/ThemeOverlay.AppCompat.Light"
        />

</android.support.v4.widget.DrawerLayout>

Indo para os detalhes do ficheiro do layout acima,  podemos notar que ele tem como parent o DrawerLayout para que possamos fazer o slide in/ out do navigation drawer. Esta parte não vem como nenhuma novidade e sempre que for preciso utilizar o navigation drawer, o parent do layout principal tem de ser um DrawerLayout.

Dentro  do Drawer layout, temos dois filhos (childrens) onde um representa o que contem o conteudo da nossa activity neste exemplo representado por um FrameLayout que contem uma Toolbar e um outro FrameLayout para que possamos trocar fragments durante o tempo de execução e o outro , o navigation view que representa o nosso navigation drawer.

O Navigation View permite criar um Navigation Drawer sem envolver codigo java, bastando seguir os seguintes passos:

  1. Criar um ficheiro de menu dentro do directório res->menu que ira conter as opções que queremos  ter no Navigation Drawer. drawer_menu.xml
  2. definir que o navigation drawer ira mostrar os items no ficheiro criado no passo anteriror ao adicionar o atributo app:menu ao navigation view.
  3. (Opcional) criar um layout para o header do navigation drawer e definir no navigation drawer pelo atributo app:headerLayout. drawer_header_layout

E prontos. Em 3 passos ficamos com o Navigation drawer todo bonitinho e prontos para passar para a fase seguinte que e dar a vida a cada item clicado e colocar o indicador do navigation drawer no Toolbar.

Para esta ultima parte do tutorial , devemos abrir a class MainActivity e copiar o seguinte método que será invocado assim que a activity e criada no onCreate logo depois do metodo setContentView() :

public void setUpDrawerAndToolbar(){
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    mToolbar.setTitle("Material Goodies");
    mToolbar.setNavigationIcon(R.drawable.ic_action_menu);
    mDrawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            mDrawerLayout.openDrawer(GravityCompat.START);
        }
    });

    mNavigationView = (NavigationView)findViewById(R.id.navigation_view);
    mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(MenuItem menuItem) {

            int id = menuItem.getItemId();
            selecteOption(id);
            return false;
        }
    });
}
public void selecteOption( int id){

    String selectionTitle = "";
    switch (id){
        case R.id.action_first_choice:{
            selectionTitle = "First Choice";
            break;
        }
        case R.id.action_second_choice: {
            selectionTitle = "Second Choice";
            break;
        }
        case R.id.action_third_choice:{
            selectionTitle = "Third Choice";
            break;
        }
    }

    Toast.makeText(this,"Item clicado: "+selectionTitle,Toast.LENGTH_SHORT).show();
}

O primeiro método setUpDrawerAndToolbar é bastante simples e o que ele faz é:

  • pegar o Toolbar pelo Id definido no activity_main.xml
  • definir o titulo
  • Definir o navigation icon passando um drawable que neste caso e o icon do indicador do navigation drawer. Nota: Em casos  em que nossa activity tem um parent podemos definir de igual forma o icon da setinha que indica que podemos voltar para nossa parent Activity.
  • Pegar o DrawerLayout pelo id.
  • Definir um listener que ficara a espera de eventos de click no indicador para mostrar o navigation drawer.
  • Pegar o NavigationView pelo Id.
  • Definir um listener que fica a espera de eventos de clique em cada item do Menu representado pelo MenuItem. Para poder saber que item foi seleccionado, devemos pegar o id do menuItem seleccionado e depois comparar com os ids que nos definimos no xml como mostra a seguinte linha
    int id = menuItem.getItemId();

e o método selectOption que recebe um id e faz o switch case para encontrar o item clicado.

Deste modo, chegamos ao fim de mais um post do Material Goodies e de forma muito rápida e simples foi implementado o Navigation Drawer utilizando o Navigation View.

Obrigado e ate a próxima Malta 🙂

MG – Navigation View(Código)

Material Goodies – Utilizando o CoordinatorLayout com o Toolbar

Standard

Hello Devs,  sejam todos muito bem vindos a mais um post sobre desenvolvimento para android. Com este post, vamos começar uma serie chamada Material Goodies, em que iremos implementar aqueles pequenos detalhes do material design que tornam apps incríveis e que os utilizadores não terão outra opção senao amar:).

Durante o IO15, foi anunciada e lançada a android design support library com o intuito de facilitar a vida dos desenvolvedores ao oferecer formas simples, rápidas e práticas de implementar o material design para que sejam compatíveis as versões anteriores ao Lolipop.  Esta biblioteca veio cheia de novidades como o NavigationView  que é uma forma fácil de implementar o NavigationDrawer( Usaremos no próximo post para actualizar o post anterior sobre a support library), a SnackBar, FAB(Floating Action Button), CoordinatorLayout o componente que utilizaremos neste post dentre outros.

Começaremos o material utilizando o CoordinatorLayout, pois foi um dos primeiros componentes da design support library que utilizei no momento em que ia implementar o Details screen da segunda fase da app do nanodegree do android e o resultado foi  o seguinte:

Screenshot_20150715-203246

Screenshot_20150715-203231

Entrando ja para os detalhes de implementação irei começar por falar sobre o CoordinatorLayout. O CoordinatorLayout  é um componente introduzido no android design support library e é um layout que permite maior controle dos eventos de cliques e animações na UI isto significa que podemos controlar como cada componente na UI ira se comportar ao receber um evento de click ou scroll em relação a todos os outros componentes  na UI. Este componente pode ser usado de diferentes formas fazendo com que o limite esteja apenas na cabeça do desenvolvedor.

O primeiro passo antes de passar para o código e garantir que temos as dependências bem especificadas no nosso build.gradle do module app como vem abaixo:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'com.android.support:design:22.2.0'
    compile 'com.squareup.picasso:picasso:2.5.2'
    compile 'com.android.support:cardview-v7:22.2.0'
    compile 'com.android.support:recyclerview-v7:22.2.0'
}

Para atingir o objectivo super cool das imagens acima, neste post iremos utilizar o CoordinatorLayout e a AppBar que era antigamente conhecida como ActionBar e agora é composta pela statusBar, Toolbar, Tab/Search Bar e Um Flexible Space como mostra a imagem abaixo tirada das especificações no GUIDE criado pela google(Aconselho que se de uma vista de olhos).

O link acima e as imagens não são referentes a um guide do AppBar mas sim a scrolling techniques  que explica como cada parte do AppBar se deve comportar ao fazer o scroll de forma a oferecer uma boa experiência de utilizador. Volto a aconselhar que entrem para o link e vejam os vídeos associados a cada possível técnica :).

Dado isto, o primeiro passo para criar este efeito incrivel é definir o layout como mostra abaixo:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/details_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:padding="8dp"/>

    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:id="@+id/collapsingToolbar"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:contentScrim="?attr/colorPrimary">

            <include layout="@layout/layout_movie_details_header" android:id="@+id/headerLayout"
                app:layout_scrollFlags="scroll"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_height="?attr/actionBarSize"
                android:layout_width="match_parent"
                app:layout_collapseParallaxMultiplier="0.7"
                app:layout_collapseMode="pin"/>
        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>


</android.support.design.widget.CoordinatorLayout>

Entrando em detalhes para o layout nesta imagem, encontramos o CoordinatorLayout como o layout mais alto da hierarquia(parent) e dentro dele temos os seguintes componentes que são de extrema importância:

  • AppBarLayout: AppBarLayout e o layout onde encontramos o toolbar e todos outros componentes bem explicados acima e referenciados ao guide de scrolling techniques. Dentro do AppBar, encontramos um CollapsingToolbarLayout que é responsável por ajudar a criar transições e animações fixes dos componentes que colocamos dentro delas quando a activity recebem eventos de scroll.

Ao CollapsingToolbarLayout e todos os componentes que queremos que se movam ao receber um evento de scroll, devemos definir o atributo layout_scrollFlags  que ira especificar como determinada view ira se comportar. As flags existentes são:

scroll: flag definida a todas as views que queremos que movam-se ao mover ao fazer scroll do conteúdo da activity.

enterAlways: flag que garante que ao fazer o scroll para baixo, a view voltara a ficar visível.

enterAlwaysCollapsed: flag que indica que a view sempre ira ser apresentada com o valor definida pelo atributo minHeight que representa a mínima altura que esta pode ter.

exitUntilCollapsed: flag que indica que ao fazer scroll do conteúdo no sentido que a view ira desaparecer, ela diminua o tamanho ate ao mínimo predefinido.

Seguindo o que esta acima, no ficheiro acima definimos as scroll flags para o CollapsibleToolbarLayout e o layout que representa o header(A imagem e o rating das figuras acima). Nota: E possivel escrever o layout do header directamente no ficheiro activity_main.xml mas por questões de performance especificamente falando da hierarquia profunda que isto causaria(possivel de ver no Hierarchy viewer) achei melhor fazer o include. A view do toolbar é a única que aparece sem o atributo do scrollFlag e por essa razão é o ultimo componente a ser colocado dentro desta hierarquia. Não temos o scroll no Toolbar porque não queremos que ele se mova e que esteja presente quando o header desaparecer no scroll. Para garantir que a toolbar fica sempre la, devemos definir o atributo layout_collapseMode=”pin”.

  • Content Container: Esta view, representada  no nosso exemplo pelo fragment, é que vai conter o conteudo que mandatoriamente deve estar dentro de um Layout com a possibilidade de fazer scroll como o RecyclerView e o NestedScrollView. Infelizmente utilizar o ListView ou GridView nao funciona para este caso e não é motivo de preocupação pois estes dois layouts podem ser implementados utilizando o RecyclerView e definindo o LayoutManager apropriado :). A este layout que ira receber o conteúdo, temos de sempre colocar a linha :
    app:layout_behavior="@string/appbar_scrolling_view_behavior"

    para informar que e desta view que estamos a espera e receber eventos de scroll.

chega a ser muito estranho ter de dizer que o essencial do post acaba aqui mas sim e exactamente isso para atingir o efeito das imagens acima não precisamos de mais código e nem mais configurações…SIMPLESMENTE SIMPLES :).

Para terminar o post e ter um exemplo funcional so nos restam duas coisas:

  1. Criar um fragment com um RecyclerView dentro a representar o conteúdo que um dia poderemos ter. gists : fragment_layout_dummy.xmlDummyFragmentlayout_dummy_card.xml
  2. Na classe da activity, preencher os dados do header(fotos e texto) e por fim colocar dinamicamente o fragment criado no FrameLayout da activity reservado ao conteúdo. Gist: MainActivity.java

Desta forma chegamos mesmo ao fim do primeiro post sobre Material Goodies e ficarei feliz em receber de voces requests de posts sobre alguma coisa incrivel do material design etc.

Espero por vocês no próximo post 🙂

Link : Código Completo

DM