Would you like to make your Android UI code cleaner and more reactive? Android data binding can help. In this talk you’ll learn everything you need to know about data binding, including why it’s so powerful and how to use it effectively. If you haven’t tried data binding in the past, that’s okay! We’ll start with the basics, assuming no prior knowledge and slowly move into more advanced topics, such as 2-way binding, binding adapters, converters, best practices and common pitfalls to avoid.
Android Application Components with Implementation & Examples
Effective Android Data Binding
1. Effective Android Data Binding
Eric Maxwell
Product Engineer @ Realm
Android, iOS Developer
2. What are you going to learn?
• Introduction & Getting Started
• Data Binding Features & Use Cases
• Data Binding + MVVM
• ViewModels and LiveData
• Tips from using Data Binding in Production
4. Data Binding Intro
• Support Library to bind data directly to your views
• Views can automatically update when underlying data changes
• Views can connect directly to actions
DATA
TADA
Data
Data
Data
10. Getting Started
Example Covers
• How to enable data binding
• Building Layouts
• Binding View Model
• Data
• Actions
https://github.com/ericmaxwell2003/DataBindingDemos/
19. Inside <data> Tag
<data>
<!—- (Define 0..* variables) -—>
<variable name="viewModel" type="com.acme.ViewModel" />
<!—- (Define 0..* imports) -—>
<import type="android.view.View" />
</data>
Variables
• Define name and type of variable
• Value set by hosting Activity/Fragment
Imports
• Define imports similar to imports for in classes
• Import static -Utils classes, View, etc.
• Example usages
android:visibility="@{someCondition ? View.VISIBLE : View.GONE}"
android:text='@{StringUtils.suffixWithAwesome("Everything is ")'
26. ‘@{viewModel.value}'
Where Value will resolve to (in order of precedence)
• getValue() // Property Accessor
• value() // Method with that name
• value // Public property
Expression Resolution
33. public class CounterViewModel extends BaseObservable {
private int count = 0;
public void increment() {
setCount(getCount() + 1);
}
public void decrement() {
setCount(getCount() - 1);
}
@Bindable
public int getCount() {
return count;
}
private void setCount(int count) {
this.count = count;
notifyPropertyChanged(BR.count);
}
}
Making Data Observable
Base
Observable
Observable
ViewModel
<<Observable>>
Counter
ViewModel
34. public class CounterViewModel extends BaseObservable {
private int count = 0;
public void increment() {
setCount(getCount() + 1);
// perform many other operations
// update many data bound variables
notifyChange();
}
public void decrement() {
setCount(getCount() - 1);
}
@Bindable
public int getCount() {
return count;
}
private void setCount(int count) {
this.count = count;
}
}
Making Data Observable
Base
Observable
Observable
ViewModel
<<Observable>>
Counter
ViewModel
35. public interface Observable {
void addOnPropertyChangedCallback(...);
void removeOnPropertyChangedCallback(...);
public abstract static class OnPropertyChangedCallback {
public abstract void onPropertyChanged(...);
}
}
Making Data Observable
Base
Observable
Observable
ViewModel
<<Observable>>
Counter
ViewModel
36. /**
* Bridge class which does everything android.databinding.BaseObservable
does plus
* extends Android Architecture Components ViewModel
*/
public abstract class ObservableViewModel
extends ViewModel
implements Observable {
...
}
How to make an Observable ViewModel
Base
Observable
Observable
ViewModel
<<Observable>>
Counter
ViewModel
37. public class CounterViewModel extends ObservableViewModel {
private int count = 0;
public void increment() {
setCount(getCount() + 1);
}
public void decrement() {
setCount(getCount() - 1);
}
@Bindable
public int getCount() {
return count;
}
private void setCount(int count) {
this.count = count;
notifyPropertyChanged(BR.count);
}
}
Base
Observable
Observable
ViewModel
<<Observable>>
Counter
ViewModel
How to make an Observable ViewModel
38. public class CounterViewModel extends ViewModel {
public ObservableInt count = new ObservableInt(0);
public void increment() {
count.set(count.get() + 1);
}
public void decrement() {
count.set(count.get() - 1);
}
}
Other Options
39. Other Options
Observable Primitives: ObservableInt, ObservableBoolean, ObservableByte, ObservableChar, ObservableDouble, ObservableFloat
Observable Collections: ObservableArrayMap, ObservableArrayList
public class CounterViewModel extends ViewModel {
public ObservableInt count = new ObservableInt(0);
public void increment() {
count.set(count.get() + 1);
}
public void decrement() {
count.set(count.get() - 1);
}
}
40. Other Options
Observable Primitives: ObservableInt, ObservableBoolean, ObservableByte, ObservableChar, ObservableDouble, ObservableFloat
Observable Collections: ObservableArrayMap, ObservableArrayList
public class CounterViewModel extends ViewModel {
public ObservableInt count = new ObservableInt(0);
public void increment() {
count.set(count.get() + 1);
}
public void decrement() {
count.set(count.get() - 1);
}
}
Best used for simpler views
with only a few observable fields
41. public class CounterViewModel extends ObservableViewModel {
private int count = 0;
public void increment() {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
count++;
notifyPropertyChanged(BR.countLabel);
Log.i(TAG, "Counter [" + count + "], on bg thread");
}
});
}
...
}
Update can happen in background threads
You can change your data model in a background thread as long as it is not a collection.
https://developer.android.com/topic/libraries/data-binding/index.html#advanced_binding
52. Binding Adapters
@BindingAdapter({"app:imageUrl"})
public static void loadImage(View view, String url) {
Picasso.with(view.getContext())
.load(url)
.into((ImageView)view);
}
<ImageView
android:id="@+id/imageView"
tools:src="@drawable/design_time_sample_image"
app:imageUrl=‘@{"http://acme.com/images/any.png"}' />
Applies to all Views
53. Binding Adapters
@BindingAdapter({"app:imageUrl"})
public static void loadImage(TextView view, String url) {
}
<ImageView
android:id="@+id/imageView"
tools:src="@drawable/design_time_sample_image"
app:imageUrl=‘@{"http://acme.com/images/any.png"}' />
Binding Adapters allow you to add custom attributes to views
Cannot find the setter for attribute 'app:imageUrl' with parameter type java.lang.String on android.widget.ImageView
54. Binding Adapters
@BindingAdapter({"app:imageUrl"})
public static void loadImage(ImageView view, String url) {
Picasso.with(view.getContext())
.load(url)
.into(view);
}
<ImageView
android:id="@+id/imageView"
tools:src="@drawable/design_time_sample_image"
app:imageUrl=‘@{"http://acme.com/images/any.png"}' />
Value of expression
Must be an expression!
56. @BindingAdapter({"android:visibility"})
public static void visibility(final View view,
final int newVisibility) {
// Do something like animate or log visibility change,…
view.setVisibility(newVisibility);
}
Override System Attribute
Good example @
https://medium.com/google-developers/android-data-binding-animations-55f6b5956a64
62. 2 Way Data Binding
public class LoginViewModel extends ViewModel {
...
public MutableLiveData<State> state = new MutableLiveData<>();
public MutableLiveData<String> error = new MutableLiveData<>();
public void login() {
state.postValue(State.ATTEMPTING_LOGIN);
updateLastUsedUsername();
loginService.attemptLogin(username, password,
new FakeLoginService.LoginCallback() {
@Override
public void onSuccess(FakeLoginService.User user) {
state.postValue(State.AUTHENTICATED);
}
@Override
public void onFailure(Throwable e) {
error.postValue("Login Error!!");
state.postValue(State.WAITING_USER);
}
});
}
}
63. 2 Way Data Binding
public class LoginViewModel extends ViewModel {
...
public MutableLiveData<State> state = new MutableLiveData<>();
public MutableLiveData<String> error = new MutableLiveData<>();
public void login() {
state.postValue(State.ATTEMPTING_LOGIN);
updateLastUsedUsername();
loginService.attemptLogin(username, password,
new FakeLoginService.LoginCallback() {
@Override
public void onSuccess(FakeLoginService.User user) {
state.postValue(State.AUTHENTICATED);
}
@Override
public void onFailure(Throwable e) {
error.postValue("Login Error!!");
state.postValue(State.WAITING_USER);
}
});
}
}
64. Observing Live Data
viewModel.state.observe(this, new Observer<LoginViewModel.State>() {
@Override
public void onChanged(@Nullable LoginViewModel.State state) {
if(state == null) { return; }
switch (state) {
case WAITING_USER:
hideProgress();
break;
case ATTEMPTING_LOGIN:
showProgress();
break;
case AUTHENTICATED:
authSuccessful();
break;
}
}
});
65. Observing Live Data
viewModel.error.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String errorMessage) {
if(!TextUtils.isEmpty(errorMessage)) {
showMessage(errorMessage);
}
}
});
67. Tips
1. Avoid adding logic to your expressions, put that in the ViewModel
android:text='@{v.someCondition() ? @string/foo : @string/boo}'
android:onClick='@{() -> someCondition() ? doSomething() : void}'
68. 1. Avoid adding logic to your expressions, put that in the ViewModel
2. Data Binding works really well with MVVM
Tips
69. 1. Avoid adding logic to your expressions, put that in the ViewModel
2. Data Binding works really well with MVVM
3. Make heavy use of tools attributes so you can see an example in your preview
android:text=‘@{String.valueOf(vm.count)}’
tools:text=‘0’
Tips
70. 1. Avoid adding logic to your expressions, put that in the ViewModel
2. Data Binding works really well with ViewModels
3. Make heavy use of tools attributes so you can see an example in your preview
4. Custom attributes (BindingAdapters) can greatly simplify creating your views
Tips
71. 1. Avoid adding logic to your expressions, put that in the ViewModel
2. Data Binding works really well with ViewModels
3. Make heavy use of tools attributes so you can see an example in your preview
4. Custom attributes can greatly simplify creating your views, use them!
5. Run often in development cycles, it can be difficult to track down build time
errors in your layouts
•See tip #1
Tips
72. Additional Materials
• Other great talk on Data Binding
• https://academy.realm.io/posts/droidkaigi-kevin-pelgrims-data-real-world-data-binding/
• https://academy.realm.io/posts/360-andev-2017-ben-mccanny-data-binding/
• https://academy.realm.io/posts/data-binding-android-boyar-mount/
• Great how to articles
• https://medium.com/@georgemount007
• Samples
• https://github.com/ericmaxwell2003/DataBindingDemos
• https://github.com/ericmaxwell2003/ticTacToe/tree/mvvm