PS: the original text was first published on wechat official account: Jingxing zhi (Jzman-blog)
The Jetpack component series for Android is as follows:
- Lifecycle for Android Jetpack components
- LiveData for Android Jetpack
- ViewModel for Android Jetpack components
- Android Jetpack component DataBinding details
This article mainly introduces how to use Binding Adapters as follows:
- Databinding mechanism
- BindingMethods
- BindingAdapter
- BindingConversion
Databinding mechanism
Binding Adapters can be used as a framework to set a value. The Databinding library allows you to specify a specific method to set the value. In this method, you can do some processing logic. So how do we call the corresponding property methods when we use databinding binding data in a layout file?
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}" />
Copy the code
When binding data to a layout file, such as the text property of the TextView above, the binding automatically receives methods for arguments of compatible types, such as setText(ARG), The Databinding library looks for the user.setName(arg) method that receives the user.getName() return type. If user.getName() returns a string, The setName(arg) method of String is called, whereas the setName(arg) method of int is called. So, to ensure that the data is correct, try to ensure that the value returned in the XML expression is correct. Of course, You can also perform type conversions as needed.
If you set a property in a layout file, the Databinding library automatically looks for a setter method to set it. If you set a property in a TextView, for example, you need to find a setter method to validate it. TextView has a setError(error) method like this:
@android.view.RemotableViewMethod
public void setError(CharSequence error) {
if (error == null) {
setError(null.null);
} else {
Drawable dr = getContext().getDrawable(
com.android.internal.R.drawable.indicator_input_error);
dr.setBounds(0.0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
setError(error, dr);
}
}
Copy the code
This method is mainly used to prompt error messages, which are usually used in code. Here we configure this method to be used in the layout file, as follows:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name,default=name}"
app:error="@{user.name}"/>
Copy the code
Below is the test effect:
Because of the setError(String error) method and the String returned by user.name, this can be configured as an attribute here.
BindingMethods
This is an annotation provided by the Databinding library to map a property in a View to its corresponding setter method name, For example, android:textColorHint is the same method as setHintTextColor. In this case, the name of the property is inconsistent with the name of the corresponding setter method. This requires binding the property to the corresponding setter method using the BindingMethods annotation so that databinding can find the corresponding setter method based on the property value. Databinding already handles cases where properties like this do not match setter methods in a native View.
@BindingMethods({
@BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),
@BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),
@BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"),
@BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),
@BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),
@BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"),
@BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"),
})
public class TextViewBindingAdapter {
/ /...
}
Copy the code
So, for some properties in the View in the Android framework, the Databinding library has already done automatic attribute lookup matching using BindingMethods, so when there is no setter method for some properties, How do you customize setter methods when using databinding? That’s when you use BindingAdapter.
BindingAdapter
- Property setting preprocessing
We can use BindingAdapter when we need to customize the processing logic for certain attributes. For example, we can use BindingAdapter to redefine the setText method of TextView to convert all the English input to lowercase. Customize TextViewAdapter as follows:
/ * *
* Custom BindingAdapters
* Powered by jzman.
* Created on 2018/12/6 0006.
* /
public class TextViewAdapter {
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
// omit special processing...
String txt = text.toString().toLowerCase();
view.setText(txt);
}
}
Copy the code
At this point, when we use databinding to use our own BindingAdapter first, we might wonder why we can recognize it. During compilation, the data-binding compiler looks for methods with @bindingAdapter annotations, The custom setter method is eventually generated into the corresponding Binding class, with the following code generated:
@Override
protected void executeBindings(a) {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
// batch finished
if ((dirtyFlags & 0x2L) != 0) {
// api target 1
// Note: this is a custom TextViewAdapter
com.manu.databindsample.activity.bindingmethods.TextViewAdapter.setText(this.tvData, "This is a TextView");
}
}
Copy the code
To verify the use of BindingAdapter as an example, create a layout file as follows:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<! - the default TextView -- -- >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#a37c7c"
android:text="This is a TextView..."
android:textSize="16sp" />
<! -- Use dataBinding TextView-->
<TextView
android:id="@+id/tvData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#a37c7c"
android:text="@ {` TextView is this... `}"
android:textSize="16sp" />
</LinearLayout>
</layout>
Copy the code
Using a custom BindingAdapter looks like this:
TextViewAdapter (BindingAdapter); TextViewAdapter (BindingAdapter);
- Custom property Settings
Custom attribute Settings can define a single attribute or multiple attributes. To define a single attribute, see the following:
/ * *
* Custom BindingAdapters
* Powered by jzman.
* Created on 2018/12/7 0007.
* /
public class ImageViewAdapter {
/ * *
* Define a single attribute
* @param view
* @param url
* /
@BindingAdapter("imageUrl")
public static void setImageUrl(ImageView view, String url) {
Glide.with(view).load(url).into(view);
}
}
Copy the code
At this point we can use the custom attribute imageUrl in the layout file as follows:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<! Customize a single attribute -->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"/>
</LinearLayout>
</layout>
Copy the code
The above code test results are as follows:
This makes it easy to use the imageUrl attribute to load web images. Don’t worry about thread switching. The Databinding library does this automatically.
You can customize multiple attributes as follows:
/ * *
* Custom BindingAdapters
* Powered by jzman.
* Created on 2018/12/7 0007.
* /
public class ImageViewAdapter {
/ * *
* Define multiple attributes
* @param view
* @param url
* @param placeholder
* @param error
* /
@BindingAdapter(value = {"imageUrl"."placeholder"."error"})
public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {
RequestOptions options = new RequestOptions();
options.placeholder(placeholder);
options.error(error);
Glide.with(view).load(url).apply(options).into(view);
}
}
Copy the code
At this point, you can use the three properties defined above in the layout file, namely imageUrl, placeholder, error, as follows:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<! -- Customize multiple attributes -->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"
app:placeholder="@{@drawable/icon}"
app:error="@{@drawable/error}"/>
</LinearLayout>
</layout>
Copy the code
If the BindingAdapter uses all three attributes, then the BindingAdapter will not work properly. If the BindingAdapter uses all three attributes, then the BindingAdapter will not work properly. The @bindingAdapter annotation also has a requireAll parameter. RequireAll defaults to true, indicating that all properties must be used. The requireAll attribute of @bindAdapter should be set to false, as shown below:
// requireAll = false
@BindingAdapter(value = {"imageUrl"."placeholder"."error"},requireAll = false)
public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {
RequestOptions options = new RequestOptions();
options.placeholder(placeholder);
options.error(error);
Glide.with(view).load(url).apply(options).into(view);
}
Copy the code
In this case, the layout file can use some attributes, such as imageUrl and placeholder, and there will be no compilation errors:
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"
app:placeholder="@{@drawable/icon}"/>
Copy the code
So much for the introduction of BindingAdapter.
BindingConversion
In some cases, conversion between types is required when a property is set. This can be done with the @bindingConversion annotation, For example, android:background accepts a Drawable. When we set a color value in databinding’s expression, we need @bindingConversion, creating the layout file as follows:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data> </data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<! -- Type conversion -->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@{true ? @color/colorRed : @color/colorBlue}"/>
</LinearLayout>
</layout>
Copy the code
Type conversion using @bindingConversion as follows:
/ * *
* Type conversion you
* Powered by jzman.
* Created on 2018/12/7 0007.
* /
public class ColorConversion {
@BindingConversion
public static ColorDrawable colorToDrawable(int color){
return new ColorDrawable(color);
}
}
Copy the code
The above code test results are as follows:
Use the same type when using @bindingConversion annotations. The Android :background attribute above cannot be used like this:
<! -- Type conversion -->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@{true ? @color/colorRed : @drawable/drawableBlue}"/>
Copy the code
Either the BindingAdapter or the BindingConversion will eventually generate the relevant code into the corresponding Binding class, and then set its value to the specified View. So much for BindingMethods, BindingAdapter, and BingingConversion.