Those who are free of resentful thoughts surely find peace. - Buddha
Posted on 27th Nov 2017
Data binding means that data from data sources is bound to data consumers. For us this usually means that data from local storage or network is bound to layouts. Also, an important feature of data binding is that data changes are automatically synchronized between sources and consumers.
TextView textView = (TextView) findViewById(R.id.label);
EditText editText = (EditText) findViewById(R.id.userinput);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);
editText.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override public void afterTextChanged(Editable s) { }
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
model.setText(s.toString());
}
});
textView.setText(model.getLabel());
progressBar.setVisibility(View.GONE);
We’ve all been writing this kind of code. Lots of findViewById() calls and later many calls to setters and listeners and so on. Even with libraries like ButterKnife it doesn’t really get better. With the data binding library, this is a thing of the past.
A binding class is created on compile time for you, which provides all views with an ID as fields. This means no more findViewById(). Actually it’s faster than manually callingfindViewById() multiple times, because the data binding library creates code that traverses the view hierarchy only once.
The binding class also implements the binding logic from the layout files, so all those setters are actually called in the binding class. You don’t have to care about it anymore. In short, this means less code in your activities, fragments and view holders.
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
...
dataBinding {
enabled = true
}
...
}
dataBinding { enabled = true }
to your app’s build.gradle. This tells the build system to enable additional processing for data binding, like creating the binding classes from your layout files.<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="vm" type="com.softhinkers.ui.main.MainViewModel" />
<import type="android.view.View" />
</data>
...
</layout>
<TextView
android:id="@+id/my_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{vm.visible ? View.VISIBLE : View.GONE}">
android:padding="@{vm.bigPadding ? @dimen/paddingBig : @dimen/paddingNormal}"
android:text='@{vm.text ?? @string/defaultText + "Additional text."}' />
android:text="@{vm.text}"
android:visibility="@{vm.visibility}"
android:paddingLeft="@{vm.padding}"
android:layout_marginBottom="@{vm.margin}"
app:adapter="@{vm.adapter}"
setText()
method in your view’s class with the right parameter type (in this case String).app:adapter
attribute on a recycler view in the xml layout to set the adapter via data binding.@BindingAdapter("android:layout_marginBottom")
public static void setLayoutMarginBottom(View v, int bottomMargin) {
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) v.getLayoutParams();
if (layoutParams != null) {
layoutParams.bottomMargin = bottomMargin;
}
}
@BindingAdapter
annotation, which takes the layout attribute name as argument, for which the binding adapter should be called. Above you see a binding adapter for layout_marginBottom.@BindingAdapter({"imageUrl", "placeholder"})
public static void setImageFromUrl(ImageView v, String url, int drawableId) {
Picasso.with(v.getContext().getApplicationContext())
.load(url)
.placeholder(drawableId)
.into(v);
}
It’s also possible to require multiple attributes to be set for a binding adapter to be called. To achieve this, provide your list of required attribute names to the
@BindingAdapter annotation. Also, each of these attributes now have a typed parameter in the method. These BindingAdapters are only called, when all declared attributes are set.
MyBinding binding;
// For Activity
binding = DataBindingUtil.setContentView(this, R.layout.layout);
// For Fragment
binding = DataBindingUtil.inflate(inflater, R.layout.layout, container, false);
// For ViewHolder
binding = DataBindingUtil.bind(view);
// Access the View with ID text_view
binding.textView.setText(R.string.sometext);
// Setting declared variables
binding.set<VariableName>(variable);
DataBindingUtil.setContentView()
, for fragments use inflate()
and for view holders use bind()
. As already mentioned, the binding class provides all views which have an ID defined as final fields. Also, you set the variables you declared in the layout files on the binding object.One of the benefits of data binding is that the layout can be updated automatically by the library when data changes. However, the library still needs to be notified of data changes. This is accomplished by having the variables you set on the binding implement the Observable interface (don’t confuse this with the RxJava Observable).
For simple data types like int or boolean, the library already provides appropriate types which implement Observable, for example ObservableBoolean. Also, there is an ObservableField type for use with other objects, like strings.
public class MyViewModel extends BaseObservable {
private Model model = new Model();
public void setModel(Model model) {
this.model = model;
notifyChange();
}
public void setAmount(int amount) {
model.setAmount(amount);
notifyPropertyChanged(BR.amount);
}
@Bindable public String getText() { return model.getText(); }
@Bindable public String getAmount() { return Integer.toString(model.getAmount()); }
}
setModel()
method, we can then update the whole layout at once when the model changes by calling notifyChange()
.setAmount()
, you see that only one property of our model is changed. In this case, we do not want the whole layout to be updated, but just those parts that use this exact property. To achieve this, the corresponding getters of the property can be annotated with @Bindable
. Then, a field in the BR class is generated, which can be passed into the notifyPropertyChanged()
method. With this, the data binding library only updates those parts of your layout that actually depend on the changed property.Good, better, best. Never let it rest. Untill your good is better and your better is best. - St. Jerome