Android Data Binding User Guide

Android Data Binding User Guide

1. Introduction

This article introduces how to use the Data Binding library to write declarative layout files and bind your app logic and layout files with minimal code.

The Data Binding library is not only flexible but also widely compatible - it is a support library, so you can use it on all Android platforms up to Android 2.1 (API level 7+).

Requires: Android Studio 1.3.0-beta1 or higher.

Test version

Please note: Data Binding library is currently in beta. While Data Binding is in beta, developers should be aware of the following considerations:

This is a beta version of a feature intended to generate developer feedback. It may contain bugs, or not be suitable for your use case, so use it at your own risk. That said, we'd love to get your feedback! Use the issue tracker to let us know what does or doesn't work for your use case.

Beta versions of the Data Binding library are subject to significant changes, including those that are not source code compatible with your application. That is, significant rework may be required to update this library in the future.

Developers can release apps built with beta versions of the Data Binding library at any time, although the standard Android SDK and Google Play Terms of Service caveats apply. And it’s always a good idea to thoroughly test your apps when adopting a new library or tool.

We are just getting started with Android Studio support at this time. There will be further Android Studio support in the future.

By using the Data Binding library beta version, you acknowledge these warnings.

2. Build environment

To start using Data Binding, you first need to download the library from the Support Library in the Android SDK Manager.

Please make sure you are using a compatible version of Android Studio. The Data Binding plugin for Android Studio requires Android Studio 1.3.0-beta1 or higher.

Work Environment

To use Data Binding in your app, you need to add Data Binding to the gradle build file, as follows:

  1. dependencies {
  2. classpath "com.android.tools.build:gradle:1.2.3"  
  3. classpath "com.android.databinding:dataBinder:1.0-rc0"  
  4. }
  5. }

Then make sure jcenter is in the repositories list, as follows:

  1. allprojects {
  2. repositories {
  3. jcenter()
  4. }
  5. }

In each module you want to use Data Binding, add the following plugin:

apply plugin: 'com.android.application'

apply plugin: 'com.android.databinding'

The Data Binding plugin will add required and compile configuration dependencies to your project.

3. Data Binding Layout File

Data Binding Expressions

The Data Binding layout file is a little different: the starting root tag is layout, followed by a data element and a view root element. The view element is the root element of your layout file that does not use Data Binding. An example is as follows:

  1. <?xml version= "1.0" encoding= "utf-8" ?>
  2. <layout xmlns:android= "http://schemas.android.com/apk/res/android" >
  3. <data>
  4. <variable name= "user" type= "com.example.User" />
  5. </data>
  6. <LinearLayout
  7. android:orientation= "vertical"  
  8. android:layout_width= "match_parent"  
  9. android:layout_height= "match_parent" >
  10. <TextView android:layout_width= "wrap_content"  
  11. android:layout_height= "wrap_content"  
  12. android:text= "@{user.firstName}" />
  13. <TextView android:layout_width= "wrap_content"  
  14. android:layout_height= "wrap_content"  
  15. android:text= "@{user.lastName}" />
  16. </LinearLayout>
  17. </layout>

A variable attribute named user is described in data, making it available in this layout:

<variable name="user" type="com.example.User"/>

In the layout attribute expression, write @{}. Here is a TextView whose text is set to the user's firstName attribute:

  1. <TextView android:layout_width= "wrap_content"  
  2. android:layout_height= "wrap_content"  
  3. android:text= "@{user.firstName}" />

Data Object

Assume you have a plain-old Java Object (POJO) named user:

  1. public   class User {
  2. public   final String firstName;
  3. public   final String lastName;
  4. public User(String firstName, String lastName) {
  5. this .firstName = firstName;
  6. this .lastName = lastName;
  7. }
  8. }

This type of object holds data that never changes. It is common in apps that can be read once and never changed afterwards. Of course, you can also use JavaBeans objects:

  1. public   class User {
  2. private   final String firstName;
  3. private   final String lastName;
  4. public User(String firstName, String lastName) {
  5. this .firstName = firstName;
  6. this .lastName = lastName;
  7. }
  8. public String getFirstName() {
  9. return   this .firstName;
  10. }
  11. public String getLastName() {
  12. return   this .lastName;
  13. }
  14. }

From a Data Binding perspective, these two classes are equivalent. The expression @{user.firstName} for the android:text attribute in TextView will access the firstName in the former POJO object and the getFirstName() method in the latter JavaBeans object.

Binding Data

