2015-05-26 16:52:23 -07:00
|
|
|
|
page.title=Data Binding Guide
|
2015-05-26 20:31:12 -07:00
|
|
|
|
page.tags="databinding", "layouts"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
@jd:body
|
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<div id="qv-wrapper">
|
|
|
|
|
<div id="qv">
|
|
|
|
|
<h2>
|
|
|
|
|
In this document:
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#build_environment">Build Environment</a>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#data_binding_layout_files">Data Binding Layout Files</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#writing_expressions">Writing your first data binding
|
2015-05-26 20:31:12 -07:00
|
|
|
|
expressions</a>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#data_object">Data Object</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#binding_data">Binding Data</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#binding_events">Binding Events</a>
|
|
|
|
|
</li>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#layout_details">Layout Details</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#imports">Imports</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#variables">Variables</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#custom_binding_class_names">Custom Binding Class Names</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#includes">Includes</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#expression_language">Expression Language</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#data_objects">Data Objects</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#observable_objects">Observable Objects</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#observablefields">ObservableFields</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#observable_collections">Observable Collections</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#generated_binding">Generated Binding</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#creating">Creating</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#views_with_ids">Views With IDs</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#variables">Variables</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#viewstubs">ViewStubs</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#advanced_binding">Advanced Binding</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#attribute_setters">Attribute Setters</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#automatic_setters">Automatic Setters</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#renamed_setters">Renamed Setters</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#custom_setters">Custom Setters</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<a href="#converters">Converters</a>
|
|
|
|
|
<ol>
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#object_conversions">Object Conversions</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-05-27 20:24:37 -07:00
|
|
|
|
<a href="#custom_conversions">Custom Conversions</a>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div><!-- qv -->
|
|
|
|
|
</div><!-- qv-wrapper -->
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
This document explains how to use the Data Binding Library to write
|
|
|
|
|
declarative layouts and minimize the glue code necessary to bind your
|
|
|
|
|
application logic and layouts.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-28 22:01:36 -07:00
|
|
|
|
<p>The Data Binding Library offers both flexibility and broad comnpatibility
|
|
|
|
|
— it's a support library, so you can use it with all Android platform
|
|
|
|
|
versions back to <strong>Android 2.1</strong> (API level 7+).</p>
|
|
|
|
|
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta4</strong>
|
2015-06-02 14:05:31 -07:00
|
|
|
|
or higher is required.</p>
|
2015-05-28 22:01:36 -07:00
|
|
|
|
|
|
|
|
|
<h4>Beta release</h4>
|
|
|
|
|
|
|
|
|
|
<div class="caution">
|
|
|
|
|
<p>Please note that the Data Binding library is a <strong>beta release</strong>.
|
|
|
|
|
While Data Binding is in beta, developers should be aware of the following
|
|
|
|
|
caveats:</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>
|
|
|
|
|
This is a beta release of the feature intended to generate developer
|
|
|
|
|
feedback. It might contain bugs, and it might not work for your use case,
|
|
|
|
|
so use it at your own risk. That said, we do want your feedback! Please
|
|
|
|
|
let us know what is or isn’t working for you using the <a
|
|
|
|
|
href="https://code.google.com/p/android-developer-preview/">issue
|
2015-06-02 14:05:31 -07:00
|
|
|
|
tracker</a>.
|
2015-05-28 22:01:36 -07:00
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
The Data Binding library beta release is subject to significant changes,
|
|
|
|
|
including those which are not source code compatible with your app. That is,
|
|
|
|
|
significant rework may be required to take updates to the library in the future.
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
Developers should feel free to publish apps built with the Data Binding
|
|
|
|
|
library beta release, with the caveats that the standard Android SDK and
|
|
|
|
|
Google Play terms of service apply, and it’s always a great idea to test your
|
|
|
|
|
app thoroughly when adopting new libraries or tools.
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
We’re just getting started with Android Studio support at this time.
|
|
|
|
|
Further Android Studio support will come in the future.
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
2015-06-02 14:05:31 -07:00
|
|
|
|
By using the Data Binding library beta release, you acknowledge these
|
2015-05-28 22:01:36 -07:00
|
|
|
|
caveats.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<h2 id="build_environment">
|
|
|
|
|
Build Environment
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<p>To get started with Data Binding, download the library from the Support
|
|
|
|
|
repository in the Android SDK manager. </p>
|
|
|
|
|
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta4
|
|
|
|
|
or higher</strong>, so update your build dependencies (in the top-level
|
|
|
|
|
<code>build.gradle</code> file) as needed.</p>
|
2015-05-28 22:01:36 -07:00
|
|
|
|
|
2015-06-02 14:05:31 -07:00
|
|
|
|
<p>Also, make sure you are using a compatible version of Android Studio.
|
|
|
|
|
<strong>Android Studio 1.3</strong> adds the code-completion and layout-preview
|
|
|
|
|
support for data binding.</p>
|
2015-05-28 22:01:36 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
<strong>Setting Up Work Environment:</strong>
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
To set up your application to use data binding, add data binding to the class
|
2015-07-17 16:47:32 -07:00
|
|
|
|
path of your top-level <code>build.gradle</code> file, right below "android".
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
dependencies {
|
2015-07-17 16:47:32 -07:00
|
|
|
|
classpath <strong>"com.android.tools.build:gradle:1.3.0-beta4"</strong>
|
|
|
|
|
classpath <strong>"com.android.databinding:dataBinder:1.0-rc1"</strong>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
Then make sure jcenter is in the repositories list for your projects in the top-level
|
|
|
|
|
<code>build.gradle</code> file.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
allprojects {
|
|
|
|
|
repositories {
|
|
|
|
|
jcenter()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
In each module you want to use data binding, apply the plugin right after
|
|
|
|
|
android plugin
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
apply plugin: 'com.android.application'
|
|
|
|
|
apply plugin: 'com.android.databinding'
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
The data binding plugin is going to add necessary <strong>provided</strong>
|
|
|
|
|
and <strong>compile configuration</strong> dependencies to your project.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="data_binding_layout_files">
|
|
|
|
|
Data Binding Layout Files
|
|
|
|
|
</h2>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="writing_expressions">
|
|
|
|
|
Writing your first data binding expressions
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Data-binding layout files are slightly different and start with a root tag of
|
|
|
|
|
<strong>layout</strong> followed by a <strong>data</strong> element and a
|
|
|
|
|
<strong>view</strong> root element. This view element is what your root would
|
|
|
|
|
be in a non-binding layout file. A sample file looks like this:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<?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
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:orientation="vertical"
|
|
|
|
|
android:layout_width="match_parent"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="match_parent">
|
|
|
|
|
<TextView android:layout_width="wrap_content"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{user.firstName}"/>
|
|
|
|
|
<TextView android:layout_width="wrap_content"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{user.lastName}"/>
|
|
|
|
|
</LinearLayout>
|
|
|
|
|
</layout>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
The user <strong>variable</strong> within <strong>data</strong> describes a
|
|
|
|
|
property that may be used within this layout.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
|
|
|
|
<<strong>variable name="user" type="com.example.User"</strong>/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Expressions within the layout are written in the attribute properties using
|
|
|
|
|
the “<code>@{}</code>” syntax. Here, the TextView’s text is set to the
|
|
|
|
|
firstName property of user:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView android:layout_width="wrap_content"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{user.firstName}"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="data_object">
|
|
|
|
|
Data Object
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Let’s assume for now that you have a plain-old Java object (POJO) for User:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public class User {
|
|
|
|
|
public final String firstName;
|
|
|
|
|
public final String lastName;
|
|
|
|
|
public User(String firstName, String lastName) {
|
|
|
|
|
this.firstName = firstName;
|
|
|
|
|
this.lastName = lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
This type of object has data that never changes. It is common in applications
|
|
|
|
|
to have data that is read once and never changes thereafter. It is also
|
|
|
|
|
possible to use a JavaBeans objects:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public class User {
|
|
|
|
|
private final String firstName;
|
|
|
|
|
private final String lastName;
|
|
|
|
|
public User(String firstName, String lastName) {
|
|
|
|
|
this.firstName = firstName;
|
|
|
|
|
this.lastName = lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public String getFirstName() {
|
|
|
|
|
return this.firstName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public String getLastName() {
|
|
|
|
|
return this.lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
From the perspective of data binding, these two classes are equivalent. The
|
|
|
|
|
expression <strong><code>@{user.firstName}</code></strong> used for
|
|
|
|
|
the TextView’s <strong><code>android:text</code></strong> attribute will
|
|
|
|
|
access the <strong><code>firstName</code></strong> field in the former class
|
2015-07-17 16:47:32 -07:00
|
|
|
|
and the <code>getFirstName()</code> method in the latter class. Alternatively, it
|
|
|
|
|
will also be resolved to <code>firstName()</code> if that method exists.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="binding_data">
|
|
|
|
|
Binding Data
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
By default, a Binding class will be generated based on the name of the layout
|
|
|
|
|
file, converting it to Pascal case and suffixing “Binding” to it. The above
|
2015-07-17 16:47:32 -07:00
|
|
|
|
layout file was <code>main_activity.xml</code> so the generate class was
|
|
|
|
|
<code>MainActivityBinding</code>. This class holds all the bindings from the
|
2015-05-26 20:31:12 -07:00
|
|
|
|
layout properties (e.g. the <code>user</code> variable) to the layout’s Views
|
|
|
|
|
and knows how to assign values for the binding expressions.The easiest means
|
|
|
|
|
for creating the bindings is to do it while inflating:
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</p>
|
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
@Override
|
2015-07-17 16:47:32 -07:00
|
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
|
|
|
|
|
User user = new User("Test", "User");
|
2015-05-26 16:52:23 -07:00
|
|
|
|
binding.setUser(user);
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
You’re done! Run the application and you’ll see Test User in the UI.
|
|
|
|
|
Alternatively, you can get the view via:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
MainActivityBinding binding = MainActivityBinding.<em>inflate</em>(getLayoutInflater());
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
If you are using data binding items inside a ListView or RecyclerView
|
|
|
|
|
adapter, you may prefer to use:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
//or
|
|
|
|
|
ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.layout.<em><strong>list_item</strong></em>, viewGroup, <strong>false</strong>);
|
|
|
|
|
</pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
|
|
|
|
|
<h3 id="binding_events">
|
|
|
|
|
Binding Events
|
|
|
|
|
</h3>
|
|
|
|
|
<p>
|
|
|
|
|
Events may be bound to handler methods directly, similar to the way
|
|
|
|
|
<strong><code>android:onClick</code></strong> can be assigned to a method in the Activity.
|
|
|
|
|
Event attribute names are governed by the name of the listener method with a few exceptions.
|
|
|
|
|
For example, {@link android.view.View.OnLongClickListener} has a method {@link android.view.View.OnLongClickListener#onLongClick onLongClick()},
|
|
|
|
|
so the attribute for this event is <code>android:onLongClick</code>.
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
To assign an event to its handler, use a normal binding expression, with the value
|
|
|
|
|
being the method name to call. For example, if your data object has two methods:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>public class MyHandlers {
|
|
|
|
|
public void onClickFriend(View view) { ... }
|
|
|
|
|
public void onClickEnemy(View view) { ... }
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
The binding expression can assign the click listener for a View:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
|
|
|
|
<data>
|
|
|
|
|
<variable name="handlers" type="com.example.Handlers"/>
|
|
|
|
|
<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="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
|
|
|
|
|
<TextView android:layout_width="wrap_content"
|
|
|
|
|
android:layout_height="wrap_content"
|
|
|
|
|
android:text="@{user.lastName}"
|
|
|
|
|
android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
|
|
|
|
|
</LinearLayout>
|
|
|
|
|
</layout>
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="layout_details">
|
|
|
|
|
Layout Details
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<h3 id="imports">
|
|
|
|
|
Imports
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Zero or more <strong><code>import</code></strong> elements may be used inside
|
|
|
|
|
the <strong><code>data</code></strong> element. These allow easy reference to
|
|
|
|
|
classes inside your layout file, just like in Java.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="android.view.View"/>
|
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Now, View may be used within your binding expression:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text="@{user.lastName}"
|
|
|
|
|
android:layout_width="wrap_content"
|
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When there are class name conflicts, one of the classes may be renamed to an
|
|
|
|
|
“alias:”
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<import type="android.view.View"/>
|
|
|
|
|
<import type="com.example.real.estate.View"
|
|
|
|
|
alias="Vista"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Now, <strong><code>Vista</code></strong> may be used to reference the
|
|
|
|
|
<code>com.example.real.estate.View</code> and
|
|
|
|
|
<strong><code>View</code></strong> may be used to reference
|
|
|
|
|
<code>android.view.View</code> within the layout file. Imported types may be
|
|
|
|
|
used as type references in variables and expressions:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="com.example.User"/>
|
|
|
|
|
<import type="java.util.List"/>
|
|
|
|
|
<variable name="user" type="User"/>
|
|
|
|
|
<variable name="userList" type="List&lt;User>"/>
|
|
|
|
|
</data>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</pre>
|
2015-05-28 22:01:36 -07:00
|
|
|
|
<p class="caution">
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<strong>Note</strong>: Android Studio does not yet handle imports so the
|
|
|
|
|
autocomplete for imported variables may not work in your IDE. Your
|
|
|
|
|
application will still compile fine and you can work around the IDE issue by
|
|
|
|
|
using fully qualified names in your variable definitions.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text="@{((User)(user.connection)).lastName}"
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Imported types may also be used when referencing static fields and methods in
|
|
|
|
|
expressions:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="com.example.MyStringUtils"/>
|
|
|
|
|
<variable name="user" type="com.example.User"/>
|
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
…
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text="@{MyStringUtils.capitalize(user.lastName)}"
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Just as in Java, <code>java.lang.*</code> is imported automatically.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="variables">
|
|
|
|
|
Variables
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Any number of <strong><code>variable</code></strong> elements may be used
|
|
|
|
|
inside the <strong><code>data</code></strong> element. Each
|
|
|
|
|
<strong><code>variable</code></strong> element describes a property that may
|
|
|
|
|
be set on the layout to be used in binding expressions within the layout
|
|
|
|
|
file.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<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>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
The variable types are inspected at compile time, so if a variable implements
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.databinding.Observable} or is an <a href=
|
2015-05-26 20:31:12 -07:00
|
|
|
|
"#observable_collections">observable collection</a>, that should be reflected
|
|
|
|
|
in the type. If the variable is a base class or interface that does not
|
|
|
|
|
implement the Observable* interface, the variables will <strong>not
|
|
|
|
|
be</strong> observed!
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When there are different layout files for various configurations (e.g.
|
|
|
|
|
landscape or portrait), the variables will be combined. There must not be
|
|
|
|
|
conflicting variable definitions between these layout files.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
The generated binding class will have a setter and getter for each of the
|
|
|
|
|
described variables. The variables will take the default Java values until
|
|
|
|
|
the setter is called — <code>null</code> for reference types,
|
|
|
|
|
<code>0</code> for <code>int</code>, <code>false</code> for
|
|
|
|
|
<code>boolean</code>, etc.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="custom_binding_class_names">
|
|
|
|
|
Custom Binding Class Names
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
By default, a Binding class is generated based on the name of the layout
|
|
|
|
|
file, starting it with upper-case, removing underscores ( _ ) and
|
|
|
|
|
capitalizing the following letter and then suffixing “Binding”. This class
|
|
|
|
|
will be placed in a databinding package under the module package. For
|
|
|
|
|
example, the layout file <code>contact_item.xml</code> will generate
|
|
|
|
|
<code>ContactItemBinding</code>. If the module package is
|
|
|
|
|
<code>com.example.my.app</code>, then it will be placed in
|
|
|
|
|
<code>com.example.my.app.databinding</code>.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Binding classes may be renamed or placed in different packages by adjusting
|
|
|
|
|
the <strong><code>class</code></strong> attribute of the
|
|
|
|
|
<strong><code>data</code></strong> element. For example:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data class="ContactItem">
|
2015-05-26 16:52:23 -07:00
|
|
|
|
...
|
2015-07-17 16:47:32 -07:00
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
This generates the binding class as <code>ContactItem</code> in the
|
|
|
|
|
databinding package in the module package. If the class should be generated
|
|
|
|
|
in a different package within the module package, it may be prefixed with
|
|
|
|
|
“.”:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data class=".ContactItem">
|
2015-05-26 16:52:23 -07:00
|
|
|
|
...
|
2015-07-17 16:47:32 -07:00
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
In this case, <code>ContactItem</code> is generated in the module package
|
|
|
|
|
directly. Any package may be used if the full package is provided:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data class="com.example.ContactItem">
|
2015-05-26 16:52:23 -07:00
|
|
|
|
...
|
2015-07-17 16:47:32 -07:00
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="includes">
|
|
|
|
|
Includes
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Variables may be passed into an included layout's binding from the
|
|
|
|
|
containing layout by using the application namespace and the variable name in
|
|
|
|
|
an attribute:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<?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
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:orientation="vertical"
|
|
|
|
|
android:layout_width="match_parent"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="match_parent">
|
|
|
|
|
<include layout="@layout/name"
|
|
|
|
|
bind:user="@{user}"/>
|
|
|
|
|
<include layout="@layout/contact"
|
|
|
|
|
bind:user="@{user}"/>
|
|
|
|
|
</LinearLayout>
|
|
|
|
|
</layout>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Here, there must be a <code>user</code> variable in both the
|
|
|
|
|
<code>name.xml</code> and <code>contact.xml</code> layout files.
|
|
|
|
|
</p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<p>
|
|
|
|
|
Data binding does not support include as a direct child of a merge element. For example,
|
|
|
|
|
<strong>the following layout is not supported:</strong>
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
<?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>
|
|
|
|
|
<include layout="@layout/name"
|
|
|
|
|
bind:user="@{user}"/>
|
|
|
|
|
<include layout="@layout/contact"
|
|
|
|
|
bind:user="@{user}"/>
|
|
|
|
|
</merge>
|
|
|
|
|
</layout>
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="expression_language">
|
|
|
|
|
Expression Language
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="common_features">
|
|
|
|
|
Common Features
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
The expression language looks a lot like a Java expression. These are the
|
|
|
|
|
same:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
|
|
|
|
<ul>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<li>Mathematical <strong><code>+ - / * %</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>String concatenation <strong><code>+</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
Logical <strong><code>&& ||</code></strong>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<li>Binary <strong><code>& | ^</code></strong>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Unary <strong><code>+ - ! ~</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Shift <strong><code>>> >>> <<</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Comparison <strong><code>== > < >= <=</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<strong><code>instanceof</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Grouping <strong><code>()</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Literals - character, String, numeric, <strong><code>null</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Cast
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Method calls
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Field access
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Array access <strong><code>[]</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Ternary operator <strong><code>?:</code></strong>
|
|
|
|
|
</li>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</ul>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Examples:
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{String.valueOf(index + 1)}"
|
2015-05-26 20:31:12 -07:00
|
|
|
|
android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:transitionName='@{"image_" + id}'
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="missing_operations">
|
|
|
|
|
Missing Operations
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
A few operations are missing from the expression syntax that you can use in
|
|
|
|
|
Java.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
|
|
|
|
<ul>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<li>
|
|
|
|
|
<strong><code>this</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<strong><code>super</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>
|
|
|
|
|
<strong><code>new</code></strong>
|
|
|
|
|
</li>
|
|
|
|
|
|
|
|
|
|
<li>Explicit generic invocation
|
|
|
|
|
</li>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</ul>
|
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="null_coalescing_operator">
|
|
|
|
|
Null Coalescing Operator
|
|
|
|
|
</h4>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
The null coalescing operator (<strong><code>??</code></strong>) chooses the
|
|
|
|
|
left operand if it is not null or the right if it is null.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<strong>android:text="@{user.displayName ?? user.lastName}"</strong>
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
This is functionally equivalent to:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<strong>android:text="@{user.displayName != null ? user.displayName : user.lastName}"</strong>
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="property_reference">
|
|
|
|
|
Property Reference
|
|
|
|
|
</h4>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
The first was already discussed in the <a href=
|
|
|
|
|
"#writing_your_first_data_binding_expressions">Writing your first data
|
|
|
|
|
binding expressions</a> above: short form JavaBean references. When an
|
|
|
|
|
expression references a property on a class, it uses the same format for
|
|
|
|
|
fields, getters, and ObservableFields.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<strong>android:text="@{user.lastName}"</strong>
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4>
|
|
|
|
|
Avoiding NullPointerException
|
|
|
|
|
</h4>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Generated data binding code automatically checks for nulls and avoid null
|
|
|
|
|
pointer exceptions. For example, in the expression
|
|
|
|
|
<code>@{user.name}</code>, if <code>user</code> is null,
|
|
|
|
|
<code>user.name</code> will be assigned its default value (null). If you were
|
|
|
|
|
referencing <code>user.age</code>, where age is an <code>int</code>, then it
|
|
|
|
|
would default to 0.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="collections">
|
|
|
|
|
Collections
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Common collections: arrays, lists, sparse lists, and maps, may be accessed
|
|
|
|
|
using the <code>[]</code> operator for convenience.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="android.util.SparseArray"/>
|
|
|
|
|
<import type="java.util.Map"/>
|
|
|
|
|
<import type="java.util.List"/>
|
|
|
|
|
<variable name="list" type="List&lt;String>"/>
|
|
|
|
|
<variable name="sparse" type="SparseArray&lt;String>"/>
|
|
|
|
|
<variable name="map" type="Map&lt;String, String>"/>
|
|
|
|
|
<variable name="index" type="int"/>
|
|
|
|
|
<variable name="key" type="String"/>
|
|
|
|
|
</data>
|
|
|
|
|
…
|
|
|
|
|
android:text="@{list[index]}"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
…
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{sparse[index]}"
|
|
|
|
|
…
|
|
|
|
|
android:text="@{map[key]}"
|
|
|
|
|
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="string_literals">
|
|
|
|
|
String Literals
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When using single quotes around the attribute value, it is easy to use double
|
|
|
|
|
quotes in the expression:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text='@{map["firstName"]}'
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
It is also possible to use double quotes to surround the attribute value.
|
|
|
|
|
When doing so, String literals should either use the &quot; or back quote
|
|
|
|
|
(`).
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{map[`firstName`}"
|
|
|
|
|
android:text="@{map[&quot;firstName&quot;]}"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="resources">
|
|
|
|
|
Resources
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
It is possible to access resources as part of expressions using the normal
|
|
|
|
|
syntax:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Format strings and plurals may be evaluated by providing parameters:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{@string/nameFormat(firstName, lastName)}"
|
|
|
|
|
android:text="@{@plurals/banana(bananaCount)}"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When a plural takes multiple parameters, all parameters should be passed:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
|
|
|
|
|
|
|
|
|
Have an orange
|
|
|
|
|
Have %d oranges
|
|
|
|
|
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
Some resources require explicit type evaluation.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
|
|
|
|
<table>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<tr>
|
|
|
|
|
<th>
|
|
|
|
|
Type
|
|
|
|
|
</th>
|
|
|
|
|
<th>
|
|
|
|
|
Normal Reference
|
|
|
|
|
</th>
|
|
|
|
|
<th>
|
|
|
|
|
Expression Reference
|
|
|
|
|
</th>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
String[]
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@array
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@stringArray
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
int[]
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@array
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@intArray
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
TypedArray
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@array
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@typedArray
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
Animator
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@animator
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@animator
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
StateListAnimator
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@animator
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@stateListAnimator
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<tr>
|
|
|
|
|
<td>
|
|
|
|
|
color <code>int</code>
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
@color
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@color
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
ColorStateList
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@color
|
|
|
|
|
</td>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<td>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
@colorStateList
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</table>
|
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="data_objects">
|
|
|
|
|
Data Objects
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Any plain old Java object (POJO) may be used for data binding, but modifying
|
|
|
|
|
a POJO will not cause the UI to update. The real power of data binding can be
|
|
|
|
|
used by giving your data objects the ability to notify when data changes.
|
|
|
|
|
There are three different data change notification mechanisms,
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<a href="#observable_objects">Observable objects</a>,
|
|
|
|
|
<a href="#observablefields">observable fields</a>, and
|
|
|
|
|
<a href="#observable_collections">observable collection</a>s.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When one of these observable data object is bound to the UI and a property of
|
|
|
|
|
the data object changes, the UI will be updated automatically.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="observable_objects">
|
|
|
|
|
Observable Objects
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
A class implementing the {@link android.databinding.Observable} interface
|
2015-05-26 20:31:12 -07:00
|
|
|
|
will allow the binding to attach a single listener to a bound object to
|
|
|
|
|
listen for changes of all properties on that object.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
The {@link android.databinding.Observable} interface has a mechanism to add and remove
|
2015-05-26 20:31:12 -07:00
|
|
|
|
listeners, but notifying is up to the developer. To make development easier,
|
2015-07-17 16:47:32 -07:00
|
|
|
|
a base class, {@link android.databinding.BaseObservable}, was created to implement the
|
2015-05-26 20:31:12 -07:00
|
|
|
|
listener registration mechanism. The data class implementer is still
|
|
|
|
|
responsible for notifying when the properties change. This is done by
|
2015-07-17 16:47:32 -07:00
|
|
|
|
assigning a {@link android.databinding.Bindable} annotation to the getter and notifying in
|
2015-05-26 20:31:12 -07:00
|
|
|
|
the setter.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
private static class User extends BaseObservable {
|
|
|
|
|
private String firstName;
|
|
|
|
|
private String lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
@Bindable
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public String getFirstName() {
|
|
|
|
|
return this.firstName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
@Bindable
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public String getLastName() {
|
|
|
|
|
return this.lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public void setFirstName(String firstName) {
|
|
|
|
|
this.firstName = firstName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
notifyPropertyChanged(BR.firstName);
|
|
|
|
|
}
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public void setLastName(String lastName) {
|
|
|
|
|
this.lastName = lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
notifyPropertyChanged(BR.lastName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
The {@link android.databinding.Bindable} annotation generates an entry in the BR class file
|
2015-05-26 20:31:12 -07:00
|
|
|
|
during compilation. The BR class file will be generated in the module
|
2015-07-17 16:47:32 -07:00
|
|
|
|
package. If the base class for data classes cannot be changed, the
|
|
|
|
|
{@link android.databinding.Observable} interface may be implemented using the convenient
|
|
|
|
|
{@link android.databinding.PropertyChangeRegistry} to store and notify listeners
|
2015-05-26 20:31:12 -07:00
|
|
|
|
efficiently.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="observablefields">
|
|
|
|
|
ObservableFields
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
A little work is involved in creating {@link android.databinding.Observable} classes, so
|
|
|
|
|
developers who want to save time or have few properties may use
|
|
|
|
|
{@link android.databinding.ObservableField} and its siblings
|
|
|
|
|
{@link android.databinding.ObservableBoolean},
|
|
|
|
|
{@link android.databinding.ObservableByte},
|
|
|
|
|
{@link android.databinding.ObservableChar},
|
|
|
|
|
{@link android.databinding.ObservableShort},
|
|
|
|
|
{@link android.databinding.ObservableInt},
|
|
|
|
|
{@link android.databinding.ObservableLong},
|
|
|
|
|
{@link android.databinding.ObservableFloat},
|
|
|
|
|
{@link android.databinding.ObservableDouble}, and
|
|
|
|
|
{@link android.databinding.ObservableParcelable}.
|
|
|
|
|
<code>ObservableFields</code> are self-contained observable objects that have a single
|
|
|
|
|
field. The primitive versions avoid boxing and unboxing during access operations.
|
|
|
|
|
To use, create a public final field in the data class:
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
private static class User {
|
|
|
|
|
public final ObservableField<String> firstName =
|
|
|
|
|
new ObservableField<>();
|
|
|
|
|
public final ObservableField<String> lastName =
|
|
|
|
|
new ObservableField<>();
|
|
|
|
|
public final ObservableInt age = new ObservableInt();
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
That's it! To access the value, use the set and get accessor methods:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
user.firstName.set("Google");
|
|
|
|
|
int age = user.age.get();
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="observable_collections">
|
|
|
|
|
Observable Collections
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Some applications use more dynamic structures to hold data. Observable
|
2015-07-17 16:47:32 -07:00
|
|
|
|
collections allow keyed access to these data objects.
|
|
|
|
|
{@link android.databinding.ObservableArrayMap} is
|
2015-05-26 20:31:12 -07:00
|
|
|
|
useful when the key is a reference type, such as String.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
|
|
|
|
|
user.put("firstName", "Google");
|
|
|
|
|
user.put("lastName", "Inc.");
|
|
|
|
|
user.put("age", 17);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
In the layout, the map may be accessed through the String keys:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="android.databinding.ObservableMap"/>
|
|
|
|
|
<variable name="user" type="ObservableMap&lt;String, Object>"/>
|
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
…
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text='@{user["lastName"]}'
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.databinding.ObservableArrayList} is useful when the key is an integer:
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
ObservableArrayList<Object> user = new ObservableArrayList<>();
|
|
|
|
|
user.add("Google");
|
|
|
|
|
user.add("Inc.");
|
2015-05-26 16:52:23 -07:00
|
|
|
|
user.add(17);
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
In the layout, the list may be accessed through the indices:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<data>
|
|
|
|
|
<import type="android.databinding.ObservableList"/>
|
|
|
|
|
<import type="com.example.my.app.Fields"/>
|
|
|
|
|
<variable name="user" type="ObservableList&lt;Object>"/>
|
|
|
|
|
</data>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
…
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text='@{user[Fields.LAST_NAME]}'
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="generated_binding">
|
|
|
|
|
Generated Binding
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
The generated binding class links the layout variables with the Views within
|
|
|
|
|
the layout. As discussed earlier, the name and package of the Binding may be
|
|
|
|
|
<a href="#custom_binding_class_names">customized</a>. The Generated binding
|
2015-07-17 16:47:32 -07:00
|
|
|
|
classes all extend {@link android.databinding.ViewDataBinding}.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="creating">
|
|
|
|
|
Creating
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
The binding should be created soon after inflation to ensure that the View
|
|
|
|
|
hierarchy is not disturbed prior to binding to the Views with expressions
|
|
|
|
|
within the layout. There are a few ways to bind to a layout. The most common
|
|
|
|
|
is to use the static methods on the Binding class.The inflate method inflates
|
|
|
|
|
the View hierarchy and binds to it all it one step. There is a simpler
|
2015-07-17 16:47:32 -07:00
|
|
|
|
version that only takes a {@link android.view.LayoutInflater} and one that takes a
|
|
|
|
|
{@link android.view.ViewGroup} as well:
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
|
|
|
|
|
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
If the layout was inflated using a different mechanism, it may be bound
|
|
|
|
|
separately:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Sometimes the binding cannot be known in advance. In such cases, the binding
|
2015-07-17 16:47:32 -07:00
|
|
|
|
can be created using the {@link android.databinding.DataBindingUtil} class:
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
|
2015-05-26 16:52:23 -07:00
|
|
|
|
parent, attachToParent);
|
2015-07-17 16:47:32 -07:00
|
|
|
|
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="views_with_ids">
|
|
|
|
|
Views With IDs
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
A public final field will be generated for each View with an ID in the
|
|
|
|
|
layout. The binding does a single pass on the View hierarchy, extracting the
|
|
|
|
|
Views with IDs. This mechanism can be faster than calling findViewById for
|
|
|
|
|
several Views. For example:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
|
|
|
|
<data>
|
|
|
|
|
<variable name="user" type="com.example.User"/>
|
|
|
|
|
</data>
|
|
|
|
|
<LinearLayout
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:orientation="vertical"
|
|
|
|
|
android:layout_width="match_parent"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="match_parent">
|
|
|
|
|
<TextView android:layout_width="wrap_content"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{user.firstName}"
|
|
|
|
|
android:id="@+id/firstName"/>
|
|
|
|
|
<TextView android:layout_width="wrap_content"
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:layout_height="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:text="@{user.lastName}"
|
|
|
|
|
android:id="@+id/lastName"/>
|
|
|
|
|
</LinearLayout>
|
|
|
|
|
</layout>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Will generate a binding class with:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public final TextView firstName;
|
|
|
|
|
public final TextView lastName;
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
IDs are not nearly as necessary as without data binding, but there are still
|
|
|
|
|
some instances where access to Views are still necessary from code.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h3 id="variables2">
|
|
|
|
|
Variables
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Each variable will be given accessor methods.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<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>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
will generate setters and getters in the binding:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public abstract com.example.User getUser();
|
|
|
|
|
public abstract void setUser(com.example.User user);
|
|
|
|
|
public abstract Drawable getImage();
|
|
|
|
|
public abstract void setImage(Drawable image);
|
|
|
|
|
public abstract String getNote();
|
|
|
|
|
public abstract void setNote(String note);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="viewstubs">
|
|
|
|
|
ViewStubs
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.view.ViewStub}s are a little different from normal Views. They start off invisible
|
2015-05-26 20:31:12 -07:00
|
|
|
|
and when they either are made visible or are explicitly told to inflate, they
|
|
|
|
|
replace themselves in the layout by inflating another layout.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
Because the <code>ViewStub</code> essentially disappears from the View hierarchy, the View
|
2015-05-26 20:31:12 -07:00
|
|
|
|
in the binding object must also disappear to allow collection. Because the
|
2015-07-17 16:47:32 -07:00
|
|
|
|
Views are final, a {@link android.databinding.ViewStubProxy} object takes the place of the
|
|
|
|
|
<code>ViewStub</code>, giving the developer access to the ViewStub when it exists and also
|
|
|
|
|
access to the inflated View hierarchy when the <code>ViewStub</code> has been inflated.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When inflating another layout, a binding must be established for the new
|
2015-07-17 16:47:32 -07:00
|
|
|
|
layout. Therefore, the <code>ViewStubProxy</code> must listen to the <code>ViewStub</code>'s
|
|
|
|
|
{@link android.view.ViewStub.OnInflateListener} and establish the binding at that time. Since
|
|
|
|
|
only one can exist, the <code>ViewStubProxy</code> allows the developer to set an
|
|
|
|
|
<code>OnInflateListener</code> on it that it will call after establishing the binding.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="advanced_binding">
|
|
|
|
|
Advanced Binding
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="dynamic_variables">
|
|
|
|
|
Dynamic Variables
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
At times, the specific binding class won't be known. For example, a
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.support.v7.widget.RecyclerView.Adapter} operating against arbitrary layouts
|
|
|
|
|
won't know the specific binding class. It still must assign the binding value during the
|
|
|
|
|
{@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
In this example, all layouts that the RecyclerView binds to have an "item"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
variable. The <code>BindingHolder</code> has a <code>getBinding</code> method returning the
|
|
|
|
|
{@link android.databinding.ViewDataBinding} base.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public void onBindViewHolder(BindingHolder holder, int position) {
|
|
|
|
|
final T item = mItems.get(position);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
holder.getBinding().setVariable(BR.item, item);
|
|
|
|
|
holder.getBinding().executePendingBindings();
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4 id="immediate_binding">
|
|
|
|
|
Immediate Binding
|
|
|
|
|
</h4>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
When a variable or observable changes, the binding will be scheduled to
|
|
|
|
|
change before the next frame. There are times, however, when binding must be
|
|
|
|
|
executed immediately. To force execution, use the
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.databinding.ViewDataBinding#executePendingBindings()} method.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h4>
|
|
|
|
|
Background Thread
|
|
|
|
|
</h4>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
You can change your data model in a background thread as long as it is not a
|
|
|
|
|
collection. Data binding will localize each variable / field while evaluating
|
|
|
|
|
to avoid any concurrency issues.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="attribute_setters">
|
|
|
|
|
Attribute Setters
|
|
|
|
|
</h2>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Whenever a bound value changes, the generated binding class must call a
|
|
|
|
|
setter method on the View with the binding expression. The data binding
|
|
|
|
|
framework has ways to customize which method to call to set the value.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="automatic_setters">
|
|
|
|
|
Automatic Setters
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
For an attribute, data binding tries to find the method setAttribute. The
|
|
|
|
|
namespace for the attribute does not matter, only the attribute name itself.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
For example, an expression associated with TextView's attribute
|
|
|
|
|
<strong><code>android:text</code></strong> will look for a setText(String).
|
|
|
|
|
If the expression returns an int, data binding will search for a setText(int)
|
|
|
|
|
method. Be careful to have the expression return the correct type, casting if
|
|
|
|
|
necessary. Note that data binding will work even if no attribute exists with
|
|
|
|
|
the given name. You can then easily "create" attributes for any setter by
|
|
|
|
|
using data binding. For example, support DrawerLayout doesn't have any
|
|
|
|
|
attributes, but plenty of setters. You can use the automatic setters to use
|
|
|
|
|
one of these.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
<android.support.v4.widget.<strong>DrawerLayout
|
|
|
|
|
android:layout_width="wrap_content"
|
|
|
|
|
android:layout_height="wrap_content"
|
|
|
|
|
app:scrimColor="@{@color/scrim}"
|
2015-05-26 20:31:12 -07:00
|
|
|
|
app:drawerListener="@{fragment.drawerListener}"/></strong>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="renamed_setters">
|
|
|
|
|
Renamed Setters
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Some attributes have setters that don't match by name. For these
|
|
|
|
|
methods, an attribute may be associated with the setter through
|
2015-07-17 16:47:32 -07:00
|
|
|
|
{@link android.databinding.BindingMethods} annotation. This must be associated with
|
|
|
|
|
a class and contains {@link android.databinding.BindingMethod} annotations, one for
|
|
|
|
|
each renamed method. For example, the <strong><code>android:tint</code></strong> attribute
|
|
|
|
|
is really associated with {@link android.widget.ImageView#setImageTintList}, not
|
|
|
|
|
<code>setTint</code>.
|
2015-05-26 20:31:12 -07:00
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
@BindingMethods({
|
2015-07-17 16:47:32 -07:00
|
|
|
|
@BindingMethod(type = "android.widget.ImageView",
|
|
|
|
|
attribute = "android:tint",
|
|
|
|
|
method = "setImageTintList"),
|
2015-05-26 16:52:23 -07:00
|
|
|
|
})
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
It is unlikely that developers will need to rename setters; the android
|
|
|
|
|
framework attributes have already been implemented.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="custom_setters">
|
|
|
|
|
Custom Setters
|
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
Some attributes need custom binding logic. For example, there is no
|
|
|
|
|
associated setter for the <strong><code>android:paddingLeft</code></strong>
|
|
|
|
|
attribute. Instead, <code>setPadding(left, top, right, bottom)</code> exists.
|
2015-07-17 16:47:32 -07:00
|
|
|
|
A static binding adapter method with the {@link android.databinding.BindingAdapter}
|
2015-05-26 20:31:12 -07:00
|
|
|
|
annotation allows the developer to customize how a setter for an attribute is
|
|
|
|
|
called.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
The android attributes have already had <code>BindingAdapter</code>s created.
|
|
|
|
|
For example, here is the one for <code>paddingLeft</code>:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
@BindingAdapter("android:paddingLeft")
|
|
|
|
|
public static void setPaddingLeft(View view, int padding) {
|
2015-05-26 16:52:23 -07:00
|
|
|
|
view.setPadding(padding,
|
|
|
|
|
view.getPaddingTop(),
|
|
|
|
|
view.getPaddingRight(),
|
|
|
|
|
view.getPaddingBottom());
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Binding adapters are useful for other types of customization. For example, a
|
|
|
|
|
custom loader can be called off-thread to load an image.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Developer-created binding adapters will override the data binding default
|
|
|
|
|
adapters when there is a conflict.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
You can also have adapters that receive multiple parameters.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
@BindingAdapter({"bind:imageUrl", "bind:error"})
|
|
|
|
|
public static void loadImage(ImageView view, String url, Drawable error) {
|
|
|
|
|
Picasso.with(view.getContext()).load(url).error(error).into(view);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
|
|
|
|
<ImageView app:imageUrl=“@{venue.imageUrl}”
|
|
|
|
|
app:error=“@{@drawable/venueError}”/>
|
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
This adapter will be called if both <strong>imageUrl</strong> and
|
|
|
|
|
<strong>error</strong> are used for an ImageView and <em>imageUrl</em> is a
|
|
|
|
|
string and <em>error</em> is a drawable.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
|
|
|
|
<ul>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<li>Custom namespaces are ignored during matching.
|
|
|
|
|
</li>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<li>You can also write adapters for android namespace.
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<p>
|
|
|
|
|
Binding adapter methods may optionally take the old values in their handlers. A method
|
|
|
|
|
taking old and new values should have all old values for the attributes come first, followed
|
|
|
|
|
by the new values:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
@BindingAdapter("android:paddingLeft")
|
|
|
|
|
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
|
|
|
|
|
if (oldPadding != newPadding) {
|
|
|
|
|
view.setPadding(newPadding,
|
|
|
|
|
view.getPaddingTop(),
|
|
|
|
|
view.getPaddingRight(),
|
|
|
|
|
view.getPaddingBottom());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
Event handlers may only be used with interfaces or abstract classes with one abstract method.
|
|
|
|
|
For example:
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
@BindingAdapter("android:onLayoutChange")
|
|
|
|
|
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
|
|
|
|
|
View.OnLayoutChangeListener newValue) {
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
|
|
|
|
if (oldValue != null) {
|
|
|
|
|
view.removeOnLayoutChangeListener(oldValue);
|
|
|
|
|
}
|
|
|
|
|
if (newValue != null) {
|
|
|
|
|
view.addOnLayoutChangeListener(newValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
When a listener has multiple methods, it must be split into multiple listeners. For example,
|
|
|
|
|
{@link android.view.View.OnAttachStateChangeListener} has two methods:
|
|
|
|
|
{@link android.view.View.OnAttachStateChangeListener#onViewAttachedToWindow onViewAttachedToWindow()} and
|
|
|
|
|
{@link android.view.View.OnAttachStateChangeListener#onViewDetachedFromWindow onViewDetachedFromWindow()}.
|
|
|
|
|
We must then create two interfaces to differentiate the attributes and handlers for them.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
|
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
|
|
|
|
|
public interface OnViewDetachedFromWindow {
|
|
|
|
|
void onViewDetachedFromWindow(View v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
|
|
|
|
|
public interface OnViewAttachedToWindow {
|
|
|
|
|
void onViewAttachedToWindow(View v);
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
Because changing one listener will also affect the other, we must have three different
|
|
|
|
|
binding adapters, one for each attribute and one for both, should they both be set.
|
|
|
|
|
</p>
|
|
|
|
|
<pre>
|
|
|
|
|
@BindingAdapter("android:onViewAttachedToWindow")
|
|
|
|
|
public static void setListener(View view, OnViewAttachedToWindow attached) {
|
|
|
|
|
setListener(view, null, attached);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@BindingAdapter("android:onViewDetachedFromWindow")
|
|
|
|
|
public static void setListener(View view, OnViewDetachedFromWindow detached) {
|
|
|
|
|
setListener(view, detached, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
|
|
|
|
|
public static void setListener(View view, final OnViewDetachedFromWindow detach,
|
|
|
|
|
final OnViewAttachedToWindow attach) {
|
|
|
|
|
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
|
|
|
|
|
final OnAttachStateChangeListener newListener;
|
|
|
|
|
if (detach == null && attach == null) {
|
|
|
|
|
newListener = null;
|
|
|
|
|
} else {
|
|
|
|
|
newListener = new OnAttachStateChangeListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onViewAttachedToWindow(View v) {
|
|
|
|
|
if (attach != null) {
|
|
|
|
|
attach.onViewAttachedToWindow(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onViewDetachedFromWindow(View v) {
|
|
|
|
|
if (detach != null) {
|
|
|
|
|
detach.onViewDetachedFromWindow(v);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
|
|
|
|
|
newListener, R.id.onAttachStateChangeListener);
|
|
|
|
|
if (oldListener != null) {
|
|
|
|
|
view.removeOnAttachStateChangeListener(oldListener);
|
|
|
|
|
}
|
|
|
|
|
if (newListener != null) {
|
|
|
|
|
view.addOnAttachStateChangeListener(newListener);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</pre>
|
|
|
|
|
<p>
|
|
|
|
|
The above example is slightly more complicated than normal because View uses add and remove
|
|
|
|
|
for the listener instead of a set method for {@link android.view.View.OnAttachStateChangeListener}.
|
|
|
|
|
The <code>android.databinding.adapters.ListenerUtil</code> class helps keep track of the previous
|
|
|
|
|
listeners so that they may be removed in the Binding Adaper.
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
By annotating the interfaces <code>OnViewDetachedFromWindow</code> and
|
|
|
|
|
<code>OnViewAttachedToWindow</code> with
|
|
|
|
|
<code>@TargetApi(VERSION_CODES.HONEYCOMB_MR1)</code>, the data binding code
|
|
|
|
|
generator knows that the listener should only be generated when running on Honeycomb MR1
|
|
|
|
|
and new devices, the same version supported by
|
|
|
|
|
{@link android.view.View#addOnAttachStateChangeListener}.
|
|
|
|
|
</p>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h2 id="converters">
|
|
|
|
|
Converters
|
|
|
|
|
</h2>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<h3 id="object_conversions">
|
|
|
|
|
Object Conversions
|
|
|
|
|
</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
When an Object is returned from a binding expression, a setter will be chosen
|
|
|
|
|
from the automatic, renamed, and custom setters. The Object will be cast to a
|
|
|
|
|
parameter type of the chosen setter.
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
This is a convenience for those using ObservableMaps to hold data. for
|
|
|
|
|
example:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<TextView
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:text='@{userMap["lastName"]}'
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
The <code>userMap</code> returns an Object and that Object will be automatically cast to
|
|
|
|
|
parameter type found in the setter <code>setText(CharSequence)</code>. When there
|
|
|
|
|
may be confusion about the parameter type, the developer will need
|
|
|
|
|
to cast in the expression.
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<h3 id="custom_conversions">Custom Conversions</h3>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Sometimes conversions should be automatic between specific types. For
|
|
|
|
|
example, when setting the background:
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<View
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:background="@{isError ? @color/red : @color/white}"
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Here, the background takes a <code>Drawable</code>, but the color is an
|
|
|
|
|
integer. Whenever a <code>Drawable</code> is expected and an integer is
|
|
|
|
|
returned, the <code>int</code> should be converted to a
|
|
|
|
|
<code>ColorDrawable</code>. This conversion is done using a static method
|
|
|
|
|
with a BindingConversion annotation:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
@BindingConversion
|
2015-07-17 16:47:32 -07:00
|
|
|
|
public static ColorDrawable convertColorToDrawable(int color) {
|
|
|
|
|
return new ColorDrawable(color);
|
2015-05-26 16:52:23 -07:00
|
|
|
|
}
|
|
|
|
|
</pre>
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<p>
|
|
|
|
|
Note that conversions only happen at the setter level, so it is <strong>not
|
|
|
|
|
allowed</strong> to mix types like this:
|
|
|
|
|
</p>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
|
2015-05-26 20:31:12 -07:00
|
|
|
|
<pre>
|
2015-07-17 16:47:32 -07:00
|
|
|
|
<View
|
2015-05-26 16:52:23 -07:00
|
|
|
|
android:background="@{isError ? @drawable/error : @color/white}"
|
|
|
|
|
android:layout_width="wrap_content"
|
2015-07-17 16:47:32 -07:00
|
|
|
|
android:layout_height="wrap_content"/>
|
2015-05-26 16:52:23 -07:00
|
|
|
|
</pre>
|