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!

Criando um Navigation Drawer customizado no Android

Padrão

Olá pessoal! Voltando a postar aqui no blog, hoje vamos ver como criar um Navigation Drawer customizado no Android. Navigation Drawer, pra quem não sabe, é aquele menuzinho deslizante no Android que vem do lado esquerdo do app, sinalizado pelos três traços horizontais ao lado do ícone do aplicativo na Action Bar.

Captura de tela 2014-06-24 20.06.17

Nas últimas versões do Android Studio, nós já temos um modelo de Activity que cria um Navigation Drawer pra gente, um modelo simplezinho com uma lista de opções. Nesse post, vamos ver como criar um Navigation Drawer com uma View customizada e fazer aquele visual bacana pro seu app.

Pra início de conversa, crie um projeto no seu Android Studio (estou utilizando para esse post a versão 0.6.1) e utilize o modelo de Activity com o Navigation Drawer.

Captura de tela 2014-06-24 20.12.13

Ao criar uma Activity com esse modelo, uma porção de código é gerada (classes e XMLs de layout). Vamos analisar alguns pontos principais.

  • Activity: Simplificadamente, possui como layout um DrawerLayout (para o efeito de deslize do Navigation Drawer), tendo como filhos o Fragment do Navigation Drawer e um FrameLayout onde o conteúdo das telas será colocado. Já no código, o ponto mais importante é o método onNavigationDrawerItemSelected(), método de callback chamado toda vez que um elemento do drawer é selecionado.
  • NavigationDrawerFragment: a classe que representa o nosso drawer. No wizard é gerada uma tonelada de código, mas podemos destacar o método onCreateView() no qual criamos o layout do drawer e o método selectItem(), responsável por notificar o que foi selecionado nele.

Nesse código gerado, temos um fragment chamado PlaceholderFragment, utilizado apenas para criar o efeito de mudança nas opções. Além disso, o layout do drawer é simplesmente uma lista semi-transparente.

Captura de tela 2014-06-24 21.12.45

Para este exemplo, então, vamos criar um Navigation Drawer mais interessante, com um cabeçalho para um resumo de um perfil de usuário (fictício), vamos deixar essas três opções de navegação que já estão ali (ou mesmo podemos adicionar mais algumas) e um rodapé para créditos de desenvolvimento. Além disso, vamos fazer com que o Navigation Drawer mantenha destacada a página de navegação atual do usuário.

Vamos lá. O primeiro passo é modificarmos o arquivo de layout do Navigation Drawer, no caso, o arquivo fragment_navigation_drawer.xml. Adicionei um RelativeLayout ao redor da lista e a configurei para ficar entre o TextView de topo (perfil do usuário) e rodapé (créditos). Ficou assim:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context="net.rafaeltoledo.hellospringapi.NavigationDrawerFragment">

    <TextView
        android:id="@+id/headerView"
        style="?android:attr/textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"        
        android:drawableLeft="@drawable/photo"
        android:gravity="center_vertical"
        android:padding="25dp"
        android:text="Rafael Toledo" />

    <TextView
        android:id="@+id/footerView"
        style="?android:attr/textAppearanceMedium"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"        
        android:gravity="center"
        android:padding="20dp"
        android:text="Desenvolvido por Rafael Toledo"
        android:textStyle="bold" />

    <ListView
        android:id="@+id/navigationItems"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/footerView"
        android:layout_below="@+id/headerView"
        android:background="#cccc"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp" />

</RelativeLayout>

O próximo passo é modificarmos a forma como o NavigationDrawerFragment lida com seu layout (já que anteriormente ele o tratava apenas como uma lista). Vamos fazer uma leve modificação no método onCreateView() para que ele busque a lista no layout. Fica assim:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(
            R.layout.fragment_navigation_drawer, container, false);

    mDrawerListView = (ListView) view.findViewById(R.id.navigationItems);
    mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    });
    mDrawerListView.setAdapter(new ArrayAdapter<String>(
            getActionBar().getThemedContext(),
            android.R.layout.simple_list_item_activated_1,
            android.R.id.text1,
            new String[]{
                    getString(R.string.title_section1),
                    getString(R.string.title_section2),
                    getString(R.string.title_section3),
            }));
    mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
    return view;
}

Veja que não modificamos muita coisa. Só fizemos alguns ajustes devido ao fato de que o elemento raiz do layout do drawer não é mais a lista e sim o RelativeLayout. Dessa forma, precisamos fazer o lookout nele pra buscar a lista. Se executarmos o app nesse ponto, teremos algo assim:

Captura de tela 2014-06-24 21.30.59

Percebam que o conteúdo está um pouco “espremido” no Navigation Drawer devido a sua largura. Segundo as boas práticas de design do Android, um Navigation Drawer pode ter entre 240dp 320dp. Vamos alterar a sua largura no arquivo values/dimens.xml para 300dp, alterando o valor de navigation_drawer_width.

Captura de tela 2014-06-24 21.35.23

Já ficou mais interessante, né? O negócio agora é fazermos as opções serem, de fato, selecionadas quando forem clicadas, ficando fácil pro usuário saber em qual tela ele está. Pra isso, basta fazermos uma pequena modificação no método onCreateView() do NavigationDrawerFragment, alterando o layout do adapter de simple_list_item_1 para simple_list_item_activated_1. Simples não?

Captura de tela 2014-06-24 22.06.59

E é isso minha gente! Ah, é bom ficar atento que esse último passo não funciona no Android 2.3! Se bem que, hoje, pra um app novo, já não compensa (na minha opinião) suportar o Gingerbread… Bola pra frente devs!

Até a próxima!

Voltei!

Padrão

Depois de algumas semanas sem postas, estou de volta! Passei por algumas semanas meio atribuladas, principalmente devido a mudança parcial no trabalho, onde comecei a me dedicar meio período a minha empresa. Mas essa semana os posts devem voltar com tudo! 😀

E pra compensar a ausência, mudei a cara do blog. Um visual mais limpo, tentando priorizar mais o conteúdo e facilitar a leitura. Aceito sugestões aqui nos comentários.

Até breve!

Tutorial Android #14 – Ajustando o Layout para Paisagem (II)

Padrão

Olá pessoal! No tutorial de hoje vamos voltar a um tema já tratado superficialmente alguns posts atrás: a adaptação do layout da aplicação Lista de Restaurantes também para o modo paisagem. Não sei se vocês chegaram a testar o exemplo do tutorial 10, mas naquela situação tínhamos um pequeno problema: ao rotacionar a tela para modo paisagem / retrato, perdíamos o conteúdo do formulário, caso alguma coisa tivesse sido digitada.

No tutorial de hoje vamos fazer a rotação de uma maneira mais organizada, resolvendo também este problema. Todos prontos? 🙂

Continue lendo!