A linguagem de expressões permite escrever expressões que processam eventos enviados por visualizações. A Data Binding Library gera automaticamente as classes necessárias para vincular as visualizações do layout aos seus objetos de dados.
Os arquivos de layout de vinculação de dados são um pouco diferentes e começam com uma tag raiz de layout
, seguido por um elemento data
e um elemento raiz view
. Esta visualização é sua raiz em um arquivo de layout sem vinculação. O código a seguir mostra um exemplo de arquivo de layout:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.lastName}"/> </LinearLayout> </layout>
A variável user
em data
descreve uma propriedade que pode ser usada em este layout:
<variable name="user" type="com.example.User" />
As expressões no layout são escritas nas propriedades do atributo usando o Sintaxe @{}
. No exemplo a seguir, TextView
é definido como Propriedade firstName
da variável user
:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" />
Objetos de dados
Suponha que você tenha um objeto simples para descrever a entidade User
:
Kotlin
data class User(val firstName: String, val lastName: String)
Java
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
Esse tipo de objeto tem dados que nunca mudam. Em apps, é comum ter dados que são lidos uma vez e nunca mudam depois. Também é possível usar um objeto que segue um conjunto de convenções, como o uso de métodos de acesso em a linguagem de programação Java, conforme mostrado neste exemplo:
Kotlin
// Not applicable in Kotlin. data class User(val firstName: String, val lastName: String)
Java
public class User { private final String firstName; private final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } }
Da perspectiva da vinculação de dados, essas duas classes são equivalentes. A expressão @{user.firstName}
usada para o android:text
acessa o campo firstName
na classe anterior e o getFirstName()
na classe posterior. Também é resolvido para firstName()
, se esse método existir.
Vincular dados
Uma classe de vinculação é gerada para cada arquivo de layout. Por padrão, o nome do baseada no nome do arquivo de layout, convertido para o padrão Pascal, com o sufixo Binding adicionado a ele. Por exemplo, o nome de arquivo do layout anterior é activity_main.xml
, então a classe de vinculação gerada correspondente é ActivityMainBinding
.
Essa classe contém todas as vinculações das propriedades de layout, por exemplo, a variável user
às visualizações do layout e sabe como atribuir valores para as expressões de vinculação. Recomendamos criar as vinculações ao inflar no layout, como mostrado no exemplo a seguir:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main) binding.user = User("Test", "User") }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); binding.setUser(user); }
Durante a execução, o app mostra o usuário de Teste na interface. Também é possível acessar a visualização usando um LayoutInflater
, conforme mostrado no exemplo a seguir:
Kotlin
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
Java
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
Se você estiver usando itens de vinculação de dados em um Fragment
, ListView
ou RecyclerView
de carga, pode preferir usar o inflate()
os métodos das classes de vinculação ou da DataBindingUtil
, conforme como mostrado neste exemplo de código:
Kotlin
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false) // or val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
Java
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); // or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Linguagem de expressões
Recursos comuns
A linguagem de expressões é muito parecida com as expressões encontradas no código gerenciado. Você pode usar os seguintes operadores e palavras-chave na linguagem de expressões:
- Matemática:
+ - / * %
- Concatenação de strings:
+
- Lógico:
&& ||
- Binário:
& | ^
- Unário:
+ - ! ~
- Shift:
>> >>> <<
- Comparação:
== > < >= <=
(o<
precisa ter escape como<
) instanceof
- Agrupamento:
()
- Literais, como caractere, string, numérico,
null
- Transmitir
- Chamadas de método
- Acesso ao campo
- Acesso à matriz:
[]
- Operador ternário:
?:
Veja alguns exemplos:
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}'
Operações ausentes
As seguintes operações estão ausentes na sintaxe da expressão que pode ser usada no código gerenciado:
this
super
new
- Invocação genérica explícita
Operador de coalescência null
O operador de coalescência nulo (??
) escolhe o operando à esquerda se ele não for null
. ou à direita, se o primeiro for null
:
android:text="@{user.displayName ?? user.lastName}"
Isso é funcionalmente equivalente ao seguinte:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
Referências de propriedades
Uma expressão pode referenciar uma propriedade em uma classe usando o seguinte formato: que é o mesmo para campos, getters e ObservableField
objetos:
android:text="@{user.lastName}"
Evitar exceções de ponteiro nulo
O código de vinculação de dados gerado verifica automaticamente os valores null
e evita exceções de ponteiro nulo. Por exemplo, na expressão @{user.name}
, se user
é nulo, user.name
recebe o valor padrão de null
. Se você referência user.age
, em que a idade é do tipo int
, a vinculação de dados usa o o valor padrão de 0
.
Ver referências
Uma expressão pode referenciar outras visualizações no layout por ID usando o seguinte sintaxe:
android:text="@{exampleText.text}"
No exemplo a seguir, a visualização TextView
faz referência a uma visualização EditText
em o mesmo layout:
<EditText android:id="@+id/example_text" android:layout_height="wrap_content" android:layout_width="match_parent"/> <TextView android:id="@+id/example_output" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{exampleText.text}"/>
Seleções de produtos
É possível acessar coleções comuns, como matrizes, listas, listas esparsas e mapas, usando o operador []
por conveniência.
<data> <import type="android.util.SparseArray"/> <import type="java.util.Map"/> <import type="java.util.List"/> <variable name="list" type="List<String>"/> <variable name="sparse" type="SparseArray<String>"/> <variable name="map" type="Map<String, String>"/> <variable name="index" type="int"/> <variable name="key" type="String"/> </data> ... android:text="@{list[index]}" ... android:text="@{sparse[index]}" ... android:text="@{map[key]}"
Você também pode se referir a um valor no mapa usando a notação object.key
. Para exemplo, é possível substituir @{map[key]}
no exemplo anterior por @{map.key}
.
Literais de strings
Você pode usar aspas simples ao redor do valor do atributo, o que permite usar aspas duplas na expressão, conforme mostrado no exemplo a seguir:
android:text='@{map["firstName"]}'
Também é possível usar aspas duplas para cercar o valor do atributo. Ao fazer isso, Os literais de string precisam ser cercados por crases `
, conforme mostrado. aqui:
android:text="@{map[`firstName`]}"
Recursos
Uma expressão pode referenciar recursos do app com a seguinte sintaxe:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
As strings de formato e plurais podem ser avaliadas fornecendo parâmetros:
android:text="@{@string/nameFormat(firstName, lastName)}" android:text="@{@plurals/banana(bananaCount)}"
Você pode transmitir referências de propriedade e visualizar referências como parâmetros de recurso:
android:text="@{@string/example_resource(user.lastName, exampleText.text)}"
Quando um plural assume vários parâmetros, transmita todos os parâmetros:
Have an orange Have %d oranges android:text="@{@plurals/orange(orangeCount, orangeCount)}"
Alguns recursos exigem avaliação de tipo explícito, como mostrado abaixo tabela:
Tipo | Referência normal | Referência de expressão |
---|---|---|
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |
Manipular eventos
A vinculação de dados permite escrever expressões que manipulam eventos enviados da as visualizações (por exemplo, onClick()
. Os nomes dos atributos do evento são determinados pelo nome do método do listener. com algumas exceções. Por exemplo: View.OnClickListener
tem um método onClick()
, portanto, o atributo desse evento é android:onClick
.
Existem alguns manipuladores de eventos especializados para o evento de clique que precisam de uma atributo diferente de android:onClick
para evitar conflito. Você pode usar o atributos a seguir para evitar esse tipo de conflito:
Classe | Listener setter | Atributo |
---|---|---|
SearchView | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
ZoomControls | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
ZoomControls | setOnZoomOutClickListener(View.OnClickListener) | android:onZoomOut |
Você pode usar esses dois mecanismos, descritos em detalhes nas seções que a seguir para manipular um evento:
- Referências a métodos: nas expressões, é possível métodos de referência que estejam em conformidade com a assinatura do método listener. Quando uma expressão for avaliada como uma referência de método, a vinculação de dados une o método referencia e proprietário em um listener e define esse listener no visualização de destino. Se a expressão for avaliada como
null
, a vinculação de dados não criar um listener e, em vez disso, definir um listenernull
. - Vinculações de listener: são expressões lambda que são avaliadas quando o evento acontece. A vinculação de dados sempre cria uma que é definido na visualização. Quando o evento é enviado, o listener avalia a expressão lambda.
Referências a métodos
É possível vincular eventos diretamente a métodos do gerenciador, assim como você atribuir android:onClick
para um em uma atividade. Uma vantagem em relação à View
onClick
é que o é processada em tempo de compilação. Portanto, se o método não existir ou a assinatura está incorreta, você recebe um erro de tempo de compilação.
A principal diferença entre referências de método e vinculações de listener é que as implementação real do listener é criada quando os dados são vinculados, não quando é acionado. Se você preferir avaliar a expressão quando o evento caso aconteça, use vinculações de listener.
Para atribuir um evento a seu manipulador, use uma expressão de vinculação normal, com o sendo o nome do método a ser chamado. Por exemplo, considere o exemplo a seguir objeto de dados de layout:
Kotlin
class MyHandlers { fun onClickFriend(view: View) { ... } }
Java
public class MyHandlers { public void onClickFriend(View view) { ... } }
A expressão de vinculação pode atribuir o listener de clique de uma visualização ao onClickFriend()
, da seguinte maneira:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="com.example.MyHandlers"/> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" android:onClick="@{handlers::onClickFriend}"/> </LinearLayout> </layout>
Vinculações de listener
As vinculações de listener são expressões de vinculação executadas quando um evento acontece. Eles são semelhantes a referências de método, mas permitem executar vinculações arbitrárias de dados expressões idiomáticas. Este recurso está disponível com o Plug-in do Android para Gradle para Gradle versão 2.0 ou posterior.
Em referências de método, os parâmetros do método devem corresponder aos parâmetros do o listener de eventos. Nas vinculações de listener, somente o valor de retorno precisa corresponder ao valor de retorno esperado do listener, a menos que seja esperado void
. Para exemplo, considere a seguinte classe de apresentador que tem um onSaveClick()
:
Kotlin
class Presenter { fun onSaveClick(task: Task){} }
Java
public class Presenter { public void onSaveClick(Task task){} }
Vincule o evento de clique ao método onSaveClick()
da seguinte maneira:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="task" type="com.android.example.Task" /> <variable name="presenter" type="com.android.example.Presenter" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{() -> presenter.onSaveClick(task)}" /> </LinearLayout> </layout>
Quando um callback é usado em uma expressão, a vinculação de dados cria automaticamente a o listener necessário e o registra para o evento. Quando a visualização dispara o a vinculação de dados avalia a expressão especificada. Assim como na vinculação normal, expressões, você obtém o nulo e a segurança de encadeamento da vinculação de dados enquanto essas ou expressões de listener estão sendo avaliadas.
No exemplo anterior, o parâmetro view
que é transmitido para onClick(View)
não está definida. As vinculações de listener oferecem duas opções para parâmetros de listener: é possível ignorar todos os parâmetros do método ou nomear todos eles. Se preferir para nomear os parâmetros, é possível usá-los na expressão. Por exemplo, escrever a expressão anterior da seguinte forma:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
Se quiser usar o parâmetro na expressão, faça o seguinte:
Kotlin
class Presenter { fun onSaveClick(view: View, task: Task){} }
Java
public class Presenter { public void onSaveClick(View view, Task task){} }
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
E você pode usar uma expressão lambda com mais de um parâmetro:
Kotlin
class Presenter { fun onCompletedChanged(task: Task, completed: Boolean){} }
Java
public class Presenter { public void onCompletedChanged(Task task, boolean completed){} }
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
Se o evento que você está ouvindo retornar um valor de tipo diferente de void
, seu expressões devem retornar o mesmo tipo de valor também. Por exemplo, se quiser ouvir o toque e segura (clique longo), sua expressão deve retornar um booleano.
Kotlin
class Presenter { fun onLongClick(view: View, task: Task): Boolean { } }
Java
public class Presenter { public boolean onLongClick(View view, Task task) { } }
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
Se não for possível avaliar a expressão devido a objetos null
, a vinculação de dados retornará o valor padrão para esse tipo, como null
para tipos de referência, 0
para int
ou false
para boolean
.
Se você precisar usar uma expressão com um predicado, por exemplo, um ternário, é possível usar void
como símbolo:
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
Evitar listeners complexos
As expressões de listener são poderosas e podem tornar seu código mais fácil de ler. No(s) dia(s) Por outro lado, listeners que contêm expressões complexas tornam seus layouts mais difíceis de ler e manter. Manter as expressões tão simples quanto a transmissão de dados disponíveis da interface para o método de callback. Implemente qualquer lógica de negócios o método callback invocado a partir da expressão do listener.
Importações, variáveis e inclusões
A Data Binding Library fornece recursos como importações, variáveis e inclui. As importações facilitam a referência de classes dentro dos seus arquivos de layout. As variáveis permitem descrever uma propriedade que pode ser usada em expressões de vinculação. As inclusões permitem reutilizar layouts complexos no seu app.
Importações
As importações permitem referenciar classes dentro do seu arquivo de layout, como no código gerenciado. É possível usar zero ou mais elementos import
dentro do elemento data
. A O exemplo de código abaixo importa a classe View
para o arquivo de layout:
<data> <import type="android.view.View"/> </data>
Importar a classe View
permite referenciá-la usando suas expressões de vinculação. O exemplo a seguir mostra como referenciar o VISIBLE
e GONE
da classe View
:
<TextView android:text="@{user.lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
Aliases de tipos
Quando existem conflitos de nome de classe, você pode renomear uma das classes para uma alias. O exemplo a seguir renomeia a classe View
na Pacote com.example.real.estate
para Vista
:
<import type="android.view.View"/> <import type="com.example.real.estate.View" alias="Vista"/>
Você pode usar Vista
para referenciar com.example.real.estate.View
e View
para referenciar android.view.View
no arquivo de layout.
Importar outras classes
É possível usar tipos importados como referências de tipo em variáveis e expressões. A O exemplo a seguir mostra User
e List
usados como o tipo de uma variável:
<data> <import type="com.example.User"/> <import type="java.util.List"/> <variable name="user" type="User"/> <variable name="userList" type="List<User>"/> </data>
É possível usar os tipos importados para converter parte de uma expressão. O seguinte exemplo transmite a propriedade connection
para um tipo de User
:
<TextView android:text="@{((User)(user.connection)).lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
Você também pode usar tipos importados ao referenciar campos e métodos estáticos em expressões idiomáticas. O código a seguir importa a classe MyStringUtils
e as referências o método capitalize
:
<data> <import type="com.example.MyStringUtils"/> <variable name="user" type="com.example.User"/> </data> … <TextView android:text="@{MyStringUtils.capitalize(user.lastName)}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
Assim como no código gerenciado, java.lang.*
é importado automaticamente.
Variáveis
É possível usar vários elementos variable
dentro do elemento data
. Cada O elemento variable
descreve uma propriedade que pode ser definida no layout a ser usado em expressões de vinculação no arquivo de layout. O exemplo a seguir declara as variáveis user
, image
e note
:
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="user" type="com.example.User"/> <variable name="image" type="Drawable"/> <variable name="note" type="String"/> </data>
Os tipos de variáveis são inspecionados em tempo de compilação, portanto, se uma variável implementa Observable
ou é um coleção observável, que precisam ser refletidas no tipo. Se a variável for uma classe ou interface base que não implementa a interface Observable
, as variáveis não são observados.
Quando houver diferentes arquivos de layout para várias configurações (por exemplo, paisagem ou retrato), as variáveis são combinadas. Não deve haver definições de variáveis conflitantes entre esses arquivos de layout.
A classe de vinculação gerada tem um setter e um getter para cada um dos variáveis. As variáveis pegam os valores padrão do código gerenciado até que o setter é chamado: null
para tipos de referência, 0
para int
, false
para boolean
etc.
Uma variável especial chamada context
é gerada para uso em expressões de vinculação conforme necessário. O valor de context
é o objeto Context
da visualização raiz método getContext()
. A A variável context
é substituída por uma declaração de variável explícita com essa nome.
Inclui
Você pode passar variáveis para a vinculação de um layout incluído a partir do objeto usando o namespace do app e o nome da variável em um atributo. A o exemplo a seguir mostra variáveis user
incluídas de name.xml
e Arquivos de layout contact.xml
:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </LinearLayout> </layout>
A vinculação de dados não oferece suporte a uma inclusão como filho direto de um elemento de mesclagem. Por exemplo, o seguinte layout não é compatível:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <merge><!-- Doesn't work --> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </merge> </layout>
Outros recursos
Para saber mais sobre vinculação de dados, consulte os recursos a seguir.
Amostras
- Amostras da Android Data Binding Library (link em inglês)
Codelabs
Postagens do blog
- Vinculação de dados: lições aprendidas (link em inglês)