The Data Binding framework was one of Google’s announcements at I/O 2015, it’s a big change in the code organization of an Android app. Some developers are sceptical about this framework but, if used in the “right way”, it’s very powerful and it allows to remove a lot of redundant boilerplate code from activities and fragments.
In this talk we’ll start from the Data Binding basic concepts and then we’ll see how to use it to improve the architecture of a typical Android application applying the Model View ViewModel pattern. Using this pattern you need to write less code to create an app that can be easily tested using JVM and instrumentation tests.
6. 6
public class TeamScore {
private final String name;
private final int goals;
//constructor and getters
}
public class MatchResult {
private final TeamScore homeTeam;
private final TeamScore awayTeam;
private final String gifUrl;
//constructor and getters
}
14. <LinearLayout style="@style/root_layout"
xmlns:android=“http://schemas.android.com/apk/res/android">
<ImageView android:id="@+id/result_gif" style="@style/gif"/>
<LinearLayout style="@style/team_layout">
<TextView android:id="@+id/home_team" style="@style/name"/>
<TextView android:id="@+id/home_goals" style="@style/goals"/>
</LinearLayout>
<LinearLayout style="@style/team_layout">
<TextView android:id="@+id/away_team" style="@style/name"/>
<TextView android:id="@+id/away_goals" style="@style/goals"/>
</LinearLayout>
</LinearLayout>
14
One layout traversal
match_result.xmlMatchResultBinding.java
Auto generated class
<?xml version="1.0" encoding="utf-8"?>
<layout>
</layout>
public class MatchResultBinding extends
android.databinding.ViewDataBinding {
// ...
public final android.widget.ImageView resultGif;
public final android.widget.TextView homeTeam;
public final android.widget.TextView homeGoals;
public final android.widget.TextView awayTeam;
public final android.widget.TextView awayGoals;
// ...
}
24. Annotated methods are static but…
@BindingAdapter("something")
public static void bindSomething(View view, AnyObject b) {
MyBinding binding = DataBindingUtil.findBinding(view);
MyObject myObject = binding.getMyObject();
//…
TextView myTextView =
binding.myTextView;
//…
}
Can be any object
Get the layout binding
Get the connected objects
Access to all the views
Can be defined anywhere Can be used everywhere
Can be any View
25. 25
BindingConversion
@BindingConversion
public static @ColorRes int convertEnumToColor(MyEnum value) {
switch (value) {
case VALUE1:
return R.color.color1;
case VALUE2:
return R.color.color2;
case VALUE3:
return R.color.color3;
default:
return R.color.color4;
}
}
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@{myObject.myEnum}"/>
44. public String getMessage() {
return message;
}____
public boolean isMessageAvailable() {
return messageAvailable;
}___
public void setMessage(String message) {
this.message = message;
}__
public void setMessageAvailable(boolean messageAvailable) {
this.messageAvailable = messageAvailable;
}_
public class ContactInfo
private String message;
private boolean messageAvailable;
44
extends BaseObservable {
}
notifyPropertyChanged(BR.message);
notifyPropertyChanged(BR.messageAvailable);
@Bindable
@Bindable
45. 45
public class ContactInfo {
public final ObservableField<String> message = new ObservableField<>();_
public final ObservableBoolean messageAvailable = new ObservableBoolean();_
}__
46. 46
public class ContactInfo {
public final ObservableField<String> message = new ObservableField<>();_
public final ObservableBoolean messageAvailable = new ObservableBoolean();_
}__
public class ContactActivity extends AppCompatActivity {
private ContactInfo contactInfo;
private ContactBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,
R.layout.contact);
contactInfo = new ContactInfo();
binding.setInfo(contactInfo);
binding.getRoot().postDelayed(() -> {
contactInfo.message.set("my message");
contactInfo.messageAvailable.set(true);
}, 2000);
}___
public void send(View view) {
Snackbar.make(binding.getRoot(),
contactInfo.message.get(), LENGTH_LONG).show();
}__
}_
47. <layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="info"
type="it.droidcon.databinding.ContactInfo" />
</data>
<LinearLayout style="@style/contact_root">
<EditText
style="@style/contact_text"
android:enabled="@{info.messageAvailable}"
android:text="@{info.message}" />
<Button
style="@style/contact_button"
android:enabled="@{info.messageAvailable}"
android:onClick="send" />
</LinearLayout>
</layout>
47
public class ContactInfo {
public final ObservableField<String> message = new ObservableField<>();_
public final ObservableBoolean messageAvailable = new ObservableBoolean();_
}__
ObservableField<String>
ObservableBoolean
ObservableBoolean
49. 49
Two way Data Binding
@BindingAdapter("binding")
public static void bindEditText(EditText view,
final ObservableString observable) {
Pair<ObservableString, TextWatcherAdapter> pair =
(Pair) view.getTag(R.id.bound_observable);
if (pair == null || pair.first != observable) {
if (pair != null)
view.removeTextChangedListener(pair.second);
TextWatcherAdapter watcher = new TextWatcherAdapter() {
@Override
public void onTextChanged(CharSequence s, int a, int b, int c) {
observable.set(s.toString());
}
};
view.setTag(R.id.bound_observable, new Pair<>(observable, watcher));
view.addTextChangedListener(watcher);
}
String newValue = observable.get();
if (!view.getText().toString().equals(newValue))
view.setText(newValue);
}
medium.com/@fabioCollini/android-data-binding-f9f9d3afc761
56. 56
MatchResultViewModel
public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading =
new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}
}
62. <LinearLayout style="@style/root_layout"
android:onClick="@{???}">
<!-- ... -->
</LinearLayout>
62
}___
public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
63. public final_View.OnClickListener reloadClickListener =
new View.OnClickListener() {
@Override public void onClick(View v) {
reload();
}_
};
<LinearLayout style="@style/root_layout"
android:onClick="@{viewModel.reloadClickListener}">
<!-- ... -->
</LinearLayout>
63
}___
public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
64. <LinearLayout style="@style/root_layout"
android:onClick="@{viewModel::reload}">
<!-- ... -->
</LinearLayout>
64
}___
public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload(View v) {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
65. <LinearLayout style="@style/root_layout"
android:onClick="@{v -> viewModel.reload()}">
<!-- ... -->
</LinearLayout>
65
}___
public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
66. public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
66
}___
@BindingAdapter("android:onClick")
public static void bindOnClick(View view, final Runnable listener) {
view.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.run();
}____
});
}___
<LinearLayout style="@style/root_layout"
android:onClick="@{v -> viewModel.reload()}">
<!-- ... -->
</LinearLayout>
67. public class MatchResultViewModel {
public final ObservableField<MatchResult> result =
new ObservableField<>();
public final ObservableBoolean loading = new ObservableBoolean();
public void reload() {
loading.set(true);
reloadInBackground(result -> {
loading.set(false);
this.result.set(result);
});
}__
<LinearLayout style="@style/root_layout"
android:onClick="@{viewModel::reload}">
<!-- ... -->
</LinearLayout>
67
}___
@BindingAdapter("android:onClick")
public static void bindOnClick(View view, final Runnable listener) {
view.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.run();
}____
});
}___
76. 76
Data binding
You can write all your business logic
in an huge xml file
————————————————————————
————————————
77. Custom attributes Reusable UI code
77
Data binding
You can write all your business logic
in an huge xml file
————————————————————————
————————————
Includes UI components
MVVM Testable code