By default, a Binding class is generated based on the name of the layout file, converting it to Pascal case and adding the "Binding" suffix. The layout file above is activity_main.xml, so the generated class name is ActivityMainBinding. This class contains all the bindings from layout properties to the layout's Views (such as the user variable), and it also knows how to assign values ​​to Binding expressions. The simplest way to create bindings is during inflating as follows:

  1. @Override  
  2. protected   void onCreate(Bundle savedInstanceState) {
  3. super .onCreate(savedInstanceState);
  4. ActivityMainBinding binding = DataBindingUtil.setContentView( this , R.layout.main_activity);
  5. User user = new User( "Test" , "User" );
  6. binding.setUser(user);
  7. }
  8.  
  9. That's it, after running the app, you will see the Test User. Or you can get the View as follows:
  10.  
  11. MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
  12.  
  13. If you use Data Binding in a ListView or RecyclerView adapter, you might use:
  14.  
  15. ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
  16. false );
  17. //or  
  18. ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false );

#p#

4. Dive into the Layout file

Import

Zero or more import elements may be used within a data element. These are used only to add references to your layout files, just like in Java:

  1. <data>
  2. < import type= "android.view.View" />
  3. </data>

Now, the View can use your Binding expression:

  1. < TextView  
  2. android:text = "@{user.lastName}"  
  3. android:layout_width = "wrap_content"  
  4. android:layout_height = "wrap_content"  
  5. android:visibility = "@{user.isAdult ? View.VISIBLE : View.GONE}" />  

When there is a conflict in class names, one of the class names can be renamed to an alias:

  1. < import type= "android.view.View" />
  2. < import type= "com.example.real.estate.View"  
  3. alias= "Vista" />

Thus, in the layout file, Vista corresponds to com.example.real.estate.View, and View corresponds to android.view.View. The imported types can be used as references in variables and expressions:

  1. <data>
  2. < import type= "com.example.User" />
  3. < import type= "java.util.List" />
  4. <variable name= "user" type= "User" />
  5. <variable name= "userList" type= "List<User>" />
  6. </data>

Note: Android Studio does not yet handle imports, so automatic variable imports will not work in your IDE. Your app will still compile fine, and you can use fully qualified names in your variable definitions to work around the IDE issue.

  1. <TextView
  2. android:text= "@{((User)(user.connection)).lastName}"  
  3. android:layout_width= "wrap_content"  
  4. android:layout_height= "wrap_content" />

Imported types can also use static properties and methods in expressions:

  1. <data>
  2. < import type= "com.example.MyStringUtils" />
  3. <variable name= "user" type= "com.example.User" />
  4. </data>
  5. <TextView
  6. android:text= "@{MyStringUtils.capitalize(user.lastName)}"  
  7. android:layout_width= "wrap_content"  
  8. android:layout_height= "wrap_content" />

Just like in Java, java.lang.* is imported automatically.

Variables

Any number of variable elements can be used in data. Each variable element describes a property used in a Binding expression in a layout file.

  1. < data >  
  2. < import   type = "android.graphics.drawable.Drawable" />  
  3. < variable   name = "user"   type = "com.example.User" />  
  4. < variable   name = "image"   type = "Drawable" />  
  5. < variable   name = "note"   type = "String" />  
  6. </ data >  

The Variable type is checked at compile time, so if a Variable implements Observable or an observable collection, this should be reflected in the type. (Annotation: Need to find information to understand) If the variable is a basic class or interface that does not implement the Observable interface, Variables will not be observed!

When there are different layout files for multiple configurations (e.g. landscape or portrait), the variables are merged. There must not be conflicting variable definitions between these layout files.

The generated Binding class will have setters and getters for each of the described Variables. These Variables will use the default Java value - null (reference type), 0 (int), false (boolean), etc., until the setter is called.

Custom Binding Class Name

By default, the Binding class is named based on the layout file name, starting with uppercase, removing underscores () and the first letter after () and then adding the "Binding" suffix. This class will be placed in a databinding package in a module package. For example, the layout file contact_item.xml will generate ContactItemBinding. If the module package is com.example.my.app, then it will be placed in com.example.my.app.databinding.

The Binding class can be renamed or placed in a different package by adjusting the class attribute in the data element. For example:

  1. <data class = "ContactItem" >
  2. ...
  3. </data>

A Binding class named ContactItem will be generated in the databinding package of the module package. If you want to generate this class in a different package, you need to add the prefix ., as follows:

  1. <data class = ".ContactItem" >
  2. ...
  3. </data>

