Conceitos básicos do Kotlin para Android 02.4: noções básicas da vinculação de dados

Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você vai aproveitar mais este curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página inicial dos codelabs de princípios básicos do Kotlin para Android.

Introdução

Em codelabs anteriores deste curso, você usou a função findViewById() para receber referências a visualizações. Quando o app tem hierarquias de visualização complexas, o findViewById() é caro e deixa o app mais lento, porque o Android percorre a hierarquia de visualização, começando pela raiz, até encontrar a visualização desejada. Felizmente, existe uma maneira melhor.

Para definir dados em visualizações, você usou recursos de string e definiu os dados da atividade. Seria mais eficiente se a visualização soubesse sobre os dados. Felizmente, isso é possível.

Neste codelab, você vai aprender a usar a vinculação de dados para eliminar a necessidade de findViewById(). Você também vai aprender a usar a vinculação de dados para acessar dados diretamente de uma visualização.

O que você já precisa saber

Você precisa:

  • O que é uma atividade e como configurar uma atividade com um layout em onCreate().
  • Criar uma visualização de texto e definir o texto que ela vai mostrar.
  • Usar findViewById() para receber uma referência a uma visualização.
  • Como criar e editar um layout XML básico para uma visualização.

O que você vai aprender

  • Como usar a biblioteca Data Binding para eliminar chamadas ineficientes para findViewById().
  • Como acessar dados de apps diretamente do XML.

Atividades deste laboratório

  • Modifique um app para usar a vinculação de dados em vez de findViewById() e acesse os dados diretamente dos arquivos XML de layout.

Neste codelab, você vai começar com o app AboutMe e mudar o app para usar a vinculação de dados. O app vai ficar exatamente igual quando você terminar.

Confira o que o app AboutMe faz:

  • Quando o usuário abre o app, ele mostra um nome, um campo para inserir um apelido, um botão Concluído, uma imagem de estrela e um texto rolável.
  • O usuário pode inserir um apelido e tocar no botão Concluído. O campo editável e o botão são substituídos por uma visualização de texto que mostra o apelido inserido.


Você pode usar o código criado no codelab anterior ou fazer o download do código AboutMeDataBinding-Starter no GitHub.

O código que você escreveu em codelabs anteriores usa a função findViewById() para receber referências a visualizações.

Sempre que você usa findViewById() para pesquisar uma visualização depois que ela é criada ou recriada, o sistema Android percorre a hierarquia de visualizações no momento da execução para encontrá-la. Quando o app tem apenas algumas visualizações, isso não é um problema. No entanto, os apps de produção podem ter dezenas de visualizações em um layout, e mesmo com o melhor design, haverá visualizações aninhadas.

Imagine um layout linear que contém uma visualização de rolagem que contém uma visualização de texto. Em uma hierarquia de visualizações grande ou profunda, encontrar uma visualização pode levar tempo suficiente para diminuir a velocidade do app para o usuário. O armazenamento em cache de visualizações em variáveis pode ajudar, mas ainda é necessário inicializar uma variável para cada visualização, em cada namespace. Com muitas visualizações e várias atividades, isso também aumenta.

Uma solução é criar um objeto que contenha uma referência a cada visualização. Esse objeto, chamado de objeto Binding, pode ser usado por todo o app. Essa técnica é chamada de vinculação de dados. Depois que um objeto de vinculação é criado para seu app, é possível acessar as visualizações e outros dados por ele, sem precisar percorrer a hierarquia de visualização ou pesquisar os dados.

A vinculação de dados tem os seguintes benefícios:

  • O código é mais curto, fácil de ler e de manter do que o que usa findByView().
  • Os dados e as visualizações estão claramente separados. Esse benefício da vinculação de dados se torna cada vez mais importante mais adiante neste curso.
  • O sistema Android percorre a hierarquia de visualização apenas uma vez para receber cada visualização, e isso acontece durante a inicialização do app, não em tempo de execução quando o usuário está interagindo com ele.
  • Você recebe segurança de tipos para acessar visualizações. Segurança de tipo significa que o compilador valida tipos durante a compilação e gera um erro se você tentar atribuir o tipo errado a uma variável.

Nesta tarefa, você vai configurar a vinculação de dados e usá-la para substituir chamadas de findViewById() por chamadas ao objeto de vinculação.

Etapa 1: ativar a vinculação de dados

Para usar a vinculação de dados, é necessário ativá-la no arquivo Gradle, já que ela não é ativada por padrão. Isso acontece porque a vinculação de dados aumenta o tempo de compilação e pode afetar o tempo de inicialização do app.

  1. Se você não tiver o app AboutMe de um codelab anterior, acesse o código AboutMeDataBinding-Starter no GitHub. Abra no Android Studio.
  2. Abra o arquivo build.gradle (Module: app).
  3. Na seção android, antes da chave de fechamento, adicione uma seção dataBinding e defina enabled como true.
dataBinding {
    enabled = true
}
  1. Quando solicitado, sincronize o projeto. Se não aparecer uma solicitação, selecione File > Sync Project with Gradle Files.
  2. Você pode executar o app, mas não vai notar nenhuma mudança.

Etapa 2: mudar o arquivo de layout para que ele possa ser usado com a vinculação de dados

Para trabalhar com vinculação de dados, você precisa incluir o layout XML em uma tag <layout>. Assim, a classe raiz não é mais um grupo de visualizações, mas sim um layout que contém grupos de visualizações e visualizações. O objeto de vinculação pode então conhecer o layout e as visualizações nele.

  1. Abra o arquivo activity_main.xml.
  2. Mude para a guia Texto.
  3. Adicione <layout></layout> como a tag mais externa ao redor de <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Escolha Code > Reformat code para corrigir o recuo do código.

    As declarações de namespace de um layout precisam estar na tag mais externa.
  1. Corte as declarações de namespace do <LinearLayout> e cole-as na tag <layout>. Sua tag de abertura <layout> deve ter a aparência mostrada abaixo, e a tag <LinearLayout> só pode conter propriedades de visualização.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Crie e execute o app para verificar se você fez isso corretamente.

Etapa 3: criar um objeto de vinculação na atividade principal

Adicione uma referência ao objeto de vinculação à atividade principal para poder usá-lo no acesso às visualizações:

  1. Abra o arquivo MainActivity.kt.
  2. Antes de onCreate(), no nível superior, crie uma variável para o objeto de vinculação. Essa variável é chamada de binding.

    O tipo de binding, a classe ActivityMainBinding, é criado pelo compilador especificamente para essa atividade principal. O nome é derivado do nome do arquivo de layout, ou seja, activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Se solicitado pelo Android Studio, importe ActivityMainBinding. Se não aparecer uma solicitação, clique em ActivityMainBinding e pressione Alt+Enter (Option+Enter em um Mac) para importar essa classe ausente. Para mais atalhos de teclado, consulte Atalhos de teclado.

    A instrução import deve ser semelhante à mostrada abaixo.
import com.example.android.aboutme.databinding.ActivityMainBinding

Em seguida, substitua a função setContentView() atual por uma instrução que faça o seguinte:

  • Cria o objeto de vinculação.
  • Usa a função setContentView() da classe DataBindingUtil para associar o layout activity_main ao MainActivity. Essa função setContentView() também cuida de algumas configurações de vinculação de dados para as visualizações.
  1. Em onCreate(), substitua a chamada setContentView() pela seguinte linha de código.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importe DataBindingUtil.
import androidx.databinding.DataBindingUtil

Etapa 4: usar o objeto de vinculação para substituir todas as chamadas para findViewById()

Agora você pode substituir todas as chamadas para findViewById() por referências às visualizações que estão no objeto de vinculação. Quando o objeto de vinculação é criado, o compilador gera os nomes das visualizações no objeto de vinculação com base nos IDs das visualizações no layout, convertendo-os para camel case. Por exemplo, done_button é doneButton no objeto de vinculação, nickname_edit se torna nicknameEdit e nickname_text se torna nicknameText.

  1. Em onCreate(), substitua o código que usa findViewById() para encontrar o done_button por um código que faz referência ao botão no objeto de vinculação.

    Substitua este código: findViewById<Button>(R.id.done_button)
    por: binding.doneButton

    O código finalizado para definir o listener de clique em onCreate() vai ficar assim.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Faça o mesmo para todas as chamadas de findViewById() na função addNickname().
    Substitua todas as ocorrências de findViewById<View>(R.id.id_view) por binding.idView. Faça isso da seguinte maneira:
  • Exclua as definições das variáveis editText e nicknameTextView com as chamadas para findViewById(). Isso vai gerar erros.
  • Para corrigir os erros, extraia as visualizações nicknameText, nicknameEdit e doneButton do objeto binding em vez das variáveis (excluídas).
  • Substitua view.visibility por binding.doneButton.visibility. Usar binding.doneButton em vez de view transmitido torna o código mais consistente.

    O resultado é o seguinte código:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Não há mudanças na funcionalidade. Opcionalmente, é possível eliminar o parâmetro view e atualizar todos os usos de view para usar binding.doneButton dentro dessa função.
  1. O nicknameText exige um String, e nicknameEdit.text é um Editable. Ao usar a vinculação de dados, é necessário converter explicitamente o Editable em um String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Você pode excluir as importações esmaecidas.
  2. Kotlinize a função usando apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Crie e execute o app. Ele vai parecer e funcionar exatamente como antes.

Você pode aproveitar a vinculação de dados para disponibilizar uma classe de dados diretamente a uma visualização. Essa técnica simplifica o código e é extremamente valiosa para lidar com casos mais complexos.

Neste exemplo, em vez de definir o nome e o apelido usando recursos de string, você cria uma classe de dados para o nome e o apelido. Você disponibiliza a classe de dados para a visualização usando a vinculação de dados.

Etapa 1: criar a classe de dados MyName

  1. No Android Studio, no diretório java, abra o arquivo MyName.kt. Se você não tiver esse arquivo, crie um novo arquivo Kotlin e chame-o de MyName.kt.
  2. Defina uma classe de dados para o nome e o apelido. Use strings vazias como valores padrão.
data class MyName(var name: String = "", var nickname: String = "")

Etapa 2: adicionar dados ao layout

No arquivo activity_main.xml, o nome está definido em um TextView de um recurso de string. É necessário substituir a referência ao nome por uma referência aos dados na classe de dados.

  1. Abra activity_main.xml na guia Texto.
  2. Na parte de cima do layout, entre as tags <layout> e <LinearLayout>, insira uma tag <data></data>. É aqui que você vai conectar a visualização aos dados.
<data>
  
</data>

Dentro das tags de dados, você pode declarar variáveis nomeadas que contêm uma referência a uma classe.

  1. Na tag <data>, adicione uma tag <variable>.
  2. Adicione um parâmetro name para dar à variável o nome "myName". Adicione um parâmetro type e defina o tipo como um nome totalmente qualificado da classe de dados MyName (nome do pacote + nome da variável).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Agora, em vez de usar o recurso de string para o nome, você pode fazer referência à variável myName.

  1. Substitua android:text="@string/name" pelo código abaixo.

@={} é uma diretiva para receber os dados referenciados dentro das chaves.

myName faz referência à variável myName que você definiu anteriormente, que aponta para a classe de dados myName e busca a propriedade name da classe.

android:text="@={myName.name}"

Etapa 3: criar os dados

Agora você tem uma referência aos dados no arquivo de layout. Em seguida, crie os dados reais.

  1. Abra o arquivo MainActivity.kt.
  2. Acima de onCreate(), crie uma variável particular, também chamada myName por convenção. Atribua à variável uma instância da classe de dados MyName, transmitindo o nome.
private val myName: MyName = MyName("Aleks Haecky")
  1. Em onCreate(), defina o valor da variável myName no arquivo de layout como o valor da variável myName que você acabou de declarar. Não é possível acessar a variável diretamente no XML. Você precisa acessá-lo pelo objeto de vinculação.
binding.myName = myName
  1. Isso pode mostrar um erro, porque é necessário atualizar o objeto de vinculação depois de fazer alterações. Crie o app, e o erro vai desaparecer.

Etapa 4: usar a classe de dados para o apelido na TextView

A última etapa é usar a classe de dados para o apelido no TextView.

  1. Abra activity_main.xml.
  2. Na visualização de texto nickname_text, adicione uma propriedade text. Faça referência ao nickname na classe de dados, conforme mostrado abaixo.
android:text="@={myName.nickname}"
  1. Em ActivityMain, substitua
    nicknameText.text = nicknameEdit.text.toString()
    por um código para definir o apelido na variável myName.
myName?.nickname = nicknameEdit.text.toString()

Depois que o apelido for definido, você vai querer que seu código atualize a interface com os novos dados. Para isso, invalide todas as expressões de vinculação para que sejam recriadas com os dados corretos.

  1. Adicione invalidateAll() depois de definir o apelido para que a interface seja atualizada com o valor no objeto de vinculação atualizado.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Crie e execute o app. Ele vai funcionar exatamente da mesma forma que antes.

Projeto do Android Studio: AboutMeDataBinding

Etapas para usar a vinculação de dados e substituir chamadas para findViewById():

  1. Ative a vinculação de dados na seção android do arquivo build.gradle:
    dataBinding { enabled = true }
  2. Use <layout> como a visualização raiz no layout XML.
  3. Defina uma variável de vinculação:
    private lateinit var binding: ActivityMainBinding
  4. Crie um objeto de vinculação em MainActivity, substituindo setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Substitua as chamadas para findViewById() por referências à visualização no objeto de vinculação. Por exemplo:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    . No exemplo, o nome da visualização é gerado em camelCase do id da visualização no XML.

Etapas para vincular visualizações a dados:

  1. Crie uma classe de dados para seus dados.
  2. Adicione um bloco <data> dentro da tag <layout>.
  3. Defina um <variable> com um nome e um tipo que seja a classe de dados.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. Em MainActivity, crie uma variável com uma instância da classe de dados. Por exemplo:
    private val myName: MyName = MyName("Aleks Haecky")
  1. No objeto de vinculação, defina a variável como a que você acabou de criar:
    binding.myName = myName
  1. No XML, defina o conteúdo da visualização para a variável definida no bloco <data>. Use a notação de ponto para acessar os dados na classe de dados.
    android:text="@={myName.name}"

Curso da Udacity:

Documentação do desenvolvedor Android:

Esta seção lista as possíveis atividades de dever de casa para os alunos que estão fazendo este codelab como parte de um curso ministrado por um professor. Cabe ao professor fazer o seguinte:

  • Atribuir o dever de casa, se necessário.
  • Informar aos alunos como enviar deveres de casa.
  • Atribuir nota aos deveres de casa.

Os professores podem usar essas sugestões o quanto quiserem, podendo passar os exercícios que acharem mais apropriados como dever de casa.

Se você estiver seguindo este codelab por conta própria, sinta-se à vontade para usar esses deveres de casa para testar seu conhecimento.

Responda estas perguntas

Pergunta 1

Por que você quer minimizar as chamadas explícitas e implícitas para findViewById()?

  • Toda vez que o findViewById() é chamado, ele passa pela hierarquia de visualização.
  • O findViewById() é executado na linha de execução principal ou de interface.
  • Essas chamadas podem deixar a interface do usuário mais lenta.
  • Seu app tem menos probabilidade de falhar.

Pergunta 2

Como você descreveria a vinculação de dados?

Por exemplo, confira algumas coisas que você pode dizer sobre a vinculação de dados:

  • A grande ideia sobre vinculação de dados é criar um objeto que conecta/mapeia/vincula duas informações distantes no momento da compilação, para que você não precise procurar os dados no tempo de execução.
  • O objeto que mostra essas vinculações para você é chamado de objeto de vinculação.
  • O objeto de vinculação é criado pelo compilador.

Pergunta 3

Qual das opções a seguir NÃO é um benefício da vinculação de dados?

  • O código é mais curto e a manutenção e leitura dele são mais fáceis.
  • Os dados e as visualizações estão claramente separados.
  • O sistema Android percorre a hierarquia de visualizações apenas uma vez para receber cada visualização.
  • Chamar findViewById() gera um erro de compilador.
  • Segurança de tipo para acessar visualizações.

Pergunta 4

Qual é a função da tag <layout>?

  • Você o envolve na visualização raiz no layout.
  • As vinculações são criadas para todas as visualizações em um layout.
  • Ele designa a visualização de nível superior em um layout XML que usa vinculação de dados.
  • Você pode usar a tag <data> dentro de um <layout> para vincular uma variável a uma classe de dados.

Pergunta 5

Qual é a maneira correta de referenciar os dados vinculados no layout XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Comece a próxima lição: 3.1: criar um fragmento

Para acessar links de outros codelabs neste curso, consulte a página inicial dos codelabs de conceitos básicos do Kotlin para Android.