diff options
author | Dirk Dougherty <ddougherty@google.com> | 2015-05-26 16:52:23 -0700 |
---|---|---|
committer | Dirk Dougherty <ddougherty@google.com> | 2015-05-26 16:52:23 -0700 |
commit | 493ff3cb8f0c6f7729763f7d4a8571c0955d6f4b (patch) | |
tree | 9f23b391fb98d08a2649d2303b62168ae42e6faa /docs/html/tools | |
parent | c85bf85941212bba8d6827397f9572ea9ec8e8ef (diff) | |
download | frameworks_base-493ff3cb8f0c6f7729763f7d4a8571c0955d6f4b.zip frameworks_base-493ff3cb8f0c6f7729763f7d4a8571c0955d6f4b.tar.gz frameworks_base-493ff3cb8f0c6f7729763f7d4a8571c0955d6f4b.tar.bz2 |
Doc change: move data binding library to tools tab.
Change-Id: I74817864dc6fe390cb52ec2ea52ee1ea3a46c830
Diffstat (limited to 'docs/html/tools')
-rw-r--r-- | docs/html/tools/data-binding/guide.jd | 908 | ||||
-rw-r--r-- | docs/html/tools/data-binding/index.jd | 20 | ||||
-rw-r--r-- | docs/html/tools/tools_toc.cs | 13 |
3 files changed, 939 insertions, 2 deletions
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/tools/data-binding/guide.jd new file mode 100644 index 0000000..49b690f --- /dev/null +++ b/docs/html/tools/data-binding/guide.jd @@ -0,0 +1,908 @@ +page.title=Data Binding Guide + +@jd:body +<p>Data Binding allows you write declarative layouts and minimize the glue code +that is necessary to bind your application logic and layouts.</p> + + +<h2 id=build_environment>Build Environment</h2> + + +<p><strong>Setting Up Work Environment:</strong></p> + +<p>Data Binding EAP only supports gradle.</p> + +<p>To set up your application, unzip the provided bundle to a location. It has 3 +sections</p> + +<ul> + <li> <em>maven-repo:</em> which keeps the data-binding libraries + <li> <em>samples:</em> Sample applications + <li> <em>databinding.properties:</em> Properties file that can be used to integrate with your app +</ul> + +<p>Add the following section to the project’s build.gradle file (not the module's +build.gradle) and replace <code><BUNDLE_FOLDER> </code>with the absolute path of the bundle that you’ve unzipped in the previous step.</p> + +<pre class=prettyprint> +buildscript { + <strong>def </strong>eapFolder = '<BUNDLE_FOLDER>' +<strong> def </strong>Properties props = <strong>new </strong>Properties() + props.load(<strong>new </strong>FileInputStream(<strong>"</strong>${eapFolder}<strong>/databinding.properties"</strong>)) + props.mavenRepoDir = <strong>"</strong>${eapFolder}<strong>/</strong>${props.mavenRepoName}<strong>" + </strong>ext.config = props + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } + dependencies { + classpath <strong>"com.android.tools.build:gradle:1.1.3" + </strong>classpath <strong>"com.android.databinding:dataBinder:</strong>${config.snapshotVersion}<strong>" +<em></strong> </em>} +} +allprojects { + repositories { + jcenter() + maven { + url config.mavenRepoDir + } + } +} +</pre> + +<p>Next, add the following lines to the <em>build.gradle</em> +file of each module that will use data-binding. The application module must +have this, even if only its libraries use data binding.</p> + +<pre class=prettyprint> +apply plugin: <strong>'com.android.databinding' +</strong>dependencies { + compile <strong>"com.android.databinding:library:</strong>${config.snapshotVersion}<strong>" +</strong> compile <strong>"com.android.databinding:baseLibrary:</strong>${config.snapshotVersion}<strong>" +</strong> compile <strong>"com.android.databinding:adapters:</strong>${config.snapshotVersion}<strong>" +</strong> provided <strong>"com.android.databinding:annotationprocessor:</strong>${config.snapshotVersion}<strong>" +</strong>} +</pre> + + +<h2 id="data_binding_layout_files">Data Binding Layout Files</h2> + + +<h3 id="writing_expressions">Writing your first data binding expressions:</h3> + +<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> + +<pre class=prettyprint> +<em><?<strong></em>xml version="1.0" encoding="utf-8"<em></strong>?> +</em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>> + <<strong>data</strong>> + <<strong>variable name="user" type="com.example.User"</strong>/> + </<strong>data</strong>> + <<strong>LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"</strong>> + <<strong>TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{user.firstName}"</strong>/> + <<strong>TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{user.lastName}"</strong>/> + </<strong>LinearLayout</strong>> +</<strong>layout</strong>> +</pre> + +<p>The user <strong>variable</strong> within <strong>data</strong> describes a property that may be used within this layout.</p> + +<pre class=prettyprint> +<<strong>variable name="user" type="com.example.User"</strong>/> +</pre> + +<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> +<pre class=prettyprint> +<<strong>TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{user.firstName}"</strong>/> +</pre> + + +<h3 id="data_object">Data Object</h3> + +<p>Let’s assume for now that you have a plain-old Java object (POJO) for User:</p> +<pre class=prettyprint> +<strong>public class </strong>User { + <strong>public final </strong>String <strong>firstName</strong>; + <strong>public final </strong>String <strong>lastName</strong>; + <strong>public </strong>User(String firstName, String lastName) { + <strong>this</strong>.<strong>firstName </strong>= firstName; + <strong>this</strong>.<strong>lastName </strong>= lastName; + } +} +</pre> + +<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> +<pre class=prettyprint> +<strong>public class </strong>User { + <strong>private final </strong>String <strong>firstName</strong>; + <strong>private final </strong>String <strong>lastName</strong>; + <strong>public </strong>User(String firstName, String lastName) { + <strong>this</strong>.<strong>firstName </strong>= firstName; + <strong>this</strong>.<strong>lastName </strong>= lastName; + } + <strong>public </strong>String getFirstName() { + <strong>return this</strong>.<strong>firstName</strong>; + } + <strong>public </strong>String getLastName() { + <strong>return this</strong>.<strong>lastName</strong>; + } +} +</pre> + +<p>From the perspective of data binding, these two classes are equivalent. The +expression <strong><code>@{user.lastName}</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 and the <code>getFirstName()</code> method in the latter class. +</p><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 +layout file was <code>activity_main.xml</code> so the generate class was <code>ActivityMainBinding</code>. This class holds all the bindings from the 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: +</p> + +<pre class=prettyprint> +@Override +<strong>protected void </strong>onCreate(Bundle savedInstanceState) { + <strong>super</strong>.onCreate(savedInstanceState); + ActivityMainBinding binding = DataBindingUtil.<em>setContentView</em>(<strong>this</strong>, R.layout.<em><strong>main_activity</strong></em>); + User user = <strong>new </strong>User(<strong>"Test"</strong>, <strong>"User"</strong>); + binding.setUser(user); +} +</pre> + +<p>You’re done! Run the application and you’ll see Test User in the UI.Alternatively, you can get the view via: +</p><pre class=prettyprint> +MainActivityBinding binding = MainActivityBinding.<em>inflate</em>(getLayoutInflater()); +</pre> + +<p>If you are using data binding items inside a ListView or RecyclerView adapter, +you may prefer to use: +</p><pre class=prettyprint> +ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, +false); +//or +ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.layout.<em><strong>list_item</strong></em>, viewGroup, <strong>false</strong>); +</pre> + + +<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><pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.view.View"</strong>/> +</<strong>data</strong>> +</pre> + +<p>Now, View may be used within your binding expression: +</p><pre class=prettyprint> +<<strong>TextView + android:text="@{user.lastName}" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"</strong>/> +</pre> + +<p>When there are class name conflicts, one of the classes may be renamed to an +“alias:”</p> +<pre class=prettyprint> +<<strong>import type="android.view.View"</strong>/> +<<strong>import type="com.example.real.estate.View" + alias="Vista"</strong>/> +</pre> + +<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> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="com.example.User"</strong>/> + <<strong>import type="java.util.List"</strong>/> + <<strong>variable name="user" type="User"</strong>/> + <<strong>variable name="userList" type="List<User>"</strong>/> +</<strong>data</strong>> +… +<<strong>TextView + android:text="@{((User)(user.connection)).lastName}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + +<p>Imported types may also be used when referencing static fields and methods in +expressions:</p> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="com.example.MyStringUtils"</strong>/> + <<strong>variable name="user" type="com.example.User"</strong>/> +</<strong>data</strong>> +… +<<strong>TextView + android:text="@{MyStringUtils.capitalize(user.lastName)}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + +<p>Just as in Java, <code>java.lang.*</code> is imported automatically.</p> +<h3 id=variables>Variables</h3> + +<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> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.graphics.drawable.Drawable"</strong>/> + <<strong>variable name="user" type="com.example.User"</strong>/> + <<strong>variable name="image" type="Drawable"</strong>/> + <<strong>variable name="note" type="String"</strong>/> +</<strong>data</strong>> +</pre> + +<p>The variable types are inspected at compile time, so if a variable implements <a href="#observable_objects">Observable</a>, <a href="#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> + +<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> + +<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> + +<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> + +<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> +<pre class=prettyprint> +<<strong>data class="ContactItem"</strong>> + ... +</<strong>data</strong>> +</pre> + +<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> +<pre class=prettyprint> +<<strong>data class=".ContactItem"</strong>> + ... +</<strong>data</strong>> +</pre> + +In this case, <code>ContactItem</code> is generated in the module package directly.Any package may be used if the full package is provided: +<pre class=prettyprint> +<<strong>data class="com.example.ContactItem"</strong>> + ... +</<strong>data</strong>> +</pre> + + +<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> +<pre class=prettyprint> +<em><?<strong></em>xml version="1.0" encoding="utf-8"<em></strong>?> +</em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android" +</strong> <strong> xmlns:bind="http://schemas.android.com/apk/res-auto"</strong>> + <<strong>data</strong>> + <<strong>variable name="user" type="com.example.User"</strong>/> + </<strong>data</strong>> + <<strong>LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"</strong>> + <<strong>include layout="@layout/name" + bind:user="@{user}"</strong>/> + <<strong>include layout="@layout/contact" + bind:user="@{user}"</strong>/> + </<strong>LinearLayout</strong>> +</<strong>layout</strong>> +</pre> + +<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> +<h3 id=expression_language>Expression Language</h3> + + +<h4 id=common_features>Common Features</h4> + +<p>The expression language looks a lot like a Java expression. These are the same:</p> +<ul> + <li> Mathematical <strong><code>+ - / * %</code></strong> + <li> String concatenation <strong><code>+</code></strong> + <li> <code>L</code>ogical <strong><code>&& ||</code></strong> + <li> Binary <strong><code>&</code> <code>|</code> <code>^</code></strong> + <li> Unary <strong><code>+ - ! ~</code></strong> + <li> Shift <strong><code>>> >>> <<</code></strong> + <li> Comparison <strong><code>== > < >= <=</code></strong> + <li> <strong><code>instanceof</code></strong> + <li> Grouping <strong><code>()</code></strong> + <li> Literals - character, String, numeric, <strong><code>null</code></strong> + <li> Cast + <li> Method calls + <li> Field access + <li> Array access <strong><code>[]</code></strong> + <li> Ternary operator <strong><code>?:</code></strong> +</ul> +<p>Examples:</p> +<pre class=prettyprint> +<strong>android:text="@{String.valueOf(index + 1)}" +android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" +android:transitionName='@{"image_" + id}'</strong> +</pre> + + +<h4 id=missing_operations>Missing Operations</h4> + +<p>A few operations are missing from the expression syntax that you can use in +Java.</p> +<ul> + <li> <strong><code>this</code></strong> + <li> <strong><code>super</code></strong> + <li> <strong><code>new</code></strong> + <li> Explicit generic invocation +</ul> + +<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> +<pre class=prettyprint> +<strong>android:text="@{user.displayName ?? user.lastName}"</strong> +</pre> + +<p>This is functionally equivalent to:</p> +<pre class=prettyprint> +<strong>android:text="@{user.displayName != null ? user.displayName : user.lastName}"</strong> +</pre> + + +<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> +<pre class=prettyprint> +<strong>android:text="@{user.lastName}"</strong> +</pre> + + +<h4 id=collections>Collections</h4> + +<p>Common collections: arrays, lists, sparse lists, and maps, may be accessed +using the <code>[]</code> operator for convenience.</p> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.util.SparseArray"</strong>/> + <<strong>import type="java.util.Map"</strong>/> + <<strong>import type="java.util.List"</strong>/> + <<strong>variable name="list" type="List<String>"</strong>/> + <<strong>variable name="sparse" type="SparseArray<String>"</strong>/> + <<strong>variable name="map" type="Map<String, String>"</strong>/> + <<strong>variable name="index" type="int"</strong>/> + <<strong>variable name="key" type="String"</strong>/> +</<strong>data</strong>> +… +<strong>android:text="@{list[index]}" +</strong>… +<strong>android:text="@{sparse[index]}" +</strong>… +<strong>android:text="@{map[key]}" +</strong> +</pre> + + +<h4 id=string_literals>String Literals</h4> + +<p>When using single quotes around the attribute value, it is easy to use double +quotes in the expression:</p> +<pre class=prettyprint> +<strong>android:text='@{map["firstName"]}'</strong> +</pre> + +<p>It is also possible to use double quotes to surround the attribute value. When +doing so, String literals should either use the " or back quote (`).</p> +<pre class=prettyprint> +<strong>android:text="@{map[`firstName`}" +android:text="@{map["firstName"]}"</strong> +</pre> + + +<h4 id=resources>Resources</h4> + +<p>It is possible to access resources as part of expressions using the normal +syntax:</p> +<pre class=prettyprint> +<strong>android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"</strong> +</pre> + +<p>Format strings and plurals may be evaluated by providing parameters:</p> +<pre class=prettyprint> +<strong>android:text="@{@string/nameFormat(firstName, lastName)}" +android:text="@{@plurals/banana(bananaCount)}"</strong> +</pre> + +<p>Some resources require explicit type evaluation.</p> + +<table> + <tr> + <th>Type</th> + <th>Normal Reference</th> + <th>Expression Reference</th> + </tr> + <tr> + <td> +<pre class=prettyprint> +String[]</td> + <td> +@array</td> + <td> +@stringArray</td> + </tr> + <tr> + <td> +int[]</td> + <td> +@array</td> + <td> +@intArray</td> + </tr> + <tr> + <td> +TypedArray</td> + <td> +@array</td> + <td> +@typedArray</td> + </tr> + <tr> + <td> +Animator</td> + <td> +@animator</td> + <td> +@animator</td> + </tr> + <tr> + <td> +StateListAnimator</td> + <td> +@animator</td> + <td> +@stateListAnimator</td> + </tr> + <tr> + <td> +</pre> + +color <code>int</code></td> + <td> +<pre class=prettyprint> +@color</td> + <td> +@color</td> + </tr> + <tr> + <td> +ColorStateList</td> + <td> +@color</td> + <td> +@colorStateList</td> + </tr> +</table> + +</pre> + + +<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, <code>Observable </code>objects, <code>ObservableField</code>s, and <code>observable collections</code>.</p> + +<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> + +<h3 id=observable_objects>Observable Objects</h3> + + +<p>A class implementing <code>android.databinding.Observable</code> interface will allow the binding to attach a single listener to a bound object +to listen for changes of all properties on that object.</p> + +<p>The <code>Observable</code> interface has a mechanism to add and remove listeners, but notifying is up to +the developer. To make development easier, a base class, <code>BaseObservable,</code> was created to implement the listener registration mechanism. The data class +implementer is still responsible for notifying when the properties change. This +is done by assigning an <code>Bindable </code>annotation to the getter and notifying in the setter.</p> + +<pre class=prettyprint> +<strong>private static class </strong>User <strong>extends </strong>BaseObservable { + <strong>private </strong>String <strong>firstName</strong>; + <strong>private </strong>String <strong>lastName</strong>; + @Bindable + <strong>public </strong>String getFirstName() { + <strong>return this</strong>.<strong>firstName</strong>; + } + @Bindable + <strong>public </strong>String getFirstName() { + <strong>return this</strong>.<strong>lastName</strong>; + } + <strong>public void </strong>setFirstName(String firstName) { + <strong>this</strong>.<strong>firstName </strong>= firstName; + notifyPropertyChanged(BR.firstName); + } + <strong>public void </strong>setLastName(String lastName) { + <strong>this</strong>.<strong>lastName </strong>= lastName; + notifyPropertyChanged(BR.lastName); + } +} +</pre> + +<p>The <code>Bindable </code>annotation generates an entry in the BR class file during compilation. The BR +class file will be generated in the module package.If the base class for data classes cannot be changed, the <code>Observable</code> interface may be implemented using the convenient <code>PropertyChangeRegistry</code> to store and notify listeners efficiently.</p> + +<h3 id=observablefields>ObservableFields</h3> + +<p>A little work is involved in creating Observable classes, so developers who +want to save time or have few properties may use ObservableFields. +ObservableFields are self-contained observable objects that have a single +field. There are versions for all primitive types and one for reference types. +To use, create a public final field in the data class:</p> +<pre class=prettyprint> +<strong>private static class </strong>User <strong>extends </strong>BaseObservable { + <strong>public final </strong>ObservableField<String> <strong>firstName </strong>= + <strong>new </strong>ObservableField<>(); + <strong>public final </strong>ObservableField<String> <strong>lastName </strong>= + <strong>new </strong>ObservableField<>(); + <strong>public final </strong>ObservableInt <strong>age </strong>= <strong>new </strong>ObservableInt(); +} +</pre> + +<p>That's it! To access the value, use the set and get accessor methods:</p> +<pre class=prettyprint> +user.<strong>firstName</strong>.set(<strong>"Google"</strong>); +<strong>int </strong>age = user.<strong>age</strong>.get(); +</pre> + + +<h3 id=observable_collections>Observable Collections</h3> + +<p>Some applications use more dynamic structures to hold data. Observable + collections allow keyed access to these data objects.ObservableArrayMap is useful when the key is a reference type, such as String.</p> + +<pre class=prettyprint> +ObservableArrayMap<String, Object> user = <strong>new </strong>ObservableArrayMap<>(); +user.put(<strong>"firstName"</strong>, <strong>"Google"</strong>); +user.put(<strong>"lastName"</strong>, <strong>"Inc."</strong>); +user.put(<strong>"age"</strong>, 17); +</pre> + +In the layout, the map may be accessed through the String keys: +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.databinding.ObservableMap"</strong>/> + <<strong>variable name="user" type="ObservableMap<String, Object>"</strong>/> +</<strong>data</strong>> +… +<<strong>TextView + android:text='@{user["lastName"]}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +<<strong>TextView + android:text='@{String.valueOf(1 + (Integer)user["age"])}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + +<p>ObservableArrayList is useful when the key is an integer:</p> +<pre class=prettyprint> +ObservableArrayList<Object> user = <strong>new </strong>ObservableArrayList<>(); +user.add(<strong>"Google"</strong>); +user.add(<strong>"Inc."</strong>); +user.add(17); +</pre> + +<p>In the layout, the list may be accessed through the indices:</p> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.databinding.ObservableList"</strong>/> + <<strong>import type="com.example.my.app.Fields"</strong>/> + <<strong>variable name="user" type="ObservableList<Object>"</strong>/> +</<strong>data</strong>> +… +<<strong>TextView + android:text='@{user[Fields.LAST_NAME]}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +<<strong>TextView + android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + + +<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 classes all extend <code>android.databinding.ViewDataBinding</code>.</p> +<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 are versions that attach the View to its parent and that inflate without +attaching.</p> +<pre class=prettyprint> +MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(<strong>this</strong>); +MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(viewGroup); +</pre> + +<p>If the layout was inflated using a different mechanism, it may be bound +separately:</p> +<pre class=prettyprint> +MyLayoutBinding binding = MyLayoutBinding.<em>bind</em>(viewRoot); +</pre> + +<p>Sometimes the binding cannot be known in advance. In such cases, the binding +can be created using the DataBindingUtil class:</p> +<pre class=prettyprint> +ViewDataBinding binding = DataBindingUtil.<em>inflate</em>(context, layoutId, + parent, attachToParent); +ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId); +</pre> + + +<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> +<pre class=prettyprint> +<<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>> + <<strong>data</strong>> + <<strong>variable name="user" type="com.example.User"</strong>/> + </<strong>data</strong>> + <<strong>LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"</strong>> + <<strong>TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{user.firstName}" +</strong> <strong>android:id="@+id/firstName"</strong>/> + <<strong>TextView android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@{user.lastName}"</strong> <strong>android:id="@+id/lastName"</strong>/> + </<strong>LinearLayout</strong>> +</<strong>layout</strong>> +</pre> + +Will generate a binding class with: +<pre class=prettyprint> +<strong>public final </strong>TextView <strong>firstName</strong>; +<strong>public final </strong>TextView <strong>lastName</strong>; +</pre> + +<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=variables>Variables</h3> + +<p>Each variable will be given a accessor methods.</p> +<pre class=prettyprint> +<<strong>data</strong>> + <<strong>import type="android.graphics.drawable.Drawable"</strong>/> + <<strong>variable name="user" type="com.example.User"</strong>/> + <<strong>variable name="image" type="Drawable"</strong>/> + <<strong>variable name="note" type="String"</strong>/> +</<strong>data</strong>> +</pre> + +<p>will generate setters and getters in the binding:</p> +<pre class=prettyprint> +<strong>public abstract </strong>com.example.User getUser(); +<strong>public abstract void </strong>setUser(com.example.User user); +<strong>public abstract </strong>Drawable getImage(); +<strong>public abstract void </strong>setImage(Drawable image); +<strong>public abstract </strong>String getNote(); +<strong>public abstract void </strong>setNote(String note); +</pre> + + +<h3 id=viewstubs>ViewStubs</h3> + +<p>ViewStubs are a little different from normal Views. They start off invisible +and when they either are made visible or are explicitly told to inflate, they +replace themselves in the layout by inflating another layout.</p> + +<p>Because the ViewStub essentially disappears from the View hierarchy, the View +in the binding object must also disappear to allow collection. Because the +Views are final, a ViewStubProxy object takes the place of the ViewStub, giving +the developer access to the ViewStub when it exists and also access to the +inflated View hierarchy when the ViewStub has been inflated.</p> + +<p>When inflating another layout, a binding must be established for the new +layout. Therefore, the ViewStubProxy must listen to the ViewStub's +OnInflateListener and establish the binding at that time. Since only one can +exist, the ViewStubProxy allows the developer to set an OnInflateListener on it +that it will call after establishing the binding.</p> + +<h3 id=advanced_binding>Advanced Binding</h3> + + +<h4 id=dynamic_variables>Dynamic Variables</h4> + +<p>At times, the specific binding class won't be known. For example, a +RecyclerView Adapter operating against arbitrary layouts won't know the +specific binding class. It still must assign the binding value during the +onBindViewHolder.</p> + +<p>In this example, all layouts that the RecyclerView binds to have an "item" +variable. The BindingHolder has a getBinding method returning the <code>ViewDataBinding</code> base.</p> +<pre class=prettyprint> +<strong>public void </strong>onBindViewHolder(BindingHolder holder, <strong>int </strong>position) { + <strong>final </strong>T item = <strong>mItems</strong>.get(position); + holder.getBinding().setVariable(BR.item, item); + holder.getBinding().executePendingBindings(); +} +</pre> + + +<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 executePendingBindings() method.</p> +<h2 id=attribute_setters>Attribute Setters</h2> + +<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> +<h3 id=automatic_setters>Automatic Setters</h3> + +For an attribute, data binding tries to find the method setAttribute. The +namespace for the attribute does not matter, only the attribute name itself. + +<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> +<pre class=prettyprint> +<android.support.v4.widget.<strong>DrawerLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:scrimColor="@{@color/scrim}" + app:drawerListener="@{fragment.drawerListener}"/></strong> +</pre> + + +<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 BindingMethods annotation. +This must be associated with a class and contains BindingMethod annotations, +one for each renamed method. For example, the <strong><code>android:tint</code></strong> attribute is really associated with setImageTintList, not setTint.</p> +<pre class=prettyprint> +@BindingMethods({ + @BindingMethod(type = <strong>"android.widget.ImageView"</strong>, + attribute = <strong>"android:tint"</strong>, + method = <strong>"setImageTintList"</strong>), +}) +</pre> + +<p>It is unlikely that developers will need to rename setters; the android +framework attributes have already been implemented.</p> +<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, setPadding(left, top, right, bottom) exists. A static +binding adapter method with the BindingAdapter annotation allows the developer +to customize how a setter for an attribute is called.</p> + +<p>The android attributes have already had BindingAdapters created. For example, +here is the one for paddingLeft:</p> +<pre class=prettyprint></p> +@BindingAdapter(<strong>"android:paddingLeft"</strong>) +<strong>public static void </strong>setPaddingLeft(View view, <strong>int </strong>padding) { + view.setPadding(padding, + view.getPaddingTop(), + view.getPaddingRight(), + view.getPaddingBottom()); +} +</pre> + +<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> + +<p>Developer-created binding adapters will override the data binding default +adapters when there is a conflict.</p> + +<p>You can also have adapters that receive multiple parameters. </p> +<pre class=prettyprint> +@BindingAdapter(attributes = {<strong>"bind:imageUrl"</strong>, <strong>"bind:error"</strong>}) +<strong>public static void </strong>loadImage(ImageView view, String url, Drawable error) { + Picasso.<em>with</em>(view.getContext()).load(url).error(error).into(view); +} +</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> +<ul> + <li> Custom namespaces are ignore during matching. + <li> You can also write adapters for android namespace. +</ul> + +<pre class=prettyprint> +<ImageView app:imageUrl=“@{venue.imageUrl}” +app:error=“@{@drawable/venueError}”/> +</pre> + + +<h2 id=converters>Converters</h2> + + +<h3 id=object_conversions>Object Conversions</h3> + +<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><p>This is a convenience for those using ObservableMaps to hold data. for example:</p> +<pre class=prettyprint> +<<strong>TextView + android:text='@{userMap["lastName"]}' + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + +<p>The userMap 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> + +<p>Sometimes conversions should be automatic between specific types. For example, +when setting the background:</p> +<pre class=prettyprint> +<<strong>View + android:background="@{isError ? @color/red : @color/white}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + +<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> +<pre class=prettyprint> +@BindingConversion +<strong>public static </strong>ColorDrawable convertColorToDrawable(<strong>int </strong>color) { + <strong>return new </strong>ColorDrawable(color); +} +</pre> + +<p>Note that conversions only happen at the setter level, so it is <strong>not allowed </strong>to mix types like this:</p> +<pre class=prettyprint> +<<strong>View + android:background="@{isError ? @drawable/error : @color/white}" + android:layout_width="wrap_content" + android:layout_height="wrap_content"</strong>/> +</pre> + diff --git a/docs/html/tools/data-binding/index.jd b/docs/html/tools/data-binding/index.jd new file mode 100644 index 0000000..ae5295a --- /dev/null +++ b/docs/html/tools/data-binding/index.jd @@ -0,0 +1,20 @@ +page.title=Android Data Binding Library +page.metaDescription=Engaging and retaining active users are the keys to success. Here are some resources to help you build an active user base. +section.landing=true +nonavpage=true + +@jd:body + +<p> + Some text here. +</p> + + + +<!-- <h2>Related Resources</h2> + <div class="resource-widget resource-flow-layout col-16" + data-query="tag:engagement" + data-sortOrder="random" + data-cardSizes="6x2" + data-maxResults="3"> + </div> --> diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs index 54da711..2f96570 100644 --- a/docs/html/tools/tools_toc.cs +++ b/docs/html/tools/tools_toc.cs @@ -294,6 +294,15 @@ class="en">Support Library</span></a></div> </ul> </li><!-- end of support library --> + <li class="nav-section"> + <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/data-binding/index.html"><span +class="en">Data Binding Library</span></a></div> + <ul> + <li><a href="<?cs var:toroot ?>tools/data-binding/overview.html">Overview</a></li> + <li><a href="<?cs var:toroot ?>tools/data-binding/guide.html">Guide</a></li> + </ul> + </li><!-- end of support library --> + <!-- Revision menu--> @@ -326,7 +335,7 @@ class="en">Support Library</span></a></div> </div> </li> - <li class="nav-section"> +<!-- <li class="nav-section"> <div class="nav-section-header"> <a href="<?cs var:toroot ?>tools/adk/index.html"> <span class="en">ADK</span></a> @@ -335,7 +344,7 @@ class="en">Support Library</span></a></div> <li><a href="<?cs var:toroot ?>tools/adk/adk2.html">ADK 2012 Guide</a></li> <li><a href="<?cs var:toroot ?>tools/adk/adk.html">ADK 2011 Guide</a></li> </ul> - </li> + </li> --> <!-- Eclipse ADT menu--> |