In this case, the ContactItem class is generated directly in the module package. Or you can provide the entire package name:

  1. <data class = "com.example.ContactItem" >
  2. ...
  3. </data>

Includes

Variables can be passed from a container layout to a contained layout by using the application namespace and the variable name in the attributes:

  1. <?xml version= "1.0" encoding= "utf-8" ?>
  2.  
  3. <layout xmlns:android= "http://schemas.android.com/apk/res/android"  
  4.  
  5. xmlns:bind= "http://schemas.android.com/apk/res-auto" >
  6.  
  7. <data>
  8.  
  9. <variable name= "user" type= "com.example.User" />
  10.  
  11. </data>
  12.  
  13. <LinearLayout
  14.  
  15. android:orientation= "vertical"  
  16.  
  17. android:layout_width= "match_parent"  
  18.  
  19. android:layout_height= "match_parent" >
  20.  
  21. <include layout= "@layout/name"  
  22.  
  23. bind:user= "@{user}" />
  24.  
  25. <include layout= "@layout/contact"  
  26.  
  27. bind:user= "@{user}" />
  28.  
  29. </LinearLayout>
  30.  
  31. </layout>

Note: User variables are required in both name.xml and contact.xml layout files.

expression

Common expressions are very similar to Java expressions. The following are the same:

Math + - / * %

String concatenation+

Logical && ||

Binary & | ^

Unary operations + - ! ~

Shift>> >>> <<

Comparison == > < >= <=

instanceof

Group()

null

Cast

Method Call

Data Access[]

Ternary operation?:

Example:

  1. android:text= "@{String.valueOf(index + 1)}"  
  2. android:visibility= "@{age < 13 ? View.GONE : View.VISIBLE}"  
  3. android:transitionName= '@{"image_" + id}'  

Missing operations :

this

super

 

new

#p#

Explicit generic calls

Null Merge Operation

?? - the object on the left. If it is not null, select the object on the left; or if it is null, select the object on the right:

android:text="@{user.displayName ?? user.lastName}"

The function is written as follows:

  1. android:text= "@{user.displayName != null ? user.displayName : user.lastName}"  

Property reference

The first one has been mentioned before: a) Data Binding expression: a short format of JavaBean reference.

When an expression refers to a property of a class, it still uses the same format for fields, getters, and ObservableFields.

  1. android:text= "@{user.lastName}"  
  2.  
  3. Avoiding NullPointerException

Data Binding code generation automatically checks for nulls to avoid null pointer exceptions. For example, in the expression @{user.name}, if user is null, user.name will be assigned its default value (null). If you reference user.age, age is an int type, then its default value is 0.

gather

Commonly used collections: arrays, lists, sparse lists, and maps, can all be accessed using [] for simplicity.

  1. <data>
  2. < import type= "android.util.SparseArray" />
  3. < import type= "java.util.Map" />
  4. < import type= "java.util.List" />
  5. <variable name= "list" type= "List<String>" />
  6. <variable name= "sparse" type= "SparseArray<String>" />
  7. <variable name= "map" type= "Map<String, String>" />
  8. <variable name= "index" type= "int" />
  9. <variable name= "key" type= "String" />
  10. </data>
  11. android:text= "@{list[index]}"  
  12. android:text= "@{sparse[index]}"  
  13. android:text= "@{map[key]}"  

String

When single quotes are used to enclose attribute values, it is easy to use double quotes in expressions:

  1. android:text= '@{map["firstName"]}'  

It is also possible to use double quotes to enclose attribute values. The string must be enclosed in "`":

  1. android:text= "@{map[`firstName`]}"  
  2. android:text= "@{map[" firstName "]}"  
  3.  
  4. Resources

It is also possible to access resources using regular expressions:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

Format strings and pluralizations can be determined by providing parameters

  1. android:text= "@{@string/nameFormat(firstName, lastName)}"  
  2. android:text= "@{@plurals/banana(bananaCount)}"  

When a complex number requires multiple arguments, all arguments are passed:

  1. Have an orange
  2. Have %d oranges
  3. android:text= "@{@plurals/orange(orangeCount, orangeCount)}"  

Some resources require explicit typing:

Type Normal reference Expression reference

  1. String[] @array   @stringArray  
  2. int [] @array   @intArray  
  3. TypedArray @array   @typedArray  
  4. Animator @animator   @animator  
  5. StateListAnimator @animator   @stateListAnimator  
  6. color int   @color   @color  
  7. ColorStateList @color   @colorStateList  

