| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1 | page.title=Data Binding Guide |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 2 | page.tags="databinding", "layouts" |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 3 | @jd:body |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 4 | |
| 5 | <div class="wrap"> |
| 6 | <p class="caution" style="background-color:#fffdeb;"> |
| 7 | The <strong>Android Data Binding Library</strong> is available as a <strong>developer |
| 8 | preview</strong>. Expression syntax and behaviors may change prior to the full release |
| 9 | of the library, currently planned for Q3 2015. If you have feedback or want to report |
| 10 | issues, please use the <a href="https://code.google.com/p/android-developer-preview/">issue |
| 11 | tracker</a>. Stay tuned for more information about Data Binding and examples of how to use it. </a> |
| 12 | </p> |
| 13 | </div> |
| 14 | <div id="qv-wrapper"> |
| 15 | <div id="qv"> |
| 16 | <h2> |
| 17 | In this document: |
| 18 | </h2> |
| 19 | |
| 20 | <ol> |
| 21 | <li> |
| 22 | <a href="#build_environment">Build Environment</a> |
| 23 | </li> |
| 24 | |
| 25 | <li> |
| 26 | <a href="#data_binding_layout_files">Data Binding Layout Files</a> |
| 27 | <ol> |
| 28 | <li> |
| 29 | <a href="writing_expressions">Writing your first data binding |
| 30 | expressions</a> |
| 31 | </li> |
| 32 | |
| 33 | <li> |
| 34 | <a href="data_object">Data Object</a> |
| 35 | </li> |
| 36 | |
| 37 | <li> |
| 38 | <a href="binding_data">Binding Data</a> |
| 39 | </li> |
| 40 | </ol> |
| 41 | </li> |
| 42 | |
| 43 | <li> |
| 44 | <a href="#layout_details">Layout Details</a> |
| 45 | <ol> |
| 46 | <li> |
| 47 | <a href="imports">Imports</a> |
| 48 | </li> |
| 49 | |
| 50 | <li> |
| 51 | <a href="variables">Variables</a> |
| 52 | </li> |
| 53 | |
| 54 | <li> |
| 55 | <a href="custom_binding_class_names">Custom Binding Class Names</a> |
| 56 | </li> |
| 57 | |
| 58 | <li> |
| 59 | <a href="includes">Includes</a> |
| 60 | </li> |
| 61 | |
| 62 | <li> |
| 63 | <a href="expression_language">Expression Language</a> |
| 64 | </li> |
| 65 | </ol> |
| 66 | </li> |
| 67 | |
| 68 | <li> |
| 69 | <a href="#data_objects">Data Objects</a> |
| 70 | <ol> |
| 71 | <li> |
| 72 | <a href="observable_objects">Observable Objects</a> |
| 73 | </li> |
| 74 | |
| 75 | <li> |
| 76 | <a href="observablefields">ObservableFields</a> |
| 77 | </li> |
| 78 | |
| 79 | <li> |
| 80 | <a href="observable_collections">Observable Collections</a> |
| 81 | </li> |
| 82 | </ol> |
| 83 | </li> |
| 84 | |
| 85 | <li> |
| 86 | <a href="#generated_binding">Generated Binding</a> |
| 87 | <ol> |
| 88 | <li> |
| 89 | <a href="creating">Creating</a> |
| 90 | </li> |
| 91 | |
| 92 | <li> |
| 93 | <a href="views_with_ids">Views With IDs</a> |
| 94 | </li> |
| 95 | |
| 96 | <li> |
| 97 | <a href="variables">Variables</a> |
| 98 | </li> |
| 99 | |
| 100 | <li> |
| 101 | <a href="viewstubs">ViewStubs</a> |
| 102 | </li> |
| 103 | |
| 104 | <li> |
| 105 | <a href="advanced_binding">Advanced Binding</a> |
| 106 | </li> |
| 107 | </ol> |
| 108 | </li> |
| 109 | |
| 110 | <li> |
| 111 | <a href="#attribute_setters">Attribute Setters</a> |
| 112 | <ol> |
| 113 | <li> |
| 114 | <a href="automatic_setters">Automatic Setters</a> |
| 115 | </li> |
| 116 | |
| 117 | <li> |
| 118 | <a href="renamed_setters">Renamed Setters</a> |
| 119 | </li> |
| 120 | |
| 121 | <li> |
| 122 | <a href="custom_setters">Custom Setters</a> |
| 123 | </li> |
| 124 | </ol> |
| 125 | </li> |
| 126 | |
| 127 | <li> |
| 128 | <a href="#converters">Converters</a> |
| 129 | <ol> |
| 130 | <li> |
| 131 | <a href="object_conversions">Object Conversions</a> |
| 132 | </li> |
| 133 | |
| 134 | <li> |
| 135 | <a href="custom_conversions">Custom Conversions</a> |
| 136 | </li> |
| 137 | </ol> |
| 138 | </li> |
| 139 | </ol> |
| 140 | </div><!-- qv --> |
| 141 | </div><!-- qv-wrapper --> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 142 | |
| 143 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 144 | <p> |
| 145 | This document explains how to use the Data Binding Library to write |
| 146 | declarative layouts and minimize the glue code necessary to bind your |
| 147 | application logic and layouts. |
| 148 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 149 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 150 | <h2 id="build_environment"> |
| 151 | Build Environment |
| 152 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 153 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 154 | <p> |
| 155 | <strong>Setting Up Work Environment:</strong> |
| 156 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 157 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 158 | <p> |
| 159 | To set up your application to use data binding, add data binding to the class |
| 160 | path of your build gradle file, right below "android". |
| 161 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 162 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 163 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 164 | dependencies { |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 165 | classpath <strong>"com.android.tools.build:gradle:1.2.3" |
| 166 | </strong>classpath <strong>"com.android.databinding:dataBinder:</strong>1.0-rc0" |
| 167 | } |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 168 | } |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 169 | </pre> |
| 170 | <p> |
| 171 | Then make sure jcenter is in the repositories list for your sub projects. |
| 172 | </p> |
| 173 | |
| 174 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 175 | allprojects { |
| 176 | repositories { |
| 177 | jcenter() |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 178 | } |
| 179 | } |
| 180 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 181 | <p> |
| 182 | In each module you want to use data binding, apply the plugin right after |
| 183 | android plugin |
| 184 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 185 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 186 | <pre> |
| 187 | apply plugin: ‘com.android.application' |
| 188 | apply plugin: '<strong>com.android.databinding</strong>' |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 189 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 190 | <p> |
| 191 | The data binding plugin is going to add necessary <strong>provided</strong> |
| 192 | and <strong>compile configuration</strong> dependencies to your project. |
| 193 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 194 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 195 | <h2 id="data_binding_layout_files"> |
| 196 | Data Binding Layout Files |
| 197 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 198 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 199 | <h3 id="writing_expressions"> |
| 200 | Writing your first data binding expressions |
| 201 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 202 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 203 | <p> |
| 204 | Data-binding layout files are slightly different and start with a root tag of |
| 205 | <strong>layout</strong> followed by a <strong>data</strong> element and a |
| 206 | <strong>view</strong> root element. This view element is what your root would |
| 207 | be in a non-binding layout file. A sample file looks like this: |
| 208 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 209 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 210 | <pre> |
| 211 | <em><?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?> |
| 212 | </em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>> |
| 213 | <<strong>data</strong>> |
| 214 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 215 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 216 | <<strong>LinearLayout |
| 217 | android:orientation="vertical" |
| 218 | android:layout_width="match_parent" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 219 | android:layout_height="match_parent"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 220 | <<strong>TextView android:layout_width="wrap_content" |
| 221 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 222 | android:text="@{user.firstName}"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 223 | <<strong>TextView android:layout_width="wrap_content" |
| 224 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 225 | android:text="@{user.lastName}"</strong>/> |
| 226 | </<strong>LinearLayout</strong>> |
| 227 | </<strong>layout</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 228 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 229 | <p> |
| 230 | The user <strong>variable</strong> within <strong>data</strong> describes a |
| 231 | property that may be used within this layout. |
| 232 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 233 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 234 | <pre> |
| 235 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 236 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 237 | <p> |
| 238 | Expressions within the layout are written in the attribute properties using |
| 239 | the “<code>@{}</code>” syntax. Here, the TextView’s text is set to the |
| 240 | firstName property of user: |
| 241 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 242 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 243 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 244 | <<strong>TextView android:layout_width="wrap_content" |
| 245 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 246 | android:text="@{user.firstName}"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 247 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 248 | <h3 id="data_object"> |
| 249 | Data Object |
| 250 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 251 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 252 | <p> |
| 253 | Let’s assume for now that you have a plain-old Java object (POJO) for User: |
| 254 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 255 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 256 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 257 | <strong>public class </strong>User { |
| 258 | <strong>public final </strong>String <strong>firstName</strong>; |
| 259 | <strong>public final </strong>String <strong>lastName</strong>; |
| 260 | <strong>public </strong>User(String firstName, String lastName) { |
| 261 | <strong>this</strong>.<strong>firstName </strong>= firstName; |
| 262 | <strong>this</strong>.<strong>lastName </strong>= lastName; |
| 263 | } |
| 264 | } |
| 265 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 266 | <p> |
| 267 | This type of object has data that never changes. It is common in applications |
| 268 | to have data that is read once and never changes thereafter. It is also |
| 269 | possible to use a JavaBeans objects: |
| 270 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 271 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 272 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 273 | <strong>public class </strong>User { |
| 274 | <strong>private final </strong>String <strong>firstName</strong>; |
| 275 | <strong>private final </strong>String <strong>lastName</strong>; |
| 276 | <strong>public </strong>User(String firstName, String lastName) { |
| 277 | <strong>this</strong>.<strong>firstName </strong>= firstName; |
| 278 | <strong>this</strong>.<strong>lastName </strong>= lastName; |
| 279 | } |
| 280 | <strong>public </strong>String getFirstName() { |
| 281 | <strong>return this</strong>.<strong>firstName</strong>; |
| 282 | } |
| 283 | <strong>public </strong>String getLastName() { |
| 284 | <strong>return this</strong>.<strong>lastName</strong>; |
| 285 | } |
| 286 | } |
| 287 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 288 | <p> |
| 289 | From the perspective of data binding, these two classes are equivalent. The |
| 290 | expression <strong><code>@{user.firstName}</code></strong> used for |
| 291 | the TextView’s <strong><code>android:text</code></strong> attribute will |
| 292 | access the <strong><code>firstName</code></strong> field in the former class |
| 293 | and the <code>getFirstName()</code> method in the latter class. |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 294 | </p> |
| 295 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 296 | <h3 id="binding_data"> |
| 297 | Binding Data |
| 298 | </h3> |
| 299 | |
| 300 | <p> |
| 301 | By default, a Binding class will be generated based on the name of the layout |
| 302 | file, converting it to Pascal case and suffixing “Binding” to it. The above |
| 303 | layout file was <code>activity_main.xml</code> so the generate class was |
| 304 | <code>ActivityMainBinding</code>. This class holds all the bindings from the |
| 305 | layout properties (e.g. the <code>user</code> variable) to the layout’s Views |
| 306 | and knows how to assign values for the binding expressions.The easiest means |
| 307 | for creating the bindings is to do it while inflating: |
| 308 | </p> |
| 309 | |
| 310 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 311 | @Override |
| 312 | <strong>protected void </strong>onCreate(Bundle savedInstanceState) { |
| 313 | <strong>super</strong>.onCreate(savedInstanceState); |
| 314 | ActivityMainBinding binding = DataBindingUtil.<em>setContentView</em>(<strong>this</strong>, R.layout.<em><strong>main_activity</strong></em>); |
| 315 | User user = <strong>new </strong>User(<strong>"Test"</strong>, <strong>"User"</strong>); |
| 316 | binding.setUser(user); |
| 317 | } |
| 318 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 319 | <p> |
| 320 | You’re done! Run the application and you’ll see Test User in the UI. |
| 321 | Alternatively, you can get the view via: |
| 322 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 323 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 324 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 325 | MainActivityBinding binding = MainActivityBinding.<em>inflate</em>(getLayoutInflater()); |
| 326 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 327 | <p> |
| 328 | If you are using data binding items inside a ListView or RecyclerView |
| 329 | adapter, you may prefer to use: |
| 330 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 331 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 332 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 333 | ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, |
| 334 | false); |
| 335 | //or |
| 336 | ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.layout.<em><strong>list_item</strong></em>, viewGroup, <strong>false</strong>); |
| 337 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 338 | <h2 id="layout_details"> |
| 339 | Layout Details |
| 340 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 341 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 342 | <h3 id="imports"> |
| 343 | Imports |
| 344 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 345 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 346 | <p> |
| 347 | Zero or more <strong><code>import</code></strong> elements may be used inside |
| 348 | the <strong><code>data</code></strong> element. These allow easy reference to |
| 349 | classes inside your layout file, just like in Java. |
| 350 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 351 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 352 | <pre> |
| 353 | <<strong>data</strong>> |
| 354 | <<strong>import type="android.view.View"</strong>/> |
| 355 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 356 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 357 | <p> |
| 358 | Now, View may be used within your binding expression: |
| 359 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 360 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 361 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 362 | <<strong>TextView |
| 363 | android:text="@{user.lastName}" |
| 364 | android:layout_width="wrap_content" |
| 365 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 366 | android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 367 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 368 | <p> |
| 369 | When there are class name conflicts, one of the classes may be renamed to an |
| 370 | “alias:” |
| 371 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 372 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 373 | <pre> |
| 374 | <<strong>import type="android.view.View"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 375 | <<strong>import type="com.example.real.estate.View" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 376 | alias="Vista"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 377 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 378 | <p> |
| 379 | Now, <strong><code>Vista</code></strong> may be used to reference the |
| 380 | <code>com.example.real.estate.View</code> and |
| 381 | <strong><code>View</code></strong> may be used to reference |
| 382 | <code>android.view.View</code> within the layout file. Imported types may be |
| 383 | used as type references in variables and expressions: |
| 384 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 385 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 386 | <pre> |
| 387 | <<strong>data</strong>> |
| 388 | <<strong>import type="com.example.User"</strong>/> |
| 389 | <<strong>import type="java.util.List"</strong>/> |
| 390 | <<strong>variable name="user" type="User"</strong>/> |
| 391 | <<strong>variable name="userList" type="List&lt;User>"</strong>/> |
| 392 | </<strong>data</strong>> |
| 393 | </pre> |
| 394 | <p class="note"> |
| 395 | <strong>Note</strong>: Android Studio does not yet handle imports so the |
| 396 | autocomplete for imported variables may not work in your IDE. Your |
| 397 | application will still compile fine and you can work around the IDE issue by |
| 398 | using fully qualified names in your variable definitions. |
| 399 | </p> |
| 400 | |
| 401 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 402 | <<strong>TextView |
| 403 | android:text="@{((User)(user.connection)).lastName}" |
| 404 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 405 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 406 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 407 | <p> |
| 408 | Imported types may also be used when referencing static fields and methods in |
| 409 | expressions: |
| 410 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 411 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 412 | <pre> |
| 413 | <<strong>data</strong>> |
| 414 | <<strong>import type="com.example.MyStringUtils"</strong>/> |
| 415 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 416 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 417 | … |
| 418 | <<strong>TextView |
| 419 | android:text="@{MyStringUtils.capitalize(user.lastName)}" |
| 420 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 421 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 422 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 423 | <p> |
| 424 | Just as in Java, <code>java.lang.*</code> is imported automatically. |
| 425 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 426 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 427 | <h3 id="variables"> |
| 428 | Variables |
| 429 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 430 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 431 | <p> |
| 432 | Any number of <strong><code>variable</code></strong> elements may be used |
| 433 | inside the <strong><code>data</code></strong> element. Each |
| 434 | <strong><code>variable</code></strong> element describes a property that may |
| 435 | be set on the layout to be used in binding expressions within the layout |
| 436 | file. |
| 437 | </p> |
| 438 | |
| 439 | <pre> |
| 440 | <<strong>data</strong>> |
| 441 | <<strong>import type="android.graphics.drawable.Drawable"</strong>/> |
| 442 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 443 | <<strong>variable name="image" type="Drawable"</strong>/> |
| 444 | <<strong>variable name="note" type="String"</strong>/> |
| 445 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 446 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 447 | <p> |
| 448 | The variable types are inspected at compile time, so if a variable implements |
| 449 | <a href="#observable_objects">Observable</a> or is an <a href= |
| 450 | "#observable_collections">observable collection</a>, that should be reflected |
| 451 | in the type. If the variable is a base class or interface that does not |
| 452 | implement the Observable* interface, the variables will <strong>not |
| 453 | be</strong> observed! |
| 454 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 455 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 456 | <p> |
| 457 | When there are different layout files for various configurations (e.g. |
| 458 | landscape or portrait), the variables will be combined. There must not be |
| 459 | conflicting variable definitions between these layout files. |
| 460 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 461 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 462 | <p> |
| 463 | The generated binding class will have a setter and getter for each of the |
| 464 | described variables. The variables will take the default Java values until |
| 465 | the setter is called — <code>null</code> for reference types, |
| 466 | <code>0</code> for <code>int</code>, <code>false</code> for |
| 467 | <code>boolean</code>, etc. |
| 468 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 469 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 470 | <h3 id="custom_binding_class_names"> |
| 471 | Custom Binding Class Names |
| 472 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 473 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 474 | <p> |
| 475 | By default, a Binding class is generated based on the name of the layout |
| 476 | file, starting it with upper-case, removing underscores ( _ ) and |
| 477 | capitalizing the following letter and then suffixing “Binding”. This class |
| 478 | will be placed in a databinding package under the module package. For |
| 479 | example, the layout file <code>contact_item.xml</code> will generate |
| 480 | <code>ContactItemBinding</code>. If the module package is |
| 481 | <code>com.example.my.app</code>, then it will be placed in |
| 482 | <code>com.example.my.app.databinding</code>. |
| 483 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 484 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 485 | <p> |
| 486 | Binding classes may be renamed or placed in different packages by adjusting |
| 487 | the <strong><code>class</code></strong> attribute of the |
| 488 | <strong><code>data</code></strong> element. For example: |
| 489 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 490 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 491 | <pre> |
| 492 | <<strong>data class="ContactItem"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 493 | ... |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 494 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 495 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 496 | <p> |
| 497 | This generates the binding class as <code>ContactItem</code> in the |
| 498 | databinding package in the module package. If the class should be generated |
| 499 | in a different package within the module package, it may be prefixed with |
| 500 | “.”: |
| 501 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 502 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 503 | <pre> |
| 504 | <<strong>data class=".ContactItem"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 505 | ... |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 506 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 507 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 508 | <p> |
| 509 | In this case, <code>ContactItem</code> is generated in the module package |
| 510 | directly. Any package may be used if the full package is provided: |
| 511 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 512 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 513 | <pre> |
| 514 | <<strong>data class="com.example.ContactItem"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 515 | ... |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 516 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 517 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 518 | <h3 id="includes"> |
| 519 | Includes |
| 520 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 521 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 522 | <p> |
| 523 | Variables may be passed into an included layout's binding from the |
| 524 | containing layout by using the application namespace and the variable name in |
| 525 | an attribute: |
| 526 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 527 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 528 | <pre> |
| 529 | <em><?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 530 | </em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 531 | </strong> <strong> xmlns:bind="http://schemas.android.com/apk/res-auto"</strong>> |
| 532 | <<strong>data</strong>> |
| 533 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 534 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 535 | <<strong>LinearLayout |
| 536 | android:orientation="vertical" |
| 537 | android:layout_width="match_parent" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 538 | android:layout_height="match_parent"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 539 | <<strong>include layout="@layout/name" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 540 | bind:user="@{user}"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 541 | <<strong>include layout="@layout/contact" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 542 | bind:user="@{user}"</strong>/> |
| 543 | </<strong>LinearLayout</strong>> |
| 544 | </<strong>layout</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 545 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 546 | <p> |
| 547 | Here, there must be a <code>user</code> variable in both the |
| 548 | <code>name.xml</code> and <code>contact.xml</code> layout files. |
| 549 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 550 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 551 | <h3 id="expression_language"> |
| 552 | Expression Language |
| 553 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 554 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 555 | <h4 id="common_features"> |
| 556 | Common Features |
| 557 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 558 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 559 | <p> |
| 560 | The expression language looks a lot like a Java expression. These are the |
| 561 | same: |
| 562 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 563 | |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 564 | <ul> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 565 | <li>Mathematical <strong><code>+ - / * %</code></strong> |
| 566 | </li> |
| 567 | |
| 568 | <li>String concatenation <strong><code>+</code></strong> |
| 569 | </li> |
| 570 | |
| 571 | <li> |
| 572 | <code>L</code>ogical <strong><code>&& ||</code></strong> |
| 573 | </li> |
| 574 | |
| 575 | <li>Binary <strong><code>&</code> <code>|</code> <code>^</code></strong> |
| 576 | </li> |
| 577 | |
| 578 | <li>Unary <strong><code>+ - ! ~</code></strong> |
| 579 | </li> |
| 580 | |
| 581 | <li>Shift <strong><code>>> >>> <<</code></strong> |
| 582 | </li> |
| 583 | |
| 584 | <li>Comparison <strong><code>== > < >= <=</code></strong> |
| 585 | </li> |
| 586 | |
| 587 | <li> |
| 588 | <strong><code>instanceof</code></strong> |
| 589 | </li> |
| 590 | |
| 591 | <li>Grouping <strong><code>()</code></strong> |
| 592 | </li> |
| 593 | |
| 594 | <li>Literals - character, String, numeric, <strong><code>null</code></strong> |
| 595 | </li> |
| 596 | |
| 597 | <li>Cast |
| 598 | </li> |
| 599 | |
| 600 | <li>Method calls |
| 601 | </li> |
| 602 | |
| 603 | <li>Field access |
| 604 | </li> |
| 605 | |
| 606 | <li>Array access <strong><code>[]</code></strong> |
| 607 | </li> |
| 608 | |
| 609 | <li>Ternary operator <strong><code>?:</code></strong> |
| 610 | </li> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 611 | </ul> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 612 | |
| 613 | <p> |
| 614 | Examples: |
| 615 | </p> |
| 616 | |
| 617 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 618 | <strong>android:text="@{String.valueOf(index + 1)}" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 619 | android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}" |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 620 | android:transitionName='@{"image_" + id}'</strong> |
| 621 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 622 | <h4 id="missing_operations"> |
| 623 | Missing Operations |
| 624 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 625 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 626 | <p> |
| 627 | A few operations are missing from the expression syntax that you can use in |
| 628 | Java. |
| 629 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 630 | |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 631 | <ul> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 632 | <li> |
| 633 | <strong><code>this</code></strong> |
| 634 | </li> |
| 635 | |
| 636 | <li> |
| 637 | <strong><code>super</code></strong> |
| 638 | </li> |
| 639 | |
| 640 | <li> |
| 641 | <strong><code>new</code></strong> |
| 642 | </li> |
| 643 | |
| 644 | <li>Explicit generic invocation |
| 645 | </li> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 646 | </ul> |
| 647 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 648 | <h4 id="null_coalescing_operator"> |
| 649 | Null Coalescing Operator |
| 650 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 651 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 652 | <p> |
| 653 | The null coalescing operator (<strong><code>??</code></strong>) chooses the |
| 654 | left operand if it is not null or the right if it is null. |
| 655 | </p> |
| 656 | |
| 657 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 658 | <strong>android:text="@{user.displayName ?? user.lastName}"</strong> |
| 659 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 660 | <p> |
| 661 | This is functionally equivalent to: |
| 662 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 663 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 664 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 665 | <strong>android:text="@{user.displayName != null ? user.displayName : user.lastName}"</strong> |
| 666 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 667 | <h4 id="property_reference"> |
| 668 | Property Reference |
| 669 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 670 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 671 | <p> |
| 672 | The first was already discussed in the <a href= |
| 673 | "#writing_your_first_data_binding_expressions">Writing your first data |
| 674 | binding expressions</a> above: short form JavaBean references. When an |
| 675 | expression references a property on a class, it uses the same format for |
| 676 | fields, getters, and ObservableFields. |
| 677 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 678 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 679 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 680 | <strong>android:text="@{user.lastName}"</strong> |
| 681 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 682 | <h4> |
| 683 | Avoiding NullPointerException |
| 684 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 685 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 686 | <p> |
| 687 | Generated data binding code automatically checks for nulls and avoid null |
| 688 | pointer exceptions. For example, in the expression |
| 689 | <code>@{user.name}</code>, if <code>user</code> is null, |
| 690 | <code>user.name</code> will be assigned its default value (null). If you were |
| 691 | referencing <code>user.age</code>, where age is an <code>int</code>, then it |
| 692 | would default to 0. |
| 693 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 694 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 695 | <h4 id="collections"> |
| 696 | Collections |
| 697 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 698 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 699 | <p> |
| 700 | Common collections: arrays, lists, sparse lists, and maps, may be accessed |
| 701 | using the <code>[]</code> operator for convenience. |
| 702 | </p> |
| 703 | |
| 704 | <pre> |
| 705 | <<strong>data</strong>> |
| 706 | <<strong>import type="android.util.SparseArray"</strong>/> |
| 707 | <<strong>import type="java.util.Map"</strong>/> |
| 708 | <<strong>import type="java.util.List"</strong>/> |
| 709 | <<strong>variable name="list" type="List<String>"</strong>/> |
| 710 | <<strong>variable name="sparse" type="SparseArray&lt;String>"</strong>/> |
| 711 | <<strong>variable name="map" type="Map&lt;String, String>"</strong>/> |
| 712 | <<strong>variable name="index" type="int"</strong>/> |
| 713 | <<strong>variable name="key" type="String"</strong>/> |
| 714 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 715 | … |
| 716 | <strong>android:text="@{list[index]}" |
| 717 | </strong>… |
| 718 | <strong>android:text="@{sparse[index]}" |
| 719 | </strong>… |
| 720 | <strong>android:text="@{map[key]}" |
| 721 | </strong> |
| 722 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 723 | <h4 id="string_literals"> |
| 724 | String Literals |
| 725 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 726 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 727 | <p> |
| 728 | When using single quotes around the attribute value, it is easy to use double |
| 729 | quotes in the expression: |
| 730 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 731 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 732 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 733 | <strong>android:text='@{map["firstName"]}'</strong> |
| 734 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 735 | <p> |
| 736 | It is also possible to use double quotes to surround the attribute value. |
| 737 | When doing so, String literals should either use the &quot; or back quote |
| 738 | (`). |
| 739 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 740 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 741 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 742 | <strong>android:text="@{map[`firstName`}" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 743 | android:text="@{map[&quot;firstName&quot;]}"</strong> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 744 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 745 | <h4 id="resources"> |
| 746 | Resources |
| 747 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 748 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 749 | <p> |
| 750 | It is possible to access resources as part of expressions using the normal |
| 751 | syntax: |
| 752 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 753 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 754 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 755 | <strong>android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"</strong> |
| 756 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 757 | <p> |
| 758 | Format strings and plurals may be evaluated by providing parameters: |
| 759 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 760 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 761 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 762 | <strong>android:text="@{@string/nameFormat(firstName, lastName)}" |
| 763 | android:text="@{@plurals/banana(bananaCount)}"</strong> |
| 764 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 765 | <p> |
| 766 | When a plural takes multiple parameters, all parameters should be passed: |
| 767 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 768 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 769 | <pre> |
| 770 | |
| 771 | Have an orange |
| 772 | Have %d oranges |
| 773 | |
| 774 | android:text="<strong>&commat{&commatplurals/orange(orangeCount, orangeCount)}</strong>" |
| 775 | </pre> |
| 776 | <p> |
| 777 | Some resources require explicit type evaluation. |
| 778 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 779 | |
| 780 | <table> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 781 | <tr> |
| 782 | <th> |
| 783 | Type |
| 784 | </th> |
| 785 | <th> |
| 786 | Normal Reference |
| 787 | </th> |
| 788 | <th> |
| 789 | Expression Reference |
| 790 | </th> |
| 791 | </tr> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 792 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 793 | <tr> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 794 | <td> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 795 | <pre> |
| 796 | String[] |
| 797 | </pre> |
| 798 | </td> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 799 | <td> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 800 | @array |
| 801 | </td> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 802 | <td> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 803 | @stringArray |
| 804 | </td> |
| 805 | </tr> |
| 806 | |
| 807 | <tr> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 808 | <td> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 809 | int[] |
| 810 | </td> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 811 | <td> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 812 | @array |
| 813 | </td> |
| 814 | <td> |
| 815 | @intArray |
| 816 | </td> |
| 817 | </tr> |
| 818 | |
| 819 | <tr> |
| 820 | <td> |
| 821 | TypedArray |
| 822 | </td> |
| 823 | <td> |
| 824 | @array |
| 825 | </td> |
| 826 | <td> |
| 827 | @typedArray |
| 828 | </td> |
| 829 | </tr> |
| 830 | |
| 831 | <tr> |
| 832 | <td> |
| 833 | Animator |
| 834 | </td> |
| 835 | <td> |
| 836 | @animator |
| 837 | </td> |
| 838 | <td> |
| 839 | @animator |
| 840 | </td> |
| 841 | </tr> |
| 842 | |
| 843 | <tr> |
| 844 | <td> |
| 845 | StateListAnimator |
| 846 | </td> |
| 847 | <td> |
| 848 | @animator |
| 849 | </td> |
| 850 | <td> |
| 851 | @stateListAnimator |
| 852 | </td> |
| 853 | </tr> |
| 854 | |
| 855 | <tr> |
| 856 | <td> |
| 857 | color <code>int</code> |
| 858 | </td> |
| 859 | <td> |
| 860 | <pre> |
| 861 | @color |
| 862 | </pre> |
| 863 | </td> |
| 864 | <td> |
| 865 | @color |
| 866 | </td> |
| 867 | </tr> |
| 868 | |
| 869 | <tr> |
| 870 | <td> |
| 871 | ColorStateList |
| 872 | </td> |
| 873 | <td> |
| 874 | @color |
| 875 | </td> |
| 876 | <td> |
| 877 | @colorStateList |
| 878 | </td> |
| 879 | </tr> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 880 | </table> |
| 881 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 882 | <h2 id="data_objects"> |
| 883 | Data Objects |
| 884 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 885 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 886 | <p> |
| 887 | Any plain old Java object (POJO) may be used for data binding, but modifying |
| 888 | a POJO will not cause the UI to update. The real power of data binding can be |
| 889 | used by giving your data objects the ability to notify when data changes. |
| 890 | There are three different data change notification mechanisms, |
| 891 | <code>Observable</code> objects, <code>ObservableField</code>s, and |
| 892 | <code>observable collections</code>. |
| 893 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 894 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 895 | <p> |
| 896 | When one of these observable data object is bound to the UI and a property of |
| 897 | the data object changes, the UI will be updated automatically. |
| 898 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 899 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 900 | <h3 id="observable_objects"> |
| 901 | Observable Objects |
| 902 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 903 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 904 | <p> |
| 905 | A class implementing <code>android.databinding.Observable</code> interface |
| 906 | will allow the binding to attach a single listener to a bound object to |
| 907 | listen for changes of all properties on that object. |
| 908 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 909 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 910 | <p> |
| 911 | The <code>Observable</code> interface has a mechanism to add and remove |
| 912 | listeners, but notifying is up to the developer. To make development easier, |
| 913 | a base class, <code>BaseObservable,</code> was created to implement the |
| 914 | listener registration mechanism. The data class implementer is still |
| 915 | responsible for notifying when the properties change. This is done by |
| 916 | assigning a <code>Bindable</code> annotation to the getter and notifying in |
| 917 | the setter. |
| 918 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 919 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 920 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 921 | <strong>private static class </strong>User <strong>extends </strong>BaseObservable { |
| 922 | <strong>private </strong>String <strong>firstName</strong>; |
| 923 | <strong>private </strong>String <strong>lastName</strong>; |
| 924 | @Bindable |
| 925 | <strong>public </strong>String getFirstName() { |
| 926 | <strong>return this</strong>.<strong>firstName</strong>; |
| 927 | } |
| 928 | @Bindable |
| 929 | <strong>public </strong>String getFirstName() { |
| 930 | <strong>return this</strong>.<strong>lastName</strong>; |
| 931 | } |
| 932 | <strong>public void </strong>setFirstName(String firstName) { |
| 933 | <strong>this</strong>.<strong>firstName </strong>= firstName; |
| 934 | notifyPropertyChanged(BR.firstName); |
| 935 | } |
| 936 | <strong>public void </strong>setLastName(String lastName) { |
| 937 | <strong>this</strong>.<strong>lastName </strong>= lastName; |
| 938 | notifyPropertyChanged(BR.lastName); |
| 939 | } |
| 940 | } |
| 941 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 942 | <p> |
| 943 | The <code>Bindable</code> annotation generates an entry in the BR class file |
| 944 | during compilation. The BR class file will be generated in the module |
| 945 | package.If the base class for data classes cannot be changed, the |
| 946 | <code>Observable</code> interface may be implemented using the convenient |
| 947 | <code>PropertyChangeRegistry</code> to store and notify listeners |
| 948 | efficiently. |
| 949 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 950 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 951 | <h3 id="observablefields"> |
| 952 | ObservableFields |
| 953 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 954 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 955 | <p> |
| 956 | A little work is involved in creating Observable classes, so developers who |
| 957 | want to save time or have few properties may use ObservableFields. |
| 958 | ObservableFields are self-contained observable objects that have a single |
| 959 | field. There are versions for all primitive types and one for reference |
| 960 | types. To use, create a public final field in the data class: |
| 961 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 962 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 963 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 964 | <strong>private static class </strong>User <strong>extends </strong>BaseObservable { |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 965 | <strong>public final </strong>ObservableField<String> <strong>firstName </strong>= |
| 966 | <strong>new </strong>ObservableField<>(); |
| 967 | <strong>public final </strong>ObservableField<String> <strong>lastName </strong>= |
| 968 | <strong>new </strong>ObservableField<>(); |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 969 | <strong>public final </strong>ObservableInt <strong>age </strong>= <strong>new </strong>ObservableInt(); |
| 970 | } |
| 971 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 972 | <p> |
| 973 | That's it! To access the value, use the set and get accessor methods: |
| 974 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 975 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 976 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 977 | user.<strong>firstName</strong>.set(<strong>"Google"</strong>); |
| 978 | <strong>int </strong>age = user.<strong>age</strong>.get(); |
| 979 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 980 | <h3 id="observable_collections"> |
| 981 | Observable Collections |
| 982 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 983 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 984 | <p> |
| 985 | Some applications use more dynamic structures to hold data. Observable |
| 986 | collections allow keyed access to these data objects.ObservableArrayMap is |
| 987 | useful when the key is a reference type, such as String. |
| 988 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 989 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 990 | <pre> |
| 991 | ObservableArrayMap<String, Object> user = <strong>new </strong>ObservableArrayMap<>(); |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 992 | user.put(<strong>"firstName"</strong>, <strong>"Google"</strong>); |
| 993 | user.put(<strong>"lastName"</strong>, <strong>"Inc."</strong>); |
| 994 | user.put(<strong>"age"</strong>, 17); |
| 995 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 996 | <p> |
| 997 | In the layout, the map may be accessed through the String keys: |
| 998 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 999 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1000 | <pre> |
| 1001 | <<strong>data</strong>> |
| 1002 | <<strong>import type="android.databinding.ObservableMap"</strong>/> |
| 1003 | <<strong>variable name="user" type="ObservableMap&lt;String, Object>"</strong>/> |
| 1004 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1005 | … |
| 1006 | <<strong>TextView |
| 1007 | android:text='@{user["lastName"]}' |
| 1008 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1009 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1010 | <<strong>TextView |
| 1011 | android:text='@{String.valueOf(1 + (Integer)user["age"])}' |
| 1012 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1013 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1014 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1015 | <p> |
| 1016 | ObservableArrayList is useful when the key is an integer: |
| 1017 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1018 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1019 | <pre> |
| 1020 | ObservableArrayList<Object> user = <strong>new </strong>ObservableArrayList<>(); |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1021 | user.add(<strong>"Google"</strong>); |
| 1022 | user.add(<strong>"Inc."</strong>); |
| 1023 | user.add(17); |
| 1024 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1025 | <p> |
| 1026 | In the layout, the list may be accessed through the indices: |
| 1027 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1028 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1029 | <pre> |
| 1030 | <<strong>data</strong>> |
| 1031 | <<strong>import type="android.databinding.ObservableList"</strong>/> |
| 1032 | <<strong>import type="com.example.my.app.Fields"</strong>/> |
| 1033 | <<strong>variable name="user" type="ObservableList&lt;Object>"</strong>/> |
| 1034 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1035 | … |
| 1036 | <<strong>TextView |
| 1037 | android:text='@{user[Fields.LAST_NAME]}' |
| 1038 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1039 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1040 | <<strong>TextView |
| 1041 | android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}' |
| 1042 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1043 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1044 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1045 | <h2 id="generated_binding"> |
| 1046 | Generated Binding |
| 1047 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1048 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1049 | <p> |
| 1050 | The generated binding class links the layout variables with the Views within |
| 1051 | the layout. As discussed earlier, the name and package of the Binding may be |
| 1052 | <a href="#custom_binding_class_names">customized</a>. The Generated binding |
| 1053 | classes all extend <code>android.databinding.ViewDataBinding</code>. |
| 1054 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1055 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1056 | <h3 id="creating"> |
| 1057 | Creating |
| 1058 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1059 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1060 | <p> |
| 1061 | The binding should be created soon after inflation to ensure that the View |
| 1062 | hierarchy is not disturbed prior to binding to the Views with expressions |
| 1063 | within the layout. There are a few ways to bind to a layout. The most common |
| 1064 | is to use the static methods on the Binding class.The inflate method inflates |
| 1065 | the View hierarchy and binds to it all it one step. There is a simpler |
| 1066 | version that only takes a <code>LayoutInflater</code> and one that takes a |
| 1067 | <code>ViewGroup</code> as well: |
| 1068 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1069 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1070 | <pre> |
| 1071 | MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(<strong>layoutInflater</strong>); |
| 1072 | MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(LayoutInflater, viewGroup, false); |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1073 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1074 | <p> |
| 1075 | If the layout was inflated using a different mechanism, it may be bound |
| 1076 | separately: |
| 1077 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1078 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1079 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1080 | MyLayoutBinding binding = MyLayoutBinding.<em>bind</em>(viewRoot); |
| 1081 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1082 | <p> |
| 1083 | Sometimes the binding cannot be known in advance. In such cases, the binding |
| 1084 | can be created using the DataBindingUtil class: |
| 1085 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1086 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1087 | <pre> |
| 1088 | ViewDataBinding binding = DataBindingUtil.<em>inflate</em>(LayoutInflater, layoutId, |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1089 | parent, attachToParent); |
| 1090 | ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId); |
| 1091 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1092 | <h3 id="views_with_ids"> |
| 1093 | Views With IDs |
| 1094 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1095 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1096 | <p> |
| 1097 | A public final field will be generated for each View with an ID in the |
| 1098 | layout. The binding does a single pass on the View hierarchy, extracting the |
| 1099 | Views with IDs. This mechanism can be faster than calling findViewById for |
| 1100 | several Views. For example: |
| 1101 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1102 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1103 | <pre> |
| 1104 | <<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>> |
| 1105 | <<strong>data</strong>> |
| 1106 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 1107 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1108 | <<strong>LinearLayout |
| 1109 | android:orientation="vertical" |
| 1110 | android:layout_width="match_parent" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1111 | android:layout_height="match_parent"</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1112 | <<strong>TextView android:layout_width="wrap_content" |
| 1113 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1114 | android:text="@{user.firstName}"</strong> |
| 1115 | <strong>android:id="@+id/firstName"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1116 | <<strong>TextView android:layout_width="wrap_content" |
| 1117 | android:layout_height="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1118 | android:text="@{user.lastName}"</strong> |
| 1119 | <strong>android:id="@+id/lastName"</strong>/> |
| 1120 | </<strong>LinearLayout</strong>> |
| 1121 | </<strong>layout</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1122 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1123 | <p> |
| 1124 | Will generate a binding class with: |
| 1125 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1126 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1127 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1128 | <strong>public final </strong>TextView <strong>firstName</strong>; |
| 1129 | <strong>public final </strong>TextView <strong>lastName</strong>; |
| 1130 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1131 | <p> |
| 1132 | IDs are not nearly as necessary as without data binding, but there are still |
| 1133 | some instances where access to Views are still necessary from code. |
| 1134 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1135 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1136 | <h3 id="variables2"> |
| 1137 | Variables |
| 1138 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1139 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1140 | <p> |
| 1141 | Each variable will be given accessor methods. |
| 1142 | </p> |
| 1143 | |
| 1144 | <pre> |
| 1145 | <<strong>data</strong>> |
| 1146 | <<strong>import type="android.graphics.drawable.Drawable"</strong>/> |
| 1147 | <<strong>variable name="user" type="com.example.User"</strong>/> |
| 1148 | <<strong>variable name="image" type="Drawable"</strong>/> |
| 1149 | <<strong>variable name="note" type="String"</strong>/> |
| 1150 | </<strong>data</strong>> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1151 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1152 | <p> |
| 1153 | will generate setters and getters in the binding: |
| 1154 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1155 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1156 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1157 | <strong>public abstract </strong>com.example.User getUser(); |
| 1158 | <strong>public abstract void </strong>setUser(com.example.User user); |
| 1159 | <strong>public abstract </strong>Drawable getImage(); |
| 1160 | <strong>public abstract void </strong>setImage(Drawable image); |
| 1161 | <strong>public abstract </strong>String getNote(); |
| 1162 | <strong>public abstract void </strong>setNote(String note); |
| 1163 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1164 | <h3 id="viewstubs"> |
| 1165 | ViewStubs |
| 1166 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1167 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1168 | <p> |
| 1169 | ViewStubs are a little different from normal Views. They start off invisible |
| 1170 | and when they either are made visible or are explicitly told to inflate, they |
| 1171 | replace themselves in the layout by inflating another layout. |
| 1172 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1173 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1174 | <p> |
| 1175 | Because the ViewStub essentially disappears from the View hierarchy, the View |
| 1176 | in the binding object must also disappear to allow collection. Because the |
| 1177 | Views are final, a ViewStubProxy object takes the place of the ViewStub, |
| 1178 | giving the developer access to the ViewStub when it exists and also access to |
| 1179 | the inflated View hierarchy when the ViewStub has been inflated. |
| 1180 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1181 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1182 | <p> |
| 1183 | When inflating another layout, a binding must be established for the new |
| 1184 | layout. Therefore, the ViewStubProxy must listen to the ViewStub's |
| 1185 | OnInflateListener and establish the binding at that time. Since only one can |
| 1186 | exist, the ViewStubProxy allows the developer to set an OnInflateListener on |
| 1187 | it that it will call after establishing the binding. |
| 1188 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1189 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1190 | <h3 id="advanced_binding"> |
| 1191 | Advanced Binding |
| 1192 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1193 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1194 | <h4 id="dynamic_variables"> |
| 1195 | Dynamic Variables |
| 1196 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1197 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1198 | <p> |
| 1199 | At times, the specific binding class won't be known. For example, a |
| 1200 | RecyclerView Adapter operating against arbitrary layouts won't know the |
| 1201 | specific binding class. It still must assign the binding value during the |
| 1202 | onBindViewHolder. |
| 1203 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1204 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1205 | <p> |
| 1206 | In this example, all layouts that the RecyclerView binds to have an "item" |
| 1207 | variable. The BindingHolder has a getBinding method returning the |
| 1208 | <code>ViewDataBinding</code> base. |
| 1209 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1210 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1211 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1212 | <strong>public void </strong>onBindViewHolder(BindingHolder holder, <strong>int </strong>position) { |
| 1213 | <strong>final </strong>T item = <strong>mItems</strong>.get(position); |
| 1214 | holder.getBinding().setVariable(BR.item, item); |
| 1215 | holder.getBinding().executePendingBindings(); |
| 1216 | } |
| 1217 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1218 | <h4 id="immediate_binding"> |
| 1219 | Immediate Binding |
| 1220 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1221 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1222 | <p> |
| 1223 | When a variable or observable changes, the binding will be scheduled to |
| 1224 | change before the next frame. There are times, however, when binding must be |
| 1225 | executed immediately. To force execution, use the |
| 1226 | <code>executePendingBindings()</code> method. |
| 1227 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1228 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1229 | <h4> |
| 1230 | Background Thread |
| 1231 | </h4> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1232 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1233 | <p> |
| 1234 | You can change your data model in a background thread as long as it is not a |
| 1235 | collection. Data binding will localize each variable / field while evaluating |
| 1236 | to avoid any concurrency issues. |
| 1237 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1238 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1239 | <h2 id="attribute_setters"> |
| 1240 | Attribute Setters |
| 1241 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1242 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1243 | <p> |
| 1244 | Whenever a bound value changes, the generated binding class must call a |
| 1245 | setter method on the View with the binding expression. The data binding |
| 1246 | framework has ways to customize which method to call to set the value. |
| 1247 | </p> |
| 1248 | |
| 1249 | <h3 id="automatic_setters"> |
| 1250 | Automatic Setters |
| 1251 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1252 | For an attribute, data binding tries to find the method setAttribute. The |
| 1253 | namespace for the attribute does not matter, only the attribute name itself. |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1254 | <p> |
| 1255 | For example, an expression associated with TextView's attribute |
| 1256 | <strong><code>android:text</code></strong> will look for a setText(String). |
| 1257 | If the expression returns an int, data binding will search for a setText(int) |
| 1258 | method. Be careful to have the expression return the correct type, casting if |
| 1259 | necessary. Note that data binding will work even if no attribute exists with |
| 1260 | the given name. You can then easily "create" attributes for any setter by |
| 1261 | using data binding. For example, support DrawerLayout doesn't have any |
| 1262 | attributes, but plenty of setters. You can use the automatic setters to use |
| 1263 | one of these. |
| 1264 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1265 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1266 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1267 | <android.support.v4.widget.<strong>DrawerLayout |
| 1268 | android:layout_width="wrap_content" |
| 1269 | android:layout_height="wrap_content" |
| 1270 | app:scrimColor="@{@color/scrim}" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1271 | app:drawerListener="@{fragment.drawerListener}"/></strong> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1272 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1273 | <h3 id="renamed_setters"> |
| 1274 | Renamed Setters |
| 1275 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1276 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1277 | <p> |
| 1278 | Some attributes have setters that don't match by name. For these |
| 1279 | methods, an attribute may be associated with the setter through |
| 1280 | BindingMethods annotation. This must be associated with a class and contains |
| 1281 | BindingMethod annotations, one for each renamed method. For example, the |
| 1282 | <strong><code>android:tint</code></strong> attribute is really associated |
| 1283 | with setImageTintList, not setTint. |
| 1284 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1285 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1286 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1287 | @BindingMethods({ |
| 1288 | @BindingMethod(type = <strong>"android.widget.ImageView"</strong>, |
| 1289 | attribute = <strong>"android:tint"</strong>, |
| 1290 | method = <strong>"setImageTintList"</strong>), |
| 1291 | }) |
| 1292 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1293 | <p> |
| 1294 | It is unlikely that developers will need to rename setters; the android |
| 1295 | framework attributes have already been implemented. |
| 1296 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1297 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1298 | <h3 id="custom_setters"> |
| 1299 | Custom Setters |
| 1300 | </h3> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1301 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1302 | <p> |
| 1303 | Some attributes need custom binding logic. For example, there is no |
| 1304 | associated setter for the <strong><code>android:paddingLeft</code></strong> |
| 1305 | attribute. Instead, <code>setPadding(left, top, right, bottom)</code> exists. |
| 1306 | A static binding adapter method with the <code>BindingAdapter</code> |
| 1307 | annotation allows the developer to customize how a setter for an attribute is |
| 1308 | called. |
| 1309 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1310 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1311 | <p> |
| 1312 | The android attributes have already had <code>BindingAdapter</code>s created. |
| 1313 | For example, here is the one for <code>paddingLeft</code>: |
| 1314 | </p> |
| 1315 | |
| 1316 | <pre> |
| 1317 | |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1318 | @BindingAdapter(<strong>"android:paddingLeft"</strong>) |
| 1319 | <strong>public static void </strong>setPaddingLeft(View view, <strong>int </strong>padding) { |
| 1320 | view.setPadding(padding, |
| 1321 | view.getPaddingTop(), |
| 1322 | view.getPaddingRight(), |
| 1323 | view.getPaddingBottom()); |
| 1324 | } |
| 1325 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1326 | <p> |
| 1327 | Binding adapters are useful for other types of customization. For example, a |
| 1328 | custom loader can be called off-thread to load an image. |
| 1329 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1330 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1331 | <p> |
| 1332 | Developer-created binding adapters will override the data binding default |
| 1333 | adapters when there is a conflict. |
| 1334 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1335 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1336 | <p> |
| 1337 | You can also have adapters that receive multiple parameters. |
| 1338 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1339 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1340 | <pre> |
| 1341 | @BindingAdapter({<strong>"bind:imageUrl"</strong>, <strong>"bind:error"</strong>}) |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1342 | <strong>public static void </strong>loadImage(ImageView view, String url, Drawable error) { |
| 1343 | Picasso.<em>with</em>(view.getContext()).load(url).error(error).into(view); |
| 1344 | } |
| 1345 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1346 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1347 | <ImageView app:imageUrl=“@{venue.imageUrl}” |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1348 | app:error=“@{@drawable/venueError}”/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1349 | </pre> |
| 1350 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1351 | <p> |
| 1352 | This adapter will be called if both <strong>imageUrl</strong> and |
| 1353 | <strong>error</strong> are used for an ImageView and <em>imageUrl</em> is a |
| 1354 | string and <em>error</em> is a drawable. |
| 1355 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1356 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1357 | <ul> |
| 1358 | <li>Custom namespaces are ignored during matching. |
| 1359 | </li> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1360 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1361 | <li>You can also write adapters for android namespace. |
| 1362 | </li> |
| 1363 | </ul> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1364 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1365 | <h2 id="converters"> |
| 1366 | Converters |
| 1367 | </h2> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1368 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1369 | <h3 id="object_conversions"> |
| 1370 | Object Conversions |
| 1371 | </h3> |
| 1372 | |
| 1373 | <p> |
| 1374 | When an Object is returned from a binding expression, a setter will be chosen |
| 1375 | from the automatic, renamed, and custom setters. The Object will be cast to a |
| 1376 | parameter type of the chosen setter. |
| 1377 | </p> |
| 1378 | |
| 1379 | <p> |
| 1380 | This is a convenience for those using ObservableMaps to hold data. for |
| 1381 | example: |
| 1382 | </p> |
| 1383 | |
| 1384 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1385 | <<strong>TextView |
| 1386 | android:text='@{userMap["lastName"]}' |
| 1387 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1388 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1389 | </pre> |
| 1390 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1391 | <p> |
| 1392 | The <code>userMap</code> returns an Object and that Object will be automatically cast to |
| 1393 | parameter type found in the setter <code>setText(CharSequence)</code>. When there |
| 1394 | may be confusion about the parameter type, the developer will need |
| 1395 | to cast in the expression. |
| 1396 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1397 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1398 | <h3 id="custom_conversions">Custom Conversions</h3> |
| 1399 | |
| 1400 | <p> |
| 1401 | Sometimes conversions should be automatic between specific types. For |
| 1402 | example, when setting the background: |
| 1403 | </p> |
| 1404 | |
| 1405 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1406 | <<strong>View |
| 1407 | android:background="@{isError ? @color/red : @color/white}" |
| 1408 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1409 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1410 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1411 | <p> |
| 1412 | Here, the background takes a <code>Drawable</code>, but the color is an |
| 1413 | integer. Whenever a <code>Drawable</code> is expected and an integer is |
| 1414 | returned, the <code>int</code> should be converted to a |
| 1415 | <code>ColorDrawable</code>. This conversion is done using a static method |
| 1416 | with a BindingConversion annotation: |
| 1417 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1418 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1419 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1420 | @BindingConversion |
| 1421 | <strong>public static </strong>ColorDrawable convertColorToDrawable(<strong>int </strong>color) { |
| 1422 | <strong>return new </strong>ColorDrawable(color); |
| 1423 | } |
| 1424 | </pre> |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1425 | <p> |
| 1426 | Note that conversions only happen at the setter level, so it is <strong>not |
| 1427 | allowed</strong> to mix types like this: |
| 1428 | </p> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1429 | |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1430 | <pre> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1431 | <<strong>View |
| 1432 | android:background="@{isError ? @drawable/error : @color/white}" |
| 1433 | android:layout_width="wrap_content" |
| Dirk Dougherty | ea1f6fb | 2015-05-26 20:31:12 -0700 | [diff] [blame^] | 1434 | android:layout_height="wrap_content"</strong>/> |
| Dirk Dougherty | 493ff3c | 2015-05-26 16:52:23 -0700 | [diff] [blame] | 1435 | </pre> |