MDC-101 Android: noções básicas dos componentes do Material Design (MDC) (Java)

logo_components_color_2x_web_96dp.png

Os componentes do Material Design (MDC, na sigla em inglês) ajudam os desenvolvedores a implementar o Material Design. Criados por uma equipe de engenheiros e designers de UX do Google, os MDC oferecem dezenas de componentes de interface bonitos e funcionais disponíveis para Android, iOS, Web e Flutter.

material.io/develop

O que são o Material Design e os componentes dele para Android?

O Material Design é um sistema para criar produtos digitais bonitos e arrojados. Ao combinar estilo, branding, interação e movimento em um conjunto consistente de princípios e componentes, as equipes de produto podem atingir o maior potencial de design.

Para aplicativos Android, os Componentes do Material Design para Android (MDC Android) unem design e engenharia com uma biblioteca de componentes para criar consistência em todo o aplicativo. Conforme o sistema do Material Design evolui, esses componentes são atualizados para garantir uma implementação consistente com perfeição de pixels e o respeito aos padrões de desenvolvimento de front-end do Google. Os MDC também estão disponíveis para Web, iOS e Flutter.

Neste codelab, você vai criar uma página de login usando vários componentes do MDC Android.

O que você vai criar

Este é o primeiro de quatro codelabs que vão ajudar você a criar o app Shrine, um app Android de e-commerce que vende roupas e artigos domésticos. Você vai aprender a personalizar componentes para refletir qualquer marca ou estilo usando o MDC-Android.

Neste codelab, você criará uma página de login para o Shrine que contém:

  • Dois campos de texto: um para inserir um nome de usuário e outro para uma senha.
  • Dois botões, um para "Cancelar" e outro para "Próxima"
  • O nome do app (Shrine).
  • A imagem do logotipo do Shrine.

Componentes do MDC Android neste codelab

  • Campo de texto
  • Botão

O que é necessário

  • Conhecimento básico de desenvolvimento para Android.
  • Android Studio (faça o download aqui, caso ainda não tenha feito)
  • Um dispositivo ou emulador Android (disponível no Android Studio)
  • O exemplo de código (confira a próxima etapa)

Como você classificaria seu nível de experiência na criação de apps Android?

Iniciante Intermediário Proficiente

Iniciar o Android Studio

Ao abrir o Android Studio, você verá uma janela com o título "Welcome to Android Studio". No entanto, se você estiver iniciando essa ferramenta pela primeira vez, siga as etapas do assistente de configuração do Android Studio com os valores padrão. O sistema pode demorar vários minutos para fazer o download e instalar os arquivos necessários, então você pode deixar o programa em execução em segundo plano enquanto acompanha a próxima seção.

Faça o download do app inicial do codelab

Fazer o download do app inicial

O app inicial está localizado no diretório material-components-android-codelabs-101-starter/java.

... ou clone-o do GitHub

Para clonar este codelab do GitHub, execute estes comandos:

git clone https://github.com/material-components/material-components-android-codelabs
cd material-components-android-codelabs/
git checkout 101-starter

Carregar o código inicial no Android Studio

  1. Quando o assistente de configuração for concluído e a janela Welcome to Android Studio for exibida, clique em Open an existing Android Studio project. Acesse o diretório em que você instalou o exemplo de código e selecione java -> shrine (ou pesquise shrine no computador) para abrir o projeto Shrine.
  2. Aguarde um pouco enquanto o Android Studio cria e sincroniza o projeto, conforme mostrado nos indicadores de atividade na parte inferior da janela.
  3. Como o SDK do Android ou as ferramentas de compilação não estão presentes, o Android Studio poderá encontrar alguns erros de compilação. Veja um exemplo a seguir. Siga as instruções no Android Studio para instalar/atualizar essas ferramentas e sincronizar o projeto.

Adicionar dependências do projeto

O projeto precisa de uma dependência na biblioteca de suporte do MDC Android. Provavelmente, o exemplo de código transferido por download já tem essa dependência listada, mas é recomendável seguir as etapas abaixo para garantir.

  1. Navegue até o arquivo build.gradle do módulo app e confira se o bloco dependencies inclui uma dependência do MDC Android:
api 'com.google.android.material:material:1.1.0-alpha06'
  1. (Opcional) Se necessário, edite o arquivo build.gradle para adicionar as seguintes dependências e sincronizar o projeto.
dependencies {
    api 'com.google.android.material:material:1.1.0-alpha06'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'com.android.volley:volley:1.1.1'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:core:1.1.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test:runner:1.2.0-alpha05'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha05'
}

Executar o app inicial

  1. Confira se a configuração do build à esquerda do botão Executar / Reproduzir é app.
  2. Pressione o botão verde Run / Play para criar e executar o app.
  3. Na janela Select Deployment Target, se você já tiver um dispositivo Android listado entre os dispositivos disponíveis, avance para a etapa 8. Caso contrário, clique em Create New Virtual Device.
  4. Na tela Select Hardware, selecione um smartphone, como o Pixel 2, e clique em Next.
  5. Na tela System Image, selecione uma versão recente do Android, de preferência o nível da API mais alto. Se ele não estiver instalado, clique no link Download exibido e faça o download.
  6. Clique em Next.
  7. Na tela Android Virtual Device (AVD), deixe as configurações como estão e clique em Finish.
  8. Selecione um dispositivo Android na caixa de diálogo de destino de implantação.
  9. Clique em OK.
  10. O Android Studio cria o app, faz a implantação e o abre automaticamente no dispositivo de destino.

Pronto. O código inicial da página de login do Shrine será executado no emulador. Você vai ver o nome "Shrine" e o logotipo do app logo abaixo.

Vamos analisar o código. Fornecemos um framework de navegação Fragment simples no nosso exemplo de código para mostrar fragmentos e navegar entre eles.

Abra MainActivity.java no diretório shrine -> app -> src -> main -> java -> com.google.codelabs.mdc.java.shrine. Ele conterá o código a seguir:

MainActivity.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;

public class MainActivity extends AppCompatActivity implements NavigationHost {

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

       if (savedInstanceState == null) {
           getSupportFragmentManager()
                   .beginTransaction()
                   .add(R.id.container, new LoginFragment())
                   .commit();
       }
   }

   /**
    * Navigate to the given fragment.
    *
    * @param fragment       Fragment to navigate to.
    * @param addToBackstack Whether or not the current fragment should be added to the backstack.
    */
   @Override
   public void navigateTo(Fragment fragment, boolean addToBackstack) {
       FragmentTransaction transaction =
               getSupportFragmentManager()
                       .beginTransaction()
                       .replace(R.id.container, fragment);

       if (addToBackstack) {
           transaction.addToBackStack(null);
       }

       transaction.commit();
   }
}

Essa atividade mostra o arquivo de layout R.layout.shr_main_activity, definido em shr_main_activity.xml.

É possível ver que, em onCreate(),, MainActivity.java inicia uma transação Fragment para mostrar o LoginFragment. LoginFragment. É isso que vamos modificar neste codelab. A atividade também implementa um método navigateTo(Fragment), definido em NavigationHost, que permite que qualquer fragmento navegue para um fragmento diferente.

Command + Clique (ou Control + Clique) shr_main_activity no arquivo de atividade para abrir o arquivo de layout ou navegue até o arquivo de layout em app -> res -> layout -> shr_main_activity.xml.

shr_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity" />

Aqui, vemos um <FrameLayout> simples que funciona como um contêiner para todos os fragmentos que a atividade mostra. Vamos abrir o LoginFragment.java.

LoginFragment.java

package com.google.codelabs.mdc.java.shrine;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

/**
* Fragment representing the login screen for Shrine.
*/
public class LoginFragment extends Fragment {

   @Override
   public View onCreateView(
           @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       // Inflate the layout for this fragment
       View view = inflater.inflate(R.layout.shr_login_fragment, container, false);

       // Snippet from "Navigate to the next Fragment" section goes here.

       return view;
   }

   // "isPasswordValid" from "Navigate to the next Fragment" section method goes here
}

LoginFragment aumenta o arquivo de layout shr_login_fragment e o mostra em onCreateView(). Vamos analisar o arquivo de layout shr_login_fragment.xml para ver como é a página de login.

shr_login_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/loginPageBackgroundColor"
   tools:context=".LoginFragment">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:clipChildren="false"
       android:clipToPadding="false"
       android:orientation="vertical"
       android:padding="24dp"
       android:paddingTop="16dp">

       <ImageView
           android:layout_width="64dp"
           android:layout_height="64dp"
           android:layout_gravity="center_horizontal"
           android:layout_marginTop="48dp"
           android:layout_marginBottom="16dp"
           app:srcCompat="@drawable/shr_logo" />

       <TextView
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center_horizontal"
           android:layout_marginBottom="132dp"
           android:text="@string/shr_app_name"
           android:textAllCaps="true"
           android:textSize="16sp" />

       <!-- Snippet from "Add text fields" section goes here. -->

       <!-- Snippet from "Add buttons" section goes here. -->

   </LinearLayout>
</ScrollView>

Aqui, podemos ver um <LinearLayout> com um <ImageView> na parte de cima, representando o logotipo do "Shrine".

Em seguida, há uma tag <TextView> que representa o rótulo "SHRINE". O texto desse rótulo é um recurso de string chamado @string/shr_app_name. Se você Command + Click (ou Control + Click) no nome do recurso de string ou abrir app -> res -> values -> strings.xml, vai ver o arquivo strings.xml em que os recursos de string são definidos. Quando mais recursos de string forem adicionados no futuro, eles serão definidos aqui. Cada recurso nesse arquivo precisa ter um prefixo shr_ para indicar que faz parte do app Shrine.

Agora que você já conhece o código inicial, vamos implementar nosso primeiro componente.

Para começar, adicione dois campos de texto à página de login para as pessoas digitarem o nome de usuário e a senha. Vamos usar o componente de campo de texto do MDC, que inclui uma funcionalidade integrada que mostra um rótulo flutuante e mensagens de erro.

Adicionar o XML

Em shr_login_fragment.xml, adicione dois elementos TextInputLayout com um TextInputEditText filho dentro do <LinearLayout>, abaixo do rótulo "SHRINE" <TextView>:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

O snippet acima representa dois campos de texto, cada um consistindo de um elemento <TextInputLayout> e um filho <TextInputEditText>. O texto de dica de cada campo de texto é especificado no atributo android:hint.

Incluímos dois novos recursos de string para o campo de texto: @string/shr_hint_username e @string/shr_hint_password. Abra strings.xml para ver esses recursos de string.

strings.xml

...
<string name="shr_hint_username">Username</string>
<string name="shr_hint_password">Password</string>
...

Adicionar validação de entrada

Os componentes TextInputLayout oferecem funcionalidade integrada de feedback de erros.

Para mostrar feedback de erro, faça as seguintes mudanças em shr_login_fragment.xml:

  • Defina o atributo app:errorEnabled como "true" no elemento Password TextInputLayout. Isso vai adicionar um padding extra para a mensagem de erro abaixo do campo de texto.
  • Defina o atributo android:inputType como "textPassword" no elemento Senha TextInputEditText. Isso vai ocultar o texto de entrada no campo de senha.

Com essas mudanças, os campos de texto em shr_login_fragment.xml vão ficar assim:

shr_login_fragment.xml

<com.google.android.material.textfield.TextInputLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_username">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="text"
       android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/password_text_input"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="4dp"
   android:hint="@string/shr_hint_password"
   app:errorEnabled="true">

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/password_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>

Agora, tente executar o app. Você vai ver uma página com dois campos de texto para "Nome de usuário" e "Senha".

Veja a animação de etiquetas flutuantes:

Em seguida, adicionaremos dois botões à página de login: "Cancel" e "Next". Vamos usar o componente de botão do MDC, que vem com o efeito de ondulação de tinta icônico do Material Design integrado.

Adicionar o XML

Em shr_login_fragment.xml, adicione um <RelativeLayout> ao <LinearLayout>, abaixo dos elementos TextInputLayout. Em seguida, adicione dois elementos <MaterialButton> ao <RelativeLayout>.

O arquivo XML resultante vai ficar assim:

shr_login_fragment.xml

<RelativeLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <com.google.android.material.button.MaterialButton
       android:id="@+id/next_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentEnd="true"
       android:layout_alignParentRight="true"
       android:text="@string/shr_button_next" />

   <com.google.android.material.button.MaterialButton
       android:id="@+id/cancel_button"
       style="@style/Widget.MaterialComponents.Button.TextButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginEnd="12dp"
       android:layout_marginRight="12dp"
       android:layout_toStartOf="@id/next_button"
       android:layout_toLeftOf="@id/next_button"
       android:text="@string/shr_button_cancel" />

</RelativeLayout>

Pronto! Ao executar o app, uma ondulação de tinta vai aparecer quando você tocar em cada botão.

Por fim, vamos adicionar um código Java a LoginFragment.java para conectar o botão "PRÓXIMA" a outro fragmento. Cada um dos componentes adicionados ao layout tinha um id atribuído. Vamos usar esses ids para referenciar os componentes no nosso código e adicionar algumas verificações de erros e navegação.

Vamos adicionar um método booleano privado isPasswordValid em LoginFragment.java abaixo de onCreateView(), com lógica para determinar se a senha é válida ou não. Para esta demonstração, vamos apenas garantir que a senha tenha pelo menos oito caracteres:

LoginFragment.java

/*
   In reality, this will have more complex logic including, but not limited to, actual
   authentication of the username and password.
*/
private boolean isPasswordValid(@Nullable Editable text) {
   return text != null && text.length() >= 8;
}

Em seguida, adicione um listener de clique ao botão "Próxima" que define e limpa o erro com base no método isPasswordValid() que acabamos de criar. Em onCreateView(), esse listener de clique precisa ser colocado entre a linha do inflater e a linha return view.

Em seguida, vamos adicionar um listener de teclas à senha TextInputEditText para detectar eventos de teclas que limpam o erro. Esse listener também precisa usar isPasswordValid() para verificar se a senha é válida. Você pode adicionar isso diretamente abaixo do listener de clique em onCreateView().

Seu método onCreateView() vai ficar assim:

LoginFragment.java

@Override
public View onCreateView(
       @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   // Inflate the layout for this fragment
   View view = inflater.inflate(R.layout.shr_login_fragment, container, false);
   final TextInputLayout passwordTextInput = view.findViewById(R.id.password_text_input);
   final TextInputEditText passwordEditText = view.findViewById(R.id.password_edit_text);
   MaterialButton nextButton = view.findViewById(R.id.next_button);

   // Set an error if the password is less than 8 characters.
   nextButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
           if (!isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(getString(R.string.shr_error_password));
           } else {
               passwordTextInput.setError(null); // Clear the error
           }
       }
   });

   // Clear the error once more than 8 characters are typed.
   passwordEditText.setOnKeyListener(new View.OnKeyListener() {
       @Override
       public boolean onKey(View view, int i, KeyEvent keyEvent) {
           if (isPasswordValid(passwordEditText.getText())) {
               passwordTextInput.setError(null); //Clear the error
           }
           return false;
       }
   });
   return view;
}            

Agora, podemos navegar para outro fragmento. Atualize o OnClickListener em onCreateView() para navegar até outro fragmento quando a validação de erros for concluída. Para fazer isso, adicione a seguinte linha para navegar até ProductGridFragment no caso else do listener de clique:

LoginFragment.java

...
((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
...

Seu listener de clique vai ficar assim:

LoginFragment.java

...
MaterialButton nextButton = view.findViewById(R.id.next_button);

// Set an error if the password is less than 8 characters.
nextButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
       if (!isPasswordValid(passwordEditText.getText())) {
           passwordTextInput.setError(getString(R.string.shr_error_password));
       } else {
           passwordTextInput.setError(null); // Clear the error
           ((NavigationHost) getActivity()).navigateTo(new ProductGridFragment(), false); // Navigate to the next Fragment
       }
   }
});
...

Essa nova linha de código chama o método navigateTo() de MainActivity para navegar até um novo fragmento: ProductGridFragment. No momento, essa é uma página vazia em que você vai trabalhar no MDC-102.

Agora, crie o app. Clique no botão "Próxima".

Você conseguiu! Essa tela será o ponto de partida do nosso próximo codelab, o MDC-102.

Usando marcação XML básica e cerca de 30 linhas de Java, a biblioteca de componentes do Material para Android ajudou você a criar uma página de login bonita que segue as diretrizes do Material Design e tem aparência e comportamento consistentes em todos os dispositivos.

Próximas etapas

O campo de texto e o botão são dois componentes principais da biblioteca MDC Android, mas há muito mais! Você pode conhecer o restante dos componentes no MDC Android. Como alternativa, consulte MDC 102: estrutura e layout do Material Design para saber mais sobre a barra de apps na parte de cima, a visualização de card e o layout de grade. Agradecemos por testar os componentes do Material. Esperamos que tenha gostado deste codelab.

Este codelab exigiu esforço e tempo normais para ser concluído

Concordo totalmente Concordo Não concordo nem discordo Discordo Discordo totalmente

Quero continuar usando componentes do Material Design no futuro

Concordo totalmente Concordo Não concordo nem discordo Discordo Discordo totalmente