5. Data Object

Any Plain old Java object (POJO) can be used for Data Binding, but modifying a POJO will not cause the UI to update. The real power of Data Binding is to notify your Data object when the data changes. There are three different data change notification mechanisms: Observable objects, ObservableFields, and observable collections.

When these observable Data objects are bound to the UI, the UI will automatically update when the Data object properties change.

Observable Object

The class that implements the android.databinding.Observable interface allows you to attach a listener to a Bound object in order to monitor changes to all properties on the object.

The Observable interface has a mechanism to add and remove listeners, but notifications are managed by the developer. To make development easier, a BaseObservable base class was created to implement the listener registration mechanism. The Data implementation class is still responsible for notifying when properties change. This is done by specifying a Bindable annotation to the getter and notifying in the setter.

  1. private static class User extends BaseObservable {
  2. private String firstName;
  3. private String lastName;
  4. @Bindable
  5. public String getFirstName() {
  6. return this.firstName;
  7. }
  8. @Bindable
  9. public String getFirstName() {
  10. return this.lastName;
  11. }
  12. public void setFirstName(String firstName) {
  13. this.firstName = firstName;
  14. notifyPropertyChanged(BR.firstName);
  15. }
  16. public void setLastName(String lastName) {
  17. this.lastName = lastName;
  18. notifyPropertyChanged(BR.lastName);
  19. }
  20. }

During compilation, the Bindable annotation generates an Entry in the BR class file. The BR class file is generated in the module package. If the base class used for the Data class cannot be changed, the Observable interface is implemented through a convenient PropertyChangeRegistry for storage and efficient notification of listeners.

Observable Fields

Little work is involved in creating the Observable class, so developers who want to save time or have few properties can use ObservableFields. ObservableFields are self-contained observable objects with a single field. It has all primitive types and one reference type. To use it, create a public final field in the data object:

  1. private   static   class User extends BaseObservable {
  2. public   final ObservableField<String> firstName =
  3. new ObservableField<>();
  4. public   final ObservableField<String> lastName =
  5. new ObservableField<>();
  6. public   final ObservableInt age = new ObservableInt();
  7. }

That's it, to access the value, use the set and get methods:

user.firstName.set("Google");

int age = user.age.get();

Observable Collections

Some apps use more dynamic structures to store data. Observable collections allow keyed access to these data objects. ObservableArrayMap is used when the keys are reference types, such as String.

  1. ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
  2. user.put( "firstName" , "Google" );
  3. user.put( "lastName" , "Inc." );
  4. user.put( "age" , 17 );

In the layout file, the map can be accessed through the String key:

  1. <data>
  2. < import type= "android.databinding.ObservableMap" />
  3. <variable name= "user" type= "ObservableMap<String, Object>" />
  4. </data>
  5. <TextView
  6. android:text= '@{user["lastName"]}'  
  7. android:layout_width= "wrap_content"  
  8. android:layout_height= "wrap_content" />
  9. <TextView
  10. android:text= '@{String.valueOf(1 + (Integer)user["age"])}'  
  11. android:layout_width= "wrap_content"  
  12. android:layout_height= "wrap_content" />
  13.  
  14. ObservableArrayList for keys that are integers:
  15.  
  16. ObservableArrayList<Object> user = new ObservableArrayList<>();
  17. user.add( "Google" );
  18. user.add( "Inc." );
  19. user.add( 17 );

In the layout file, the list can be accessed by index:

  1. <data>
  2. < import type= "android.databinding.ObservableList" />
  3. < import type= "com.example.my.app.Fields" />
  4. <variable name= "user" type= "ObservableList<Object>" />
  5. </data>
  6. <TextView
  7. android:text= '@{user[Fields.LAST_NAME]}'  
  8. android:layout_width= "wrap_content"  
  9. android:layout_height= "wrap_content" />
  10. <TextView
  11. android:text= '@{String.valueOf(1 + (Integer)user[Fields.AGE])}'  
  12. android:layout_width= "wrap_content"  
  13. android:layout_height= "wrap_content" />
  14.  
  15. 6. Binding generation

The generated Binding class links the variables in the layout with the Views. As discussed earlier, the name and package name of the Binding can be customized. The generated Binding class extends android.databinding.ViewDataBinding.

#p#

create

Bindings should be created right after inflation to ensure that the View hierarchy does not interfere with the previous expressions in the layout that bind to the views. There are several ways to bind to a layout. The most common is to use the static .inflate method on the Binding class to load the View hierarchy and bind to it. There is also a simpler version that only requires LayoutInflater and another one that uses ViewGroup:

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);

MyLayoutBinding binding = MyLayoutBinding.inflate(LayoutInflater, viewGroup, false);

If a different mechanism is used to load the layout, it can be bound separately:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

Sometimes the Binding cannot be known in advance. In this case, you can use the DataBindingUtil class to create the Binding:

  1. ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
  2. parent, attachToParent);
  3. ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

Views with IDs

For each View with an ID in the layout, a public final field is generated. Binding makes a single pass over the View hierarchy, extracting the Views with IDs. This mechanism is faster than using findViewById for some Views. For example:

  1. <layout xmlns:android= "http://schemas.android.com/apk/res/android" >
  2. <data>
  3. <variable name= "user" type= "com.example.User" />
  4. </data>
  5. <LinearLayout
  6. android:orientation= "vertical"  
  7. android:layout_width= "match_parent"  
  8. android:layout_height= "match_parent" >
  9. <TextView android:layout_width= "wrap_content"  
  10. android:layout_height= "wrap_content"  
  11. android:text= "@{user.firstName}"  
  12. android:id= "@+id/firstName" />
  13. <TextView android:layout_width= "wrap_content"  
  14. android:layout_height= "wrap_content"  
  15. android:text= "@{user.lastName}"  
  16. android:id= "@+id/lastName" />
  17. </LinearLayout>
  18. </layout>

It will generate the following Binding class:

public final TextView firstName;

public final TextView lastName;

IDs are not nearly as necessary as they would be without Data Bindings, but there will still be instances where you will need to access Views from your code.

Variables

Each Variable will have access methods.

  1. <data>
  2. < import type= "android.graphics.drawable.Drawable" />
  3. <variable name= "user" type= "com.example.User" />
  4. <variable name= "image" type= "Drawable" />
  5. <variable name= "note" type= "String" />
  6. </data>

It will generate setters and getters in Binding:

  1. public   abstract com.example.User getUser();
  2. public   abstract   void setUser(com.example.User user);
  3. public   abstract Drawable getImage();
  4. public   abstract   void setImage(Drawable image);
  5. public   abstract String getNote();
  6. public   abstract   void setNote(String note);

ViewStubs

ViewStubs are slightly different from normal Views. They start out invisible, and when they are either made visible or explicitly told to load, they replace themselves by loading another layout.

Since the ViewStub essentially disappears from the View hierarchy, the View in the Binding object must also disappear to allow it to be collected. Since Views are private, a ViewStubProxy object takes a ViewStub, giving the developer access to the ViewStub when it exists and also access to the loaded View hierarchy when the ViewStub is loaded.

When another layout is loaded, a Binding must be created for the new layout. Therefore, the ViewStubProxy must listen to the ViewStub's OnInflateListener listener and create the Binding at that time. Since only one can exist, the ViewStubProxy allows developers to set an OnInflateListener on it which will be called after the Binding is created.

Advanced Binding

Dynamic Variables

Sometimes, the specific Binding class is not known, for example, a RecyclerView adapter does arbitrary operations on layouts and does not know the specific Binding class. It still has to be assigned to the Binding during onBindViewHolder.

In this example, all layouts bound to the RecyclerView have an "item" Variable. The BindingHolder has a getBinding method that returns a ViewDataBinding.

  1. public   void onBindViewHolder(BindingHolder holder, int position) {
  2. final T item = mItems.get(position);
  3. holder.getBinding().setVariable(BR.item, item);
  4. holder.getBinding().executePendingBindings();
  5. }

Direct Binding

When a variable or observable changes, the binding is scheduled to change before the next frame. There are many times, but it must be executed immediately when binding. To force execution, use the executePendingBindings() method.

Background Threads

You can change your data model in a background thread as long as it is not a collection. Data Binding will localize each variable/field when deciding whether to avoid any concurrency issues.

7. Property Setters

Whenever the binding value changes, the generated Binding class must call the setter method. The Data Binding framework has methods that can be used to customize the assignment.

Automatic Setters

For an attribute, Data Binding tries to find the setAttribute method. It has nothing to do with the namespace of the attribute, but only with the name of the attribute itself.

For example, an expression for the android:text attribute of a TextView will look for a setText(String) method. If the expression returns an int, Data Binding will search for a setText(int) method. Note: To make the expression return the correct type, use casting if necessary. Data Binding will still work even if no attribute with the given name exists. You can then easily "invent" attributes for any setter through Data Binding. For example, DrawerLayout does not have any attributes, but a large number of setters. You can use one of them using automatic setters.

  1. <android.support.v4.widget.DrawerLayout
  2. android:layout_width= "wrap_content"  
  3. android:layout_height= "wrap_content"  
  4. app:scrimColor= "@{@color/scrim}"  
  5. app:drawerListener= "@{fragment.drawerListener}" />

Renamed Setters

Some properties that have setters do not match by name. For these methods, the property can be associated via the BindingMethods annotation. This must be associated with a class containing a BindingMethod annotation, one for each renamed method. For example, the android:tint attribute is associated with setImageTintList, not setTint.

  1. @BindingMethods ({
  2. @BindingMethod (type = "android.widget.ImageView" ,
  3. attribute = "android:tint" ,
  4. method = "setImageTintList" ),
  5. })

In the above example, it is unlikely that the developer will need to rename the setters, as this is already implemented by the Android architecture attributes.

Custom Setters

Some properties require custom binding logic. For example, there is no setter for the android:paddingLeft property. Instead, setPadding(left, top, right, bottom) exists. A static binding adapter method annotated with BindingAdapter allows developers to customize how the setter is called for a property.

Android has created BindingAdapters with properties. For example, for paddingLeft:

  1. @BindingAdapter ( "android:paddingLeft" )
  2. public   static   void setPaddingLeft(View view, int padding) {
  3. view.setPadding(padding,
  4. view.getPaddingTop(),
  5. view.getPaddingRight(),
  6. view.getPaddingBottom());
  7. }

Binding adapters are useful for other custom types. For example, a custom loader can be used to load images asynchronously.

When there is a conflict, the Binding adapter created by the developer will override the Data Binding default adapter.

You can also create adapters that can receive multiple parameters.

  1. @BindingAdapter ({ "bind:imageUrl" , "bind:error" })
  2. public   static   void loadImage(ImageView view, String url, Drawable error) {
  3. Picasso.with(view.getContext()).load(url).error(error).into(view);
  4. }
  5.  
  6. <ImageView app:imageUrl="@{venue.imageUrl}"
  7. app:error="@{ @drawable /venueError}"/>

This adapter is called if both imageUrl and error are used for an ImageView and imageUrl is a string and error is a drawable.

Custom namespaces will be ignored during the matching process.

You can also write adapters for Android namespaces.

#p#

8. Conversion

Object conversion

When returning an object from a Binding expression, a setter is selected from automatic, renamed, and custom setters. The object is converted to the parameter type of the selected setter.

This is for convenience of those using ObservableMaps to store data. For example:

  1. <TextView
  2. android:text= '@{userMap["lastName"]}'  
  3. android:layout_width= "wrap_content"  
  4. android:layout_height= "wrap_content" />

In userMap an object is returned and the object will be automatically converted to the parameter type of setText(CharSequence). When there may be confusion about the parameter type, the developer needs to convert in the expression.

Custom Transformations

Sometimes conversions should be automatic between certain types. For example, when setting a background:

  1. <View
  2. android:background= "@{isError ? @color/red : @color/white}"  
  3. android:layout_width= "wrap_content"  
  4. android:layout_height= "wrap_content" />

Here, the background requires a Drawable object, but the color is an integer. Whenever there is a Drawable and the return value is an integer, the integer type is converted to a ColorDrawable. This conversion is done by using a static method annotated with BindingConversion:

  1. @BindingConversion  
  2. public   static ColorDrawable convertColorToDrawable( int color) {
  3. return   new ColorDrawable(color);
  4. }

Note: Conversion only happens at the setter level, so it is not allowed to mix the following types:

  1. <View
  2. android:background= "@{isError ? @drawable/error : @color/white}"  
  3. android:layout_width= "wrap_content"  
  4. android:layout_height= "wrap_content" />

<<:  How to become a top coder in three years

>>:  10 design principles that developers should know

Recommend

A famous actress lost 100 pounds. Can we copy her weight loss journey?

Review expert: Shen Yingjian, Director of the Nut...

Brand promotion: Why must 50% of advertising costs be wasted?

Is it necessary to make every penny of advertisin...

14 seconds! China's own CPU + operating system achieves a leap forward

1 second, 2 seconds, 3 seconds... Several "c...

In the Internet age, let users help you build and promote your brand

People of different eras created big brands that ...

The most complete! New product launch event process planning plan!

The most important thing about an event is to hav...