Data Binding — uma biblioteca Android Jetpack que vincula componentes de UI de layouts XML a fontes de dados no código da aplicação através de sintaxe declarativa. Explicamos os fundamentos: Data Binding elimina o código boilerplate findViewById() e permite atualizar a UI automaticamente quando os dados mudam. De acordo com o Google (Android Developers, 2025), o Data Binding é usado em 45% dos projetos Android e, em combinação com LiveData ou StateFlow, fornece vinculação completamente reativa sem gerenciamento manual de assinaturas.
Pontos Principais
@={} em XML.Data Binding é uma biblioteca de suporte (Android Jetpack) que apareceu pela primeira vez em 2015 no Google I/O e foi estabilizada no Android Gradle Plugin 1.5. Ela permite vincular componentes de UI em XML a fontes de dados (POJO, ViewModel, LiveData) diretamente no layout, sem chamar findViewById() no código da Activity ou Fragment.
Como funciona: o layout XML é envolvido em uma tag <layout>, que declara uma <variable> com um tipo de dado. Dentro do layout, os dados são substituídos através de expressões entre chaves @{}. Em tempo de compilação, o Android Gradle Plugin gera uma classe Binding (por exemplo, ActivityMainBinding) contendo referências diretas às Views com tipos corretos e métodos para definir dados.
De acordo com a pesquisa Android Developers (2024), o Data Binding reduz a quantidade de código UI em Activity/Fragment em 30–50% ao transferir a lógica de vinculação para o XML. O número de erros relacionados a tipos incorretos de View (ClassCastException com findViewById()) cai para zero, já que todos os tipos são verificados em tempo de compilação.
ViewBinding é uma alternativa mais leve ao Data Binding, introduzida no Android Studio 3.6 (2020). O ViewBinding gera uma classe Binding para cada arquivo de layout, mas sem suporte para expressões, variáveis e reatividade. Comparação por critérios-chave:
| Critério | Data Binding | ViewBinding |
|---|---|---|
| Geração de classe Binding | Sim | Sim |
| Expressões em XML (@{}) | Sim | Não |
| Vinculação bidirecional | Sim | Não |
| Reativo (LiveData) | Sim | Não |
| @BindingAdapter | Sim | Não |
| Velocidade de compilação | Mais lenta (processamento de expressões) | Mais rápida |
| Complexidade | Alta | Baixa |
Recomendação do Google (Android Developers, 2025): para a maioria dos projetos, o ViewBinding é suficiente — ele fornece acesso type-safe às Views sem a sobrecarga do Data Binding. Escolha Data Binding se precisar de: (1) vinculação reativa com LiveData/StateFlow a partir do XML, (2) vinculação bidirecional para formulários, (3) BindingAdapter para atributos personalizados, (4) expressões em XML para formatação. Na IT Sectr, usamos ViewBinding para telas simples e Data Binding para formulários e painéis complexos.
Vinculação unidirecional (@{}) transmite dados da fonte (ViewModel) para a View. Vinculação bidirecional (@={}) sincroniza dados em ambas as direções: alterações na View (entrada de texto, alternância de Switch) atualizam automaticamente a fonte.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.LoginViewModel" />
</data>
<LinearLayout ...>
<!-- Unidirecional: dados do ViewModel para TextView -->
<TextView
android:text="@{viewModel.userName}" />
<!-- Bidirecional: alterações EditText → ViewModel, ViewModel → EditText -->
<EditText
android:text="@{=viewModel.email}" />
<CheckBox
android:checked="@{=viewModel.agreeToTerms}" />
</LinearLayout>
</layout>
Para vinculação bidirecional, o ViewModel deve usar ObservableField, LiveData ou StateFlow. Quando os dados mudam através da entrada do usuário, o Data Binding chama automaticamente o setter da fonte. Importante: a vinculação bidirecional funciona com atributos que possuem um @InverseBindingAdapter definido. O Android fornece adaptadores integrados para: text, checked, visibility, progress, rating e outros atributos padrão.
@BindingAdapter é uma anotação para funções de extensão Kotlin que permite definir lógica de vinculação personalizada para qualquer atributo de View. Por exemplo, carregar uma imagem via Glide ao especificar uma URL em XML, ou formatar uma data ao vincular a um TextView.
// BindingAdapter para carregar imagem por URL
@BindingAdapter("imageUrl")
fun ImageView.setImageUrl(url: String?) {
Glide.with(this.context)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(this)
}
// BindingAdapter com vários atributos
@BindingAdapter("visibleGone")
fun View.setVisibleGone(visible: Boolean) {
visibility = if (visible) View.VISIBLE else View.GONE
}
// BindingAdapter com conversor (formatDate)
@BindingAdapter("formattedDate")
fun TextView.setFormattedDate(timestamp: Long) {
text = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date(timestamp))
}
<!-- Usar BindingAdapter em XML -->
<ImageView
imageUrl="@{user.avatarUrl}"
android:layout_width="48dp"
android:layout_height="48dp" />
<TextView
formattedDate="@{message.createdAt}"
visibleGone="@{message.isVisible}" />
@BindingAdapter pode aceitar vários atributos (requireAll = true/false), permitindo combinar valores. Por exemplo, @BindingAdapter("imageUrl", "circleCrop") — se circleCrop for true, o Glide aplica a transformação CircleCrop. De acordo com o Google (Android Performance, 2024), o BindingAdapter com Glide no Data Binding processa até 60 quadros por segundo ao percorrer um RecyclerView, já que o carregamento assíncrono não bloqueia a thread da UI.
Data Binding suporta LiveData nativamente desde o Android Architecture Components 1.0. Se uma variável no layout tem o tipo LiveData, o Binding se inscreve automaticamente nela e atualiza a UI quando o valor muda. Para o funcionamento correto, é necessário definir o LifecycleOwner na classe Binding: binding.lifecycleOwner = viewLifecycleOwner.
// ViewModel com LiveData
class WeatherViewModel : ViewModel() {
private val _temperature = MutableLiveData("--")
val temperature: LiveData<String> get() = _temperature
val cityName = MutableLiveData("Moscou")
val weatherIcon = MutableLiveData(R.drawable.ic_sunny)
fun refresh() {
viewModelScope.launch {
_temperature.value = weatherRepository.getTemperature()
}
}
}
// Em Fragment:
val binding = FragmentWeatherBinding.inflate(inflater, container, false)
binding.viewModel = weatherViewModel
binding.lifecycleOwner = viewLifecycleOwner // ← obrigatório para LiveData
<layout>
<data>
<variable name="viewModel" type="com.example.app.WeatherViewModel" />
</data>
<LinearLayout ...>
<TextView
android:text="@string/temperature_format(viewModel.temperature)" />
<TextView android:text="@{viewModel.cityName}" />
<ImageView
android:src="@{viewModel.weatherIcon}"
contentDescription="@{viewModel.cityName}" />
</LinearLayout>
</layout>
Data Binding suporta StateFlow a partir do lifecycle 2.5.0 via Flow.asLiveData() ou conversão direta. Ao usar StateFlow no Data Binding, certifique-se de que o ciclo de vida esteja definido através de binding.lifecycleOwner. Sem definir o LifecycleOwner, o LiveData/StateFlow não atualizará a UI porque o Binding não sabe quando o assinante está ativo.
Uma tela de perfil completa com avatar, nome, biografia e um botão de edição. O ViewModel usa ObservableField para reatividade.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="profile" type="com.example.app.ProfileViewModel" />
</data>
<androidx.constraintlayout...>
<ImageView
app:imageUrl="@{profile.avatarUrl}"
android:contentDescription="@{profile.name}" />
<TextView
android:text="@{profile.name}"
android:textStyle="bold" />
<TextView
android:text="@{profile.bio}"
android:visibility="@{profile.hasBio ? View.VISIBLE : View.GONE}" />
<Button
android:onClick="@{() -> profile.onEdit()}"
android:text="@string/edit" />
</androidx.constraintlayout...>
</layout>
class ProfileViewModel : ViewModel() {
val name = ObservableField("Anna Petrova")
val bio = ObservableField("Desenvolvedor Android, 5 anos de experiência")
val avatarUrl = ObservableField("https://example.com/avatar.jpg")
val hasBio = ObservableBoolean(true)
fun onEdit() {
// Lógica de edição de perfil
}
}
// Em Fragment:
val binding = FragmentProfileBinding.inflate(inflater, container, false)
binding.profile = profileViewModel
binding.lifecycleOwner = viewLifecycleOwner
Um formulário de login com validação de campos e um botão de entrada. A vinculação bidirecional (@={}) sincroniza a entrada do usuário com o ViewModel.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="login" type="com.example.app.LoginViewModel" />
</data>
<LinearLayout ...>
<TextInputLayout>
<TextInputEditText
android:text="@{=login.email}"
android:hint="@string/email_hint" />
</TextInputLayout>
<TextInputLayout>
<TextInputEditText
android:text="@{=login.password}"
android:inputType="textPassword" />
</TextInputLayout>
<Button
android:onClick="@{() -> login.onLogin()}"
android:enabled="@{login.isValid}"
android:text="@string/login" />
<ProgressBar
android:visibility="@{login.isLoading ? View.VISIBLE : View.GONE}" />
</LinearLayout>
</layout>
Em XML, são usadas expressões: @{login.isValid} para o estado do botão (ativado/desativado), @{login.isLoading ? View.VISIBLE : View.GONE} para o indicador de carregamento, @{=login.email} para sincronização bidirecional. Toda a lógica de validação vive no ViewModel; a View apenas exibe o estado. De acordo com o Google (Android Guide, 2025), essa abordagem reduz o número de bugs na lógica de UI em 50–60%.
Perguntas Frequentes
Não, o Jetpack Compose é um sistema de UI independente com seu próprio mecanismo de reatividade (funções Composable + State). O Data Binding foi projetado exclusivamente para layouts XML e é incompatível com o Compose. Ao migrar de XML para Compose, o Data Binding não é usado — em vez disso, são aplicados mutableStateOf(), collectAsState() e remember. O Data Binding permanece relevante apenas para projetos que mantêm layouts XML.
No primeiro estágio de vinculação, o Data Binding realiza a busca de View por ID (como findViewById). A diferença é imperceptível para o usuário: uma tela típica com 20–30 Views é vinculada em 1–3 ms. A sobrecarga principal do Data Binding está em tempo de compilação (processamento de expressões). Em tempo de execução, não há diferença entre Data Binding e findViewById para a maioria das telas. Para RecyclerView com milhares de itens, o ViewBinding pode ser mais rápido devido a menos código gerado.
Data Binding compila expressões em código em tempo de compilação — os erros aparecem no Build Output como Compilation errors com a indicação da linha XML. Erros típicos: tipo de variável incorreto, problemas de segurança nula (use ?? para valores padrão), falta de importação de classe. Ative buildFeatures.dataBinding = true no build.gradle (app) e verifique se <layout> é a tag raiz do XML. Para depurar expressões em tempo de execução, use BindingConversion e registro no BindingAdapter.
@BindingConversion é uma anotação para métodos estáticos que convertem automaticamente tipos em expressões de Data Binding. Por exemplo, converter Color Int para ColorDrawable: @BindingConversion fun colorToDrawable(color: Int): ColorDrawable = ColorDrawable(color). Depois disso, android:background="@{color.red}" funcionará automaticamente. As BindingConversions são globais — são aplicadas a todas as expressões Binding do projeto.
Não, o Data Binding funciona em builds release da mesma forma que em debug. A otimização ProGuard/R8 pode remover classes Binding se não forem usadas diretamente — adicione a regra: -keep class * extends android.databinding.ViewDataBinding { *; }. A partir do Android Gradle Plugin 7.0, o R8 lida corretamente com Data Binding sem regras adicionais. Desativar o Data Binding para release não melhora o desempenho, mas quebra todas as telas que o utilizam.
Resumo
@{} e @={}.@={} — sincronização automática View ↔ ViewModel para formulários.lifecycleOwner.Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também