➙ Conteúdo completo, texto e vídeo, em: https://www.thiengo.com.br/data-binding-para-vinculo-de-dados-na-ui-android
Neste conjunto de slides vamos ao estudo e aplicação da biblioteca Android Data Binding, popular biblioteca que ajuda a diminuir o código boilerplate de vinculo de dados e métodos à interface do usuário.
➙ Para receber o conteúdo do blog em primeira mão, assine a lista de emails em: http://www.thiengo.com.br
Abraço.
▶ Treinamento oficial:
➙ Prototipagem Profissional de Aplicativos Android:
↳ https://www.udemy.com/android-prototipagem-profissional-de-aplicativos/?couponCode=DATA_BINDING&persist_locale&locale=pt_BR
▶ Livros oficiais:
➙ Desenvolvedor Kotlin Android - Bibliotecas para o dia a dia:
↳ https://www.thiengo.com.br/livro-desenvolvedor-kotlin-android
➙ Receitas Para Desenvolvedores Android:
↳ https://www.thiengo.com.br/livro-receitas-para-desenvolvedores-android
➙ Refatorando Para Programas Limpos:
↳ https://www.thiengo.com.br/livro-refatorando-para-programas-limpos
▶ Redes:
➙ Udemy: https://www.udemy.com/user/vinicius-thiengo/?persist_locale&locale=pt_BR
➙ YouTube: https://www.youtube.com/user/thiengoCalopsita
➙ Facebook: https://www.facebook.com/thiengoCalopsita
➙ LinkedIn: https://www.linkedin.com/in/vin%C3%ADcius-thiengo-5179b180/
➙ GitHub: https://github.com/viniciusthiengo
➙ Twitter: https://twitter.com/thiengoCalops
➙ Google Plus: https://plus.google.com/+ThiengoCalopsita
▶ Blog App:
➙ https://play.google.com/store/apps/details?id=br.thiengocalopsita&hl=pt_BR
2. Android Data Binding
O termo Data Binding já é bem conhecido no desenvolvimento de software. No
Android trabalhamos com o binding de dados com frequência, mesmo que não de
maneira eficiente.
O simples código a seguir, em Kotlin, é um exemplo de binding de dados e método:
3. A principal proposta da biblioteca Android Data Binding é diminuir, ou remover por
completo, todo código boilerplate de acesso a Views e então vinculação de dados e
métodos a elas.
O vinculo ainda vai existir, porém em código estático, direto no layout XML. Com
isso é possível conseguir maior tempo e espaço em código para foco somente no
domínio de problema do aplicativo em desenvolvimento.
Devido aos benefícios alcançados com o uso da Data Binding library no
desenvolvimento Android, muitas empresas de desenvolvimento mobile exigem do
profissional o conhecimento desta API.
A documentação é bem completa, há até uma parte em português, "Visão Geral".
Neste conjunto de slides vamos abordar as partes mais relevantes, logo, essa não
é uma fonte única de estudo e recomendo também que você leia a documentação
oficial para estudar alguns conceitos mais avançados que poderão ser úteis a ti:
Documentação oficial Data Binding Android.
Assim podemos partir para os códigos. Não deixe de também acompanhar o
projeto de exemplo que está em artigo, nele ficará mais evidente os ganhos quando
utilizando a Android Data Binding API.
4. Instalação da biblioteca
Diferente de quando utilizando o Java, com o Kotlin a configuração de instalação
da biblioteca Data Binding exige algumas referências a mais, mas todas no Gradle
App Level, ou build.gradle (Module: app):
Até o momento da
construção deste
conteúdo a versão
estável mais atual da
com.android.databinding
era a 3.1.4.
5. A versão da biblioteca é a mesma versão do plugin do Gradle, logo, no Gradle
Project Level, ou build.gradle (Project: NomeApp), podemos ter:
E então no Gradle App Level teríamos a atualização da referência
'com.android.databinding:compiler':
Para acompanhar as versões da API, entre em MVN Repository Data Binding.
Escolha sempre utilizar a versão estável mais atual para seus projetos em
produção.
6. Antes de partirmos para a próxima seção de slides, saiba que se você estivesse
utilizando a liguagem Java somente o código a seguir, no Gradle App Level, é que
seria necessário para liberar o trabalho com a Data Binding API:
7. Códigos estático e dinâmico sem Data Binding
Para seguirmos os estudos da Data Binding de maneira eficiente, primeiro vamos
ao XML de layout sem a sintaxe desta API:
8. Agora a simples classe de domínio, User:
Então o código de vinculo de alguns dados de user ao layout apresentado:
Observação: aqui estamos assumindo que o plugin kotlin-android-extensions
está sendo utilizado e assim não precisamos de códigos de acesso com
findViewById().
10. Trabalhando o binding de dados
O binding direto em arquivo XML de layout é bem simples. Vamos a atualização do
último XML apresentado, já fazendo uso da Data Binding API:
11. A tag <layout> passa a ser a tag root do layout. O bloco <data> é aonde estarão
as variáveis, <variable>, e as importações, <import>. E então as referências via
lambda @{ ... }. A expressão dentro de @{} deve retornar o valor esperado pelo
atributo.
Em <variable> temos os atributos:
• name: contém o rótulo da variável, o mesmo que será utilizado no arquivo
XML;
• type: contém o tipo da variável.
12. Ainda é preciso o binding no código dinâmico. Logo, no mesmo onCreate()
apresentado anteriormente, temos agora:
13. O código DataBindingUtil.setContentView() é um possível código de vinculo de
layout ao objeto dele (atividade, fragmento, dialog, ...). Na verdade o
DataBindingUtil.setContentView() é utilizado somente em contextos de
atividades.
O primeiro argumento é a atividade host do layout e o segundo argumento é a
referência do layout.
Note que para cada layout com código Data Binding é gerada uma classe binding,
classe tendo como rótulo o nome do arquivo de layout em modelo CamelCase e
com o sufixo Binding. Aqui a classe binding gerada foi a ActivityMainBinding,
referente ao layout activity_main.xml.
As variáveis definidas em <data> estarão presentes como propriedades do objeto
da classe binding. E é preciso o vinculo como foi realizado em binding.user =
user.
Se o vinculo não ocorrer, as Views não serão preenchidas e não haverá exceção
alguma para sinalizar o problema.
14. Executando o código anterior, temos o mesmo resultado:
Se removermos os comentários da última atualização de onCreate() teremos um
código ainda menor do que o anterior. Isso neste exemplo simples.
15. Problemas com a geração da classe binding
Caso você tenha já verificado todo o seu código Data Binding, certificado de que ele está correto
e mesmo assim alguma classe binding ainda não tenha sido gerada.
Como possíveis soluções, temos:
• Aplicando o Clean e Rebuild:
• Acesse "Build" no menu de topo do Android Studio; Acione "Clean Project";
• Ao final do processamento de "Clean Project", volte em "Build";
• Agora acione "Rebuild Project";
• Ao final do processamento de "Rebuild Project", verifique se a classe binding foi gerada.
• Reiniciando o Android Studio com limpeza de cache:
• Acesse a opção "File" no menu de topo do IDE;
• Acione a opção "Invalidate Caches / Restart…";
• Na caixa de diálogo aberta acione "Invalidate and Restart”;
• Ao final da reinicialização do IDE, verifique se a classe binding foi gerada.
16. Alguns desenvolvedores Android informaram na comunidade que a adição de
generateStubs = true ao Gradle App Level, como a seguir, fez com que as classes
binding problemáticas fossem geradas:
Logo, se mesmo tentando as duas primeiras soluções indicadas anteriormente a
classe binding não for gerada, tente a adição do código kapt acima e sincronize o
projeto.
17. Importando entidades
Na sintaxe XML Data Binding também é possível importar entidades por meio da
tag <import>. Veja o código a seguir:
O type de <variable> pôde referenciar somente o nome principal do tipo, neste
caso. Tipos de dados que já estão presentes no pacote java.lang não precisam da
adição explícita por meio de <import>.
A classe String é um desses tipos. Quando você passar a utilizar mais a biblioteca
Data Binding notará que muitas vezes você estará utilizando <import> para
aquelas classes estáticas de seu domínio de problema que contém constantes e
métodos úteis às visualizações no layout.
18. Invocando métodos
Também é possível trabalharmos a invocação de métodos. Veja a seguir a
atualização da classe User:
E então a adição de mais uma View no layout, View que recebe valor de
fullName():
20. Também é possível passar parâmetros e colocar métodos de outras classes. A
seguir uma nova classe, BindListener:
Então a adição de uma variável BindListener ao layout:
21. Agora o vinculo de um objeto BindListener ao objeto da classe binding de layout:
Importante: é possível que a nova variável adicionada ao layout não apareça
como opção de propriedade do objeto da classe binding, neste caso você deve dar
o rebuild no projeto, "Menu de topo" > "Build" > "Rebuild Project".
22. Por fim, voltando ao layout, o código de invocação do método onClickName():
Como informado nas seções iniciais, @{} representa um lambda, com isso, quando
não omitidos os parâmetros devem vir à esquerda da seta (->) e a expressão à
direita.
Ok, mas como eu sei quais são os possíveis parâmetros?
No caso da invocação de método é possível saber os parâmetros de acordo com o
atributo em uso.
No exemplo anterior estamos utilizando o android:onClick, e como sabemos, pelo
uso de onClick() da Interface View.OnClickListener, o único argumento enviado é
a View que tem o vinculo com o método ouvidor.
23. Executando o projeto com o novo código e acionando o TextView de nome
completo, temos:
24. Veja como seria no caso de um CheckBox, por exemplo. Na classe BindListener
adicione o método a seguir:
Agora a atualização no layout:
Se você acessar a documentação de CompoundButton.OnCheckedChangeListener
confirmará que o onCheckedChanged() tem dois parâmetros.
26. Configurando métodos ouvidores
É possível trabalhar diretamente com métodos ouvidores de Interfaces criadas
somente para isso. Veja a seguir o novo código de BindListener:
27. Então a vinculação do método ouvidor direto ao atributo android:onLongClick:
28. Executando o projeto com o novo código e acionando o toque / clique longo no
TextView de nome completo, temos:
29. Note que a sintaxe variável::método pode ser utilizada para qualquer método que
respeite a assinatura e retorno de método aguardado pelo atributo em uso. No caso
do android:onLongClick poderia ser o método a seguir, que não vem de nenhuma
Interface exclusiva de listener:
30. A documentação informa que a possibilidade de conflito entre gerenciadores de
eventos que também fariam uso do evento de clique já foi resolvida com a adição
de novos atributos:
Classe Configurador do ouvinte Atributo
SearchView setOnSearchClickListener( View.OnClickListener ) android:onSearchClick
ZoomControls setOnZoomInClickListener( View.OnClickListener ) android:onZoomIn
ZoomControls setOnZoomOutClickListener( View.OnClickListener ) android:onZoomOut
31. Ouvidores via @BindingAdapter
É possível colocar listener de atributo, até mesmo de atributo personalizado. A
anotação @BindingAdapter nos permiti isso.
Para esta seção, assuma que a Picasso API já está incluída no projeto e a
permissão de Internet adicionada ao AndroidManifest.xml.
Antes de apresentar um método BindAdapter saiba que ele tem de ser estático e
no Kotlin não basta somente colocarmos o método no escopo de um companion
object, ainda é necessário o uso da anotação @JvmStatic.
Na classe BindListener adicione o trecho de código a seguir:
32. Dê atenção especial ao código do slide anterior, pois na época da construção deste
conteúdo, na documentação oficial, quando apresentando os códigos em versão
Kotlin, não havia nada sobre a sintaxe necessária de "entidade estática" para que
métodos BindAdapter funcionassem no Kotlin, alias tinha um código não atualizado
que simplesmente nem permitia a compilação do projeto.
Voltando a anotação @BindingAdapter... os argumentos esperados são os rótulos
dos atributos. Tome muito cuidado neste ponto, pois se fosse colocado somente,
por exemplo, "text", todos os TextViews que tivessem o atributo android:text
sendo utilizado acionariam o método loadRemoteImage().
Logo, o que recomendo é que em caso de necessidade de um método
@BindingAdapter, busque utilizar um nome específico para o atributo.
Outra coisa: o namespace não é necessário em @BindingAdapter, mas
poderíamos colocar "app:picassoLoad", por exemplo, se o namespace em uso
fosse app:.
33. Os parâmetros do método BindAdapter são dispostos na seguinte ordem:
• Primeiro o objeto da View que tem o listener de atributo;
• Os parâmetros seguintes são os valores colocados nos atributos referenciados
em @BindingAdapter, na ordem de referência.
Caso fosse necessário definir mais atributos, somente teríamos de defini-los com a
separação por vírgula, veja a seguir
34. Para que um método BindAdapter seja acionado todos os atributos declarados nele
devem estar presentes na View ou então devemos explicitar em código que não há
necessidade de que todos os atributos estejam sendo utilizados, como a seguir:
35. Ainda temos de adicionar a propriedade de imagem a classe User:
E também adicionar o ImageView ao layout:
36. Lembrando que o atributo picassoLoad não foi criado em lugar algum,
simplesmente escolhemos este rótulo, pois condiz com a funcionalidade do método
BindAdapter. Não é preciso criar o novo atributo em algum local específico do
projeto.
Executando o projeto com o novo código, temos:
37. Note que apesar de o método BindAdapter criado estar dentro da classe
BindListener, métodos BindAdapter não necessitam da vinculação em código,
digo, vinculação como fizemos no onCreate() anteriormente:
E é devido a essa particularidade de @BindingAdapter que nós desenvolvedores
temos de ter cautela no uso dele, pois é possível termos os mesmos problemas
encontrados no uso de go to ou de "variável global”. Nem mesmo o <import> de
classe é necessário para que um método BindAdapter funcione.
38. Trabalhando com o vinculo de entidades estáticas
Para invocar métodos estáticos, sem o uso de @BindAdapter, ou até mesmo
propriedades, basta adicionar a classe container de entidades estáticas via
<import> e então trabalhar as referências. Veja a nova classe a seguir:
39. Então a atualização do layout que estamos trabalhando como layout de exemplo:
41. Bind via <include>
Para aproveitar variáveis já declaradas em um layout pai, podemos passa-las por
meio de <include> com um atributo referente ao nome da variável.
Veja a seguir o novo layout de nome completo, /res/layout/full_name.xml:
Veja como ainda é necessária toda a configuração de tags (<layout>, <data>,
<import> e <variable>) para uso de uma variável herdada do layout pai.
42. Então o código de inclusão do layout anterior no XML principal, incluindo o envio da
variável user:
44. Expressões em sintaxe Java
Apesar de a linguagem dinâmica em uso nos exemplos aqui ser Kotlin, as
expressões dentro de @{} são em maioria em Java, digo em maioria, pois há a
possibilidade de uso do operador null-safe, ?, neste caso utilizando duas
interrogações, ??.
Veja a seguir um código de exemplo fazendo uso do operador ternário, não
disponível em Kotlin, mas em Java. Código adicionado ao layout principal abordado
até aqui, activity_main.xml:
Veja que quando for necessário utilizar uma String dentro da expressão em @{} o
roteiro é utilizar aspas simples, ', como aspas principais e as aspas duplas, ", para
a String.
46. Veja uma outra maneira de fornecer o nome completo do usuário:
Lembrando que String faz parte do pacote java.lang e assim não precisa de um
<import> no bloco <data>.
48. Também é possível aplicar casting no modelo Java:
Mesmo no cast, se o tipo referenciado não estiver presente no pacote java.lang e
nos imports já incluídos no bloco <data>, um <import> para esse tipo será
necessário.
Importante:
Tome cuidado com as possibilidades de código como expressão em @{}, pois o
objetivo do Data Binding é facilitar as coisas, ou seja, se houverem expressões
complexas sendo utilizadas no binding é bem provável que o uso da biblioteca
traga mais pontos negativos, em termos de leitura de código, do que pontos
positivos.
49. Se for necessária a comparação utilizando os sinais de maior, >, e menor, <, na
verdade os códigos HTML destes símbolos, respectivamente > e <, é que
deverão ser utilizados. Veja o exemplo a seguir:
O código acima não compila, pois os símbolos > e < não são processáveis como
parte de uma expressão. O código a seguir compila sem problemas:
50. Objeto de contexto
Quando desenvolvendo aplicativos Android é comum que seja necessário o uso do
objeto de contexto para acesso a algum recurso.
A API Data Binding disponibiliza para nós o objeto context representando o
contexto em uso.
Por questões de didática vamos modificar o método mudancaStatus() de
BindListener para fazer uso de um contexto passado como parâmetro:
53. Binding no menu gaveta, NavigationView
A partir deste ponto abordaremos alguns exemplos de Data Binding que certamente serão úteis
a maioria dos domínios de problema, pois é a aplicação do binding em componentes visuais
comumente utilizados no desenvolvimento Android.
Veja a seguir o código XML de cabeçalho de um menu gaveta, código já com a configuração
Data Binding. O path do layout de cabeçalho é /res/layout/nav_header.xml:
54. Por motivos de brevidade, no exemplo vamos omitir a apresentação da nova classe
User. Nossa prioridade aqui é mostrar como acessar a classe binding de cabeçalho
de menu gaveta para então vincular um objeto a ela.
A seguir o layout que contém o NavigationView que referencia o layout de
cabeçalho apresentado no slide anterior:
55. Agora o modo de acesso, em código dinâmico, a classe binding referente ao layout
de cabeçalho:
A invocação getHeaderView(0) retorna o objeto ViewGroup root do layout de
cabeçalho, aqui o LinearLayout.
Como não havia necessidade de bind de dados e métodos no layout ancestral do
layout de cabeçalho, não foi necessária a inclusão de código Data Binding nele.
56. Binding em caixa de diálogo
A seguir um simples layout personalizado para um AlertDialog, layout já com os
códigos Data Binding. Segue /res/layout/dialog_custom.xml:
57. Então o código dinâmico que contém a classe binding do layout anterior:
58. Binding em adapter de lista, RecyclerView
A seguir o código XML do layout de item do adapter de framework de lista, código
já com sintaxe Data Binding. Segue /res/layout/user.xml:
59. Agora o código da classe adapter com o vinculo Data Binding:
60. Note a importância da invocação do método executePendingBindings() para que
a atualização de item ocorra imediatamente. Este método deve ser invocado de
maneira direta ou indireta pelo onBindViewHolder().
No slide seguir um exemplo de binding de dados via Data Binding API quando
temos mais de um layout de item, mais precisamente, em nosso exemplo, os
layouts header.xml e regular_item.xml. Segue classe adaptadora:
62. Binding em fragmento
A seguir um layout de exemplo já com o código binding. Segue /res/layout/
fragment_user.xml:
64. Pontos negativos
• Não é possível ter acesso, no código XML, ao objeto da atividade host, mesmo
quando aplicando casting na propriedade context;
• A documentação falhou na apresentação da sintaxe de entidades estáticas
para Data Binding em Kotlin. Não há nada sobre a necessidade do companion
object e nem sobre a anotação @JvmStatic;
• Depois de muitos testes é percebido que métodos ouvidores muitas vezes são
mais eficientes em leitura de código se utilizando o objeto View e o método de
listener tradicionais, sem o auxílio do Data Binding.
65. Pontos positivos
• O código dinâmico (Java, Kotlin) realmente fica mais simples sem a
necessidade de códigos tradicionais de binding de dados;
• A sintaxe de uso da API é bem simples;
• O suporte a layouts nos mais diferentes lugares (fragmento, atividade, caixa de
diálogo, adapter, ...) ajuda em muito no uso completo da biblioteca;
• Métodos @BindingAdapter e a propriedade context facilitam em muito o
vinculo de métodos com códigos e assinaturas mais complexas.
66. Conclusão
Neste artigo optei por abordar as partes mais importantes para o uso imediato da
biblioteca Data Binding, isso, pois a biblioteca e a documentação dela são bem
maiores.
Logo, sem o objetivo de ter aqui uma fonte única de estudos sobre a Data Binding
API, mesmo acreditando termos abordado o essencial, não deixe de também
acessar a documentação oficial para estudar ao menos algumas características
mais complexas.
De qualquer forma, conhecer e dominar a biblioteca Data Binding no Android pode
até mesmo lhe proporcionar melhores posições no mercado de desenvolvedor
mobile.
A API é excelente, permite o uso mesclado de binding via API e binding via
modelo tradicional (utilizando findViewById(), por exemplo) e não tem
comprometimentos em termos de performance de processamento.
67. Fontes
Conteúdo completo, em texto e em vídeo, no link a seguir:
• Data Binding Para Vinculo de Dados na UI Android.
Fonte:
• Documentação oficial Data Binding Android;
• Annotation Processing with Kotlin;
• Loading images with data binding and Picasso;
• Android Data Binding: 2-way Your Way;
• Binding Spinner in Android using DataBinding & InverseDataBinding;
• Android Data Binding: RecyclerView;
• How to use Data Binding and Kotlin in Android Studio 3.0.0 - Resposta de
Rubber Duck;
• How to data bind to a header? - Resposta de Ready Android;
• Android databinding using && logical operator - Resposta de Leonardo Acevedo;
• How to use data-binding in Dialog? - Resposta de IHeartAndroid;
• How to use data-binding with Fragment - Resposta de hdioui abdeljalil e de
Paweł Szczur;
• Android recyclerview adapter with multiple viewtype using databinding -
Resposta de macros013.
68. Para estudo
• Treinamento oficial:
• Prototipagem Profissional de Aplicativos Android.
• Meus livros:
• Receitas Para Desenvolvedores Android;
• Refatorando Para Programas Limpos.
• Redes:
• Udemy;
• YouTube;
• Facebook;
• LinkedIn;
• GitHub;
• Twitter;
• Google Plus.
• Blog App.
69. Data Binding Para Vinculo de Dados
na UI Android
thiengo.com.br
Vinícius Thiengo
thiengocalopsita@gmail.com