Layouts e Resources – Tutorial Android 4

Padrão

Olá pessoal! No último post criamos a nossa primeira Activity, e entendemos um pouco sobre Intents! No post de hoje vamos entender um pouco mais sobre resources no Android e criar um layout para a nossa Activity. Preparados?

Para o tutorial de hoje, vamos trabalhar majoritariamente com a pasta res, que fica localizada no caminho app/src/main/res. É nela que ficam todos os recursos do seu projeto, que não são necessariamente código Java. O projeto, no momento, deve ter algumas pastas, como drawable (vazia), mipmap (em algumas versões com sufixos estranhos) e values.

Um projeto Android padrão costuma ter várias outras, que entraremos em mais detalhes a medida que forem necessárias. Essas pastas separam os tipos de recursos que temos em nosso projeto. Por exemplo, drawable, conterá qualquer tipo de coisa desenhável, desde formas, a imagens e seletores. A mipmap contém somente o ícone do aplicativo. A pasta values contém valores em gerais, desde inteiros, até strings, cores, dimensões, estilos e etc.

Se você abrir cada uma das pastas (mipmap-hdpimipmap-mdpimipmap-xhdpi, etc), você verá que temos o ícone do aplicativo em diferentes tamanhos. Isso ocorre porque os dispositivos Android possuem diversas configurações. Configuração diz respeito a tudo no dispositivo, desde idioma, até o que chamamos de densidade da tela. Uma maior densidade, quer dizer que aquele dispositivo tem mais pixels em uma menor área física. Exemplo: um dispositivo que tem uma tela de 4,7″ e uma resolução de 480×800 pixels tem uma densidade mais baixa (hdpi) do que um device que tem a mesma tela de 4,7″, porém uma resolução de 1080×1920 (xxhdpi). Exatamente por suportar uma diversidade grande de tamanhos de tela, é que no Android não trabalhamos com pixels, e sim com density pixels. Assim, um botão tende a ter sempre “mais ou menos o mesmo tamanho”, independente da resolução da tela. Para aprender mais sobre isso, dê uma olhada na documentação do Android (que agora também possui versão em português!).

E como essa mágica é feita? Não é mágica. Ao colocar um qualificador na pasta, o Android, na hora que estiver executando, conhece a configuração do dispositivo e usa o recurso específico para a sua configuração. Assim, não precisamos fazer várias condições em nosso código. É importante lembrar que podemos ter vários tipos de qualificadores ou mesmo adicionar pastas com mais de um qualificador. Veremos alguns casos em outros posts 😉

Assim, resumindo, Supondo que estou em dispositivo xhdpi e preciso do resource mipmap/ic_launcher, o Android automaticamente buscará em mipmap-xhdpi/ic_launcher. Caso não exista uma versão específica para a configuração, ele busca a opção mais genérica, sem nenhum sufixo.

Bom, agora que já estamos mais familiarizados com o mecanismo de resources e pastas no Android, vamos criar uma nova pasta dentro de res, chamada layout. Essa pasta, como o próprio nome sugere, guardará arquivos que representam os layouts das nossas telas. Vamos criar um arquivo chamado activity_main.xml. A grande maioria dos resources no Android utilizam a notação de XML.

Neste arquivo, primeiramente colocaremos um container, o LinearLayout. Os containers servem para organizar os elementos na tela. No caso do LinearLayout, ele organiza os elementos alinhados, um após o outro, utilizando a orientação vertical ou horizontal. Adicionando o LinearLayout, o arquivo activity_main.xml fica assim:

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

</LinearLayout>

Você pode perceber que, além da orientação, também configuramos a largura (width) e a altura (height) do elemento. Para essas dimensões, podemos utilizar um valor ou as constantes match_parentwrap_contentmatch_parent indica que o elemento adotará para essa dimensão, a mesma dimensão que seu elemento pai. No caso, como o LinearLayout é o primeiro elemento da nossa hierarquia de views, ele adotará o tamanho de seu pai (algum container do Android que representa a tela). Assim, temos um LinearLayout do tamanho da tela 🙂

Vamos agora adicionar dois campos de texto e um botão ao nosso layout.

Os campos de entrada de texto no Android são conhecidos como EditText. Além da altura e da largura, também especificamos o id e a propriedade hint. Ids no Android são identificadores, o que nos permitirá referenciar este elemento em outros lugares (seja no XML, seja nas nossas classes Java). A notação @+id indica que estamos criando este recurso caso ele não exista. Sempre que você ver a notação @ em um XML, significa que estamos referenciando um recurso. O + indica a criação e id indica que é um resource do tipo id. Já o hint indica um texto de sugestão que será exibido quando o campo estiver vazio, indicando ao usuário o que deve ser inserido. Também estamos utilizando a notação de recurso, dessa vez referenciando um recurso do tipo string (que terá um valor correspondente dentro de res/values/strings.xml):

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

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/name" />

    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/address" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/save" />

</LinearLayout>
<!-- Arquivo strings.xml -->
<resources>
    <string name="app_name">Tutorial</string>
    <string name="save">Save</string>
    <string name="address">Address</string>
    <string name="name">Name</string>
</resources>

No caso do Button, temos atributos semelhantes, com a diferença de que utilizamos a propriedade text em vez de hint. Com a propriedade text, teremos um texto no botão.

Com o arquivo de layout criado, vamos voltar a nossa classe MainActivity e definir este arquivo de layout, vinculando-o a Activity. Para isso, vamos utilizar ainda o método onCreate() que é chamado sempre que a Activity é criada. Nele, vincularemos o nosso layout XML a Activity. Fazemos isso chamando o método setContentView(). Este método pode receber um objeto do tipo View ou uma referência a um resource de layout. Note o uso da classe R. Esta classe é gerada automaticamente a cada build do projeto, e é uma referência a todos os resources que temos no projeto, para uso no nosso código Java. Vamos também, remover os Logs que adicionamos anteriormente. O método fica assim:

public class MainActivity extends AppCompatActivity {

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

Com esse XML, caso execute o aplicativo, nossa tela fica com o seguinte layout:

Para uma experiência mais agradável, poderíamos centralizar os elementos verticalmente. Para isso, podemos adicionar a propriedade gravity com o valor center_vertical. Essa propriedade indica como os filhos do container vão se comportar, conforme forem adicionados. Assim, temos a tela final:

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

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/name" />

    <EditText
        android:id="@+id/address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/address" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/save" />

</LinearLayout>

E é isso pessoal! No próximo post veremos o mecanismo de binding de views e callbacks no Android! Caso deseje baixar o projeto que desenvolvemos até agora, você pode encontrar no Github.

Até o próximo post!

Entendendo a Activities e Intents – Tutorial Android 3

Padrão

Olá pessoal! Dando prosseguimento a nossa série de tutoriais, hoje vamos criar a nossa primeira Activity, entender o que ela é e como funciona, além de uma noção geral sobre Intents! Vamos lá?

No último post criamos um projeto no famoso estilo Hello World. Para este, vamos criar um projeto que será evoluído ao longo de toda a série. Para isso, crie um novo projeto seguindo o tutorial anterior, com a diferença de que, em vez de selecionar Empty Activity, selecione a opção Add No Activity. Assim, podemos controlar melhor o que é criado e entender o que está acontecendo.

Algumas configurações iniciais serão realizadas e um modelo básico de projeto será criado. A princípio não se preocupe, vamos focar nos pontos um a um ao longo desta série. Assim que o projeto é criado, você verá à esquerda da IDE, na árvore do projeto, as opções appGradle Scripts. Isso ocorre porque estamos utilizando a visão Android, que agrupa muitas coisas e mostra os arquivos de uma forma mais significativa para Android. Ok, um pouco de mágica e esses “açúcares” são bons, mas no início, quando estamos entendendo o que de fato está acontecendo, é melhor ver as coisas na sua forma mais crua, para que a mágica deixe de ser mágica. Sempre que tiver algo “mágico”, vamos trabalhar pra entender como a mágica é feita. Quebra um pouco o encanto, mas te possibilita de fazer suas próprias mágicas depois 😉

Vamos então mudar a visão de Android para Project, que exibirá a estrutura real de pastas do projeto.

Vamos navegar então até a pasta java, no caminho app/src/main/java. O caminho para a pasta tem este formato devido a estrutura de um projeto Gradle. O Gradle uma ferramenta bastante popular que nos ajuda a gerenciar todo o ciclo de build do projeto, incluindo suas dependências e a orquestração das ferramentas de compilação e tooling em geral. Em um momento oportuno analisaremos o Gradle mais a fundo. Nesta pasta você pode ver, por enquanto, apenas a declaração do pacote do aplicativo. Vamos criar então, a nossa primeira Activity. Clique com o botão direito sobre o pacote (no meu caso, net.rafaeltoledo.tutorial) e selecionar a opção New -> Java Class e nomear a classe a ser criada de MainActivity.

Activities no Android são a representação de uma tela. É através dela que conteúdo das telas é exibido e as interações de interface são executadas. Dentre os principais componentes do Android, é o que corresponde a UI dos aplicativos.

Para que esta classe se torne uma Activity, ela deve estender da classe Activity do Android. Porém, para o nosso caso, vamos fazer a nossa classe estender AppCompatActivity. Por quê? A AppCompatActivity é uma versão da Activity que garante um comportamento uniforme a partir da versão 7 (2.1) do Android. Essa classe faz parte das bibliotecas de suporte, que conheceremos e entenderemos mais a fundo em um post futuro.

Para que a nossa Activity esteja disponível no launcher do dispositivo, na forma de um ícone na lista de aplicativos, precisamos configurar o nosso Android Manifest. O Android Manifest é um arquivo que corresponde a uma espécie de índice do nosso aplicativo. Nele é que serão descritas, dentre outras informações, quais Activities nosso aplicativo possui e quais permissões ele necessita para ser executado.

No caso, como não selecionamos a criação automática da Activity quando criamos o projeto, temos apenas a descrição básica do nosso aplicativo no arquivo AndroidManifest.xml, localizado na pasta app/src/main (podem haver pequenas diferenças, dependendo da versão do Android Studio que você estiver usando):

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.rafaeltoledo.tutorial">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

    </application>

</manifest>

Basicamente, temos aqui duas informações importantes:

  • A declaração do nó principal manifest com o atributo package. Esse atributo é importante, pois ele indica em qual pacote Java está a classe R. Esta classe, gerada automaticamente a cada compilação, é um índice com todos os recursos do nosso aplicativo que não são código Java. Veremos recursos muito em breve.
  • A declaração do nó application, que configura e descreve o nosso aplicativo. Dentre as informações principais, temos aqui o ícone, o label (que será o nome do aplicativo no launcher do sistema) e o tema.

Para adicionarmos a nossa Activity, vamos adicionar um novo nó dentro do nó application, dessa forma:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.rafaeltoledo.tutorial">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Neste nó, estamos adicionando uma Activity com o nome .MainActivity. Este caminho pode ser absoluto (net.rafaeltoledo.tutorial.MainActivity) ou, como é o caso, relativo ao package que temos na raiz do manifesto. Perceba que temos também uma configuração dentro do nó activity, chamada de intent-filter.

O Android possui um mecanismo de troca de mensagens conhecido como Intents. Uma intent indica a intenção de se fazer alguma coisa, seja enviar uma mensagem para um componente, seja iniciar um componente. Essa intent pode conter diversas informações, como o destinatário, uma ação (action), uma categoria (category) e outras várias informações extras.

Lembra que quase todo aplicação que a gente escreve em qualquer linguagem sempre começa com um método main()? A ideia é parecida aqui: quando o ícone do aplicativo no launcher é tocado, é disparada uma Intent para a Activity configurada, com a categoria android.intent.category.LAUNCHER e a ação android.intent.action.MAIN, indicando que o aplicativo deve foi iniciado a partir do launcher do sistema. Existem, inclusive, aplicativos que possuem mais de um ícone na lista de apps do launcher.

Para vermos isso acontecendo na prática, vamos ver esse Intent chegando para a nossa Activity. Para isso, vamos sobrescrever o método onCreate() da nossa Activity MainActivity. Como estendemos de AppCompatActivity, herdamos vários métodos que nos permitem fazer diversas coisas no Android. O método onCreate() é um dos callbacks do ciclo de vida de uma Activity. Como você pode perceber, não chamamos o construtor da Activity diretamente, quem faz esse gerenciamento é o próprio Android. Então, somos apenas notificados quando cada coisa acontece. Você pode ver o ciclo de vida da Activity completo aqui, mas não se preocupe muito com isso por enquanto. Cada um desses estados vai fazer sentido mais adiante 🙂

No método onCreate(), vamos então pegar as informações que vieram da Intent que dispararam a nossa Activity. Para isso, vamos escrever no log do Android (conhecido como LogCat) algumas informações da Intent, como a ação, a categoria e o componente que foi lançado. O código fica assim:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        Log.d("MA::Action", intent.getAction());
        for (String category : intent.getCategories()) {
            Log.d("MA::Category", category);
        }
        Log.d("MA::Component", intent.getComponent().getClassName());
    }
}

Repare que, para exibir informação no Logcat, em vez do padrão System.out do Java, utilizamos a classe Log do Android. Com essa classe, temos melhores mecanismos para controlar o nível do log (error, warning, debug, etc) e podemos adicionar uma tag (primeiro parâmetro) para que fique mais fácil de encontrarmos o que queremos 🙂

Agora, se você executar o aplicativo e clicar na aba Android Monitor (na parte inferior do Android Studio), poderá conferir o log do seu aplicativo, exibindo as informações da Intent:

Bom, por hoje é só isso pessoal! No próximo post vamos entender melhor os resources do Android e criar nosso primeiro layout para a MainActivity!

Ah, sim, o projeto completo desenvolvido até aqui pode ser encontrado no Github! 🙂

Tutorial Android #9 – Ciclo de Vida da Activity

Padrão

Olá novamente! No último tutorial vimos como criar uma barra de progresso para carregar uma pseudo-atividade em nosso aplicativo. No post de hoje, vamos trabalhar um pouco com o ciclo de uma Activity. Vamos tornar a nossa enrolação tarefa um pouco mais longa, e fazer com que ela seja pausada ao alternar para outra aplicação e retomá-la ao voltar. Este padrão, pausando processamento quando a aplicação não está em primeiro plano, é bastante útil em diversas outras situações.

O primeiro passo é deixar a nossa tarefa longa o suficiente para que a nossa ideia de aplicativo funcione. Altere a nossa tarefaLonga, aumentando a quantidade de loopings:

Continue lendo!