SlideShare a Scribd company logo
#droidconUK
Android Data Binding in action
using MVVM pattern
Fabio Collini
droidcon London
October 2016
2
Ego slide
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
codingjam.it
3
Agenda
1. Data Binding basics
2. Custom attributes
3. Components
4. Two Way Data Binding
5. Data Binding + RxJava
6. Model View ViewModel
#droidconUK - London - October 2016 - @fabioCollini 4
1Data Binding basics
Google I/O 2016
5
Google I/O 2015
6
github.com/fabioCollini/DataBindingInAction
7
match_result.xml
<?xml version="1.0" encoding="utf-8"?>

<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>
8
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

}
dataBinding {

enabled = true

}

9
build.gradle
android {

//...

//...



defaultConfig {

//...

____}

buildTypes {

//...

____}
}
<?xml version="1.0" encoding="utf-8"?>

<layout>

<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>

</layout>
10
Data Binding layout
<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>
<?xml version="1.0" encoding="utf-8"?>
<layout>
</layout>
<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>
11
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;

// ...

}
12
public class MatchResultActivity extends AppCompatActivity {



private MatchResultBinding binding;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



binding = DataBindingUtil.setContentView(this, R.layout.match_result);



MatchResult result = getIntent().getParcelableExtra("RESULT");



if (result.getHomeTeam() != null) {

binding.homeTeam.setText(result.getHomeTeam().getName());

binding.homeGoals.setText(
Integer.toString(result.getHomeTeam().getGoals()));

}if1

if (result.getAwayTeam() != null) {

binding.awayTeam.setText(result.getAwayTeam().getName());

binding.awayGoals.setText(
Integer.toString(result.getAwayTeam().getGoals()));

}if

Glide.with(this).load(result.getGifUrl())

.placeholder(R.drawable.loading).into(binding.resultGif);

}onCreate

}activity
13
Variable in layout
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android">
Automatic null check
<data>

<variable name="result"
type="it.droidcon.databinding.MatchResult"/>

</data>
<LinearLayout style="@style/root_layout">

<ImageView android:id="@+id/result_gif" style="@style/gif"/>



<LinearLayout style="@style/team_layout">

<TextView style=“@style/name"

android:text="@{result.homeTeam.name}"/>

<TextView style="@style/goals"

android:text="@{Integer.toString(result.homeTeam.goals)}"/>

</LinearLayout>



<LinearLayout style="@style/team_layout">

<TextView style="@style/name"

android:text="@{result.awayTeam.name}"/>

<TextView style="@style/goals"

android:text="@{Integer.toString(result.awayTeam.goals)}"/>

</LinearLayout>

</LinearLayout>

</layout>
14
public class MatchResultActivity extends AppCompatActivity {



private MatchResultBinding binding;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



binding = DataBindingUtil.setContentView(this, R.layout.match_result);



MatchResult result = getIntent().getParcelableExtra("RESULT");

binding.setResult(result);

if

Glide.with(this).load(result.getGifUrl())

.placeholder(R.drawable.loading).into(binding.resultGif);

}onCreate

}activity
15
Code in XML?
Are you serious?!?
16
Complex code in XML is
NOT
a best practice
#droidconUK - London - October 2016 - @fabioCollini 17
2Custom attributes
18
@BindingAdapter
<ImageView android:id="@+id/result_gif" style="@style/gif"/>
Glide.with(this).load(result.getGifUrl())

.placeholder(R.drawable.loading).into(binding.resultGif);

<ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/>
@BindingAdapter("imageUrl")

public static void loadImage(ImageView view, String url) {

Glide.with(view.getContext()).load(url)

.placeholder(R.drawable.loading).into(view);

}
19
public class MatchResultActivity extends AppCompatActivity {



private MatchResultBinding binding;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



binding = DataBindingUtil.setContentView(
this, R.layout.match_result);



MatchResult result = getIntent().getParcelableExtra("RESULT");

binding.setResult(result);

}onCreate

}activity
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
@BindingAdapter("goals")

public static void bindGoals(TextView view, int goals) {

view.setText(Integer.toString(goals));

}__

21
BindingAdapter
<TextView style="@style/goals"

android:text="@{Integer.toString(result.awayTeam.goals)}"/>
<TextView style="@style/goals"
app:goals="@{result.awayTeam.goals}"/>
#droidconUK - London - October 2016 - @fabioCollini 22
3Components
23
<layout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">

<data>

<variable name="result"

type="it.droidcon.databinding.MatchResult"/>

</data>

<LinearLayout style="@style/root_layout">

<ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/>





<LinearLayout style="@style/team_layout">

<TextView style="@style/name"

android:text="@{result.awayTeam.name}"/>

<TextView style="@style/goals"

app:goals="@{result.awayTeam.goals}"/>

</LinearLayout>

</LinearLayout>

</layout>
<LinearLayout style="@style/team_layout">

<TextView style="@style/name"

android:text="@{result.homeTeam.name}"/>

<TextView style="@style/goals"

app:goals="@{result.homeTeam.goals}"/>

</LinearLayout>
24
<layout
xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">

</layout>
team_detail.xml
<data>

<variable name="team"

type="it.droidcon.databinding.TeamScore"/>

</data>

<LinearLayout style="@style/team_layout">

<TextView style="@style/name"

android:text="@{team.name}"/>

<TextView style="@style/goals"

app:goals="@{team.goals}"/>

</LinearLayout>
</data>

<LinearLayout style="@style/root_layout">

<ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/>





<layout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">

<data>

<variable name="result"

type="it.droidcon.databinding.MatchResult"/>

<include layout="@layout/team_detail"

/>
<include layout="@layout/team_detail"

/>
</LinearLayout>

</layout>
bind:team="@{result.homeTeam}"
bind:team="@{result.awayTeam}"
#droidconUK - London - October 2016 - @fabioCollini 26
4Two Way Data Binding
27
28
public class QuestionInfo {

public String answer = "";

public int countdown = 10;
public int decrementCountdown() {

return --countdown;

}_
}_
29
public class QuestionInfo {

public String answer = "";

public int countdown = 10;
public int decrementCountdown() {

return --countdown;

}_
}_
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable

name="info"

type="it.droidcon.databinding.question.QuestionInfo"/>

</data>



<LinearLayout style="@style/form_root">

<TextView style="@style/question"/>



<EditText style="@style/answer" 

android:text="@{info.answer}" />



<Button style="@style/form_button"

android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>



<TextView style="@style/countdown"

android:text="@{Integer.toString(info.countdown)}" />

</LinearLayout>

</layout>
public class QuestionActivity extends AppCompatActivity {

private QuestionInfo info;



@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

QuestionBinding binding =
DataBindingUtil.setContentView(this, R.layout.question);

info = new QuestionInfo();

binding.setInfo(info);
Handler handler = new Handler();

handler.postDelayed(new Runnable() {

@Override public void run() {

int newValue = info.decrementCountdown();

if (newValue > 0) {

handler.postDelayed(this, 1000);

}

}

}, 1000);

}
}
30
public class QuestionInfo {

public String answer = "";

public int countdown = 10;
public int decrementCountdown() {

return --countdown;

}_
}_
31
32
Views are not automatically updated :(
package android.databinding;



public interface Observable {



void addOnPropertyChangedCallback(
OnPropertyChangedCallback callback);



void removeOnPropertyChangedCallback(
OnPropertyChangedCallback callback);



abstract class OnPropertyChangedCallback {

public abstract void onPropertyChanged(
Observable sender, int propertyId);

}

}
33
Observable hierarchy
34
public class QuestionInfo extends BaseObservable {_

private String answer = "";



private int countdown = 10;



public int decrementCountdown() {

--countdown;

notifyPropertyChanged(BR.countdown);

return countdown;

}__



@Bindable public String getAnswer() {

return answer;

}getAnswer



public void setAnswer(String answer) {

this.answer = answer;

notifyPropertyChanged(BR.answer);

}setAnswer



@Bindable public int getCountdown() {

return countdown;

}getCountdown

}___
35
public class QuestionInfo {_

public final ObservableField<String> answer = new ObservableField<>("");



public final ObservableInt countdown = new ObservableInt(10);



public int decrementCountdown() {

int value = countdown.get() - 1;

countdown.set(value);

return value;

}__

}___
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable

name="info"

type="it.droidcon.databinding.question.QuestionInfo"/>

</data>



<LinearLayout style="@style/form_root">

<TextView style="@style/question"/>



<EditText style="@style/answer" 

android:text="@{info.answer}" />



<Button style="@style/form_button"

android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>



<TextView style="@style/countdown"

android:text="@{Integer.toString(info.countdown)}" />

</LinearLayout>

</layout>
36
ObservableField<String>
ObservableInt
ObservableInt
public class QuestionInfo {_

public final ObservableField<String> answer = new ObservableField<>("");



public final ObservableInt countdown = new ObservableInt(10);



public int decrementCountdown() {

int value = countdown.get() - 1;

countdown.set(value);

return value;

}__

}___
ObservableField<String>
37
38
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
39
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable

name="info"

type="it.droidcon.databinding.question.QuestionInfo"/>

</data>



<LinearLayout style="@style/form_root">

<TextView style="@style/question"/>



<EditText style="@style/answer" 

android:text="@={info.answer}" />



<Button style="@style/form_button"

android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>



<TextView style="@style/countdown"

android:text="@{Integer.toString(info.countdown)}" />

</LinearLayout>

</layout>
40
Two way data binding
41
42
43
Layout
QuestionInfo
Binding
TextWatcherset(…)
addOnProperty
ChangedCallbackset(…)
if (changed)
WeakReference
if (changed)
#droidconUK - London - October 2016 - @fabioCollini 44
5Data Binding + RxJava
45
<layout xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable

name="info"

type="it.droidcon.databinding.question.QuestionInfo"/>

</data>



<LinearLayout style="@style/form_root">

<TextView style="@style/question"/>



<EditText style="@style/answer" 

android:text="@={info.answer}" />



<Button style="@style/form_button"

android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>



<TextView style="@style/countdown"

android:text="@{Integer.toString(info.countdown)}" />

</LinearLayout>

</layout>
public class QuestionInfo extends BaseObservable {_

private String answer = "";

private int countdown = 10;



public int decrementCountdown() {

--countdown;

notifyPropertyChanged(BR.countdown);
notifyPropertyChanged(BR.sendEnabled);

return countdown;

}__



@Bindable public String getAnswer() {

return answer;

}getAnswer



public void setAnswer(String answer) {

this.answer = answer;

notifyPropertyChanged(BR.answer);

notifyPropertyChanged(BR.sendEnabled);

}setAnswer



@Bindable public int getCountdown() {

return countdown;

}getCountdown
@Bindable public boolean isSendEnabled() {

return !answer.isEmpty() && countdown > 0;

}isButtonEnabled

}___
47
Not an Observable, View is not updated!
public class QuestionInfo {_

public final ObservableField<String> answer = new ObservableField<>("");



public final ObservableInt countdown = new ObservableInt(10);



public int decrementCountdown() {

int value = countdown.get() - 1;

countdown.set(value);

return value;

}__
public boolean isSendEnabled() {

return !answer.get().isEmpty() && countdown.get() > 0;

}

}___
48
RxJava FTW!
ObservableField<T> rx.Observable<T>
50
ObservableField<T> rx.Observable<T>
public static <T> rx.Observable<T> toRx(ObservableField<T> observableField) {

return rx.Observable.fromEmitter(emitter -> {

emitter.onNext(observableField.get());

OnPropertyChangedCallback callback = new OnPropertyChangedCallback() {

@Override

public void onPropertyChanged(Observable observable, int i) {

emitter.onNext(((ObservableField<T>) observable).get());

}

};

observableField.addOnPropertyChangedCallback(callback);

emitter.setCancellation(() ->
observableField.removeOnPropertyChangedCallback(callback));

}, Emitter.BackpressureMode.BUFFER);

}
51
public class QuestionInfo {_

public final ObservableField<String> answer = new ObservableField<>("");



public final ObservableInt countdown = new ObservableInt(10);
public final ObservableBoolean sendEnabled = new ObservableBoolean();



public int decrementCountdown() {

int value = countdown.get() - 1;

countdown.set(value);

return value;

}__
}___
52
public class QuestionActivity extends AppCompatActivity {



private QuestionInfo info;



private Subscription subscription;



@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

//...

}



@Override protected void onStart() {

super.onStart();

subscription = Observable.combineLatest(

toRx(info.answer),

toRx(info.countdown),

(answer, countdown) ->

!answer.isEmpty() && countdown > 0

).subscribe(info.sendEnabled::set);

}



@Override protected void onStop() {

super.onStop();

subscription.unsubscribe();

}

}
53
compile 'com.cantrowitz:rxbroadcast:1.0.0'
public class ConnectionChecker {



private Context context;



public ConnectionChecker(Context context) {

this.context = context;

}



public Observable<Boolean> getConnectionStatus() {

IntentFilter filter = new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION);

return RxBroadcast.fromBroadcast(context, filter)

.map(i -> getNetworkInfo())

.map(info -> info != null && info.isConnected())

.distinctUntilChanged();

}



private NetworkInfo getNetworkInfo() {

ConnectivityManager connectivityManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);

return connectivityManager.getActiveNetworkInfo();

}

}
public class QuestionActivity extends AppCompatActivity {



private QuestionInfo info;



private Subscription subscription;



@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

//...

}onCreate



@Override protected void onStart() {

super.onStart();

subscription = Observable.combineLatest(

toRx(info.answer),

toRx(info.countdown),

connectionChecker.getConnectionStatus(),

(answer, countdown, connected) ->

!answer.isEmpty() && countdown > 0 && connected

).subscribe(info.sendEnabled::set);

}onStart



@Override protected void onStop() {

super.onStop();

subscription.unsubscribe();

}onStop

}_
54
#droidconUK - London - October 2016 - @fabioCollini 55
6MVVM
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);

});

}

}
<layout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">

<data>

<variable name="viewModel"

type="it.droidcon.databinding.MatchResultViewModel"/>

</data>

<LinearLayout style="@style/root_layout">

<ImageView style="@style/gif"
app:imageUrl="@{viewModel.result.gifUrl}"/>





<include layout="@layout/team_detail"

/>
<include layout="@layout/team_detail"

/>
</LinearLayout>

</layout>
bind:team="@{viewModel.result.homeTeam}"
bind:team="@{viewModel.result.awayTeam}"
ObservableField
58
Visibility
<FrameLayout style="@style/progress_layout"

android:visibility=
"@{viewModel.loading ? View.VISIBLE : View.GONE}">

<ProgressBar style="@style/progress"/>

</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:bind="http://schemas.android.com/tools">

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

<variable name="viewModel"

type="it.droidcon.databinding.MatchResultViewModel"/>
</data>

<FrameLayout style="@style/main_container">


<LinearLayout style="@style/root_layout">

<!-- ... -->

</LinearLayout>





</FrameLayout>

</layout>
59
Visibility
<FrameLayout style="@style/progress_layout"

app:visibleOrGone="@{viewModel.loading}">

<ProgressBar style="@style/progress"/>

</FrameLayout>
@BindingAdapter("visibleOrGone")

public static void bindVisibleOrGone(View view, boolean b) {

view.setVisibility(b ? View.VISIBLE : View.GONE);

}____
@BindingAdapter("visible")

public static void bindVisible(View view, boolean b) {

view.setVisibility(b ? View.VISIBLE : View.INVISIBLE);

}
<LinearLayout style="@style/root_layout"

android:onClick="@{???}">

<!-- ... -->

</LinearLayout>

60
}___
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="@{v -> viewModel.reload()}">

<!-- ... -->

</LinearLayout>

61
public void reload() {
//..

}__
<LinearLayout style="@style/root_layout"

android:onClick=“@{viewModel::reload}”>

<!-- ... -->

</LinearLayout>

public void reload(View v) {
//..

}__
@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=“@{viewModel::reload}”>

<!-- ... -->

</LinearLayout>

public void reload() {
//..

}__
62
Final layout
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:bind="http://schemas.android.com/tools">

<data>

<!-- ... -->

</data>

<FrameLayout style="@style/main_container">


<LinearLayout style="@style/root_layout"

android:onClick=“@{viewModel::reload}”>

<!-- ... -->

</LinearLayout>



<FrameLayout style="@style/progress_layout"

app:visibleOrGone="@{viewModel.loading}">

<ProgressBar style="@style/progress"/>

</FrameLayout>



</FrameLayout>

</layout>
63
Model View ViewModel
View
ViewModel
Model
DataBinding
Retained on
configuration change
Saved in Activity or
Fragment state
Activity or Fragment
MVVM
View
ViewModel
Model
DataBinding
View
Presenter
Model
MVPVs
MVVM MVPVs
Less Java code
if (view != null)
A/B testing on View
Sometimes we need an
Activity :(
Testable code Testable code
Less XML
No more
66
github.com/fabioCollini/LifeCycleBinder
Move your Android code to testable Java classes
Custom attributes Reusable UI code
67
Data binding
Includes UI components
RxJava Easy composition
68
Links
developer.android.com/tools/data-binding/guide.html
Google I/O 2015 - What's new in Android
Data Binding -- Write Apps Faster (Android Dev Summit 2015)
Advanced Data Binding - Google I/O 2016
George Mount medium profile
Radosław Piekarz: RxJava meets Android Data Binding
Florina Muntenescu: A Journey Through MV Wonderland
Bill Phillips: Shades of MVVM
69
Thanks for your attention!
Questions?
This presentation will be soon available on the droidcon London website at
uk.droidcon.com/#skillscasts
Ad

More Related Content

What's hot (20)

React JS - A quick introduction tutorial
React JS - A quick introduction tutorialReact JS - A quick introduction tutorial
React JS - A quick introduction tutorial
Mohammed Fazuluddin
 
Git & GitHub for Beginners
Git & GitHub for BeginnersGit & GitHub for Beginners
Git & GitHub for Beginners
Sébastien Saunier
 
Hello, ReactorKit 
Hello, ReactorKit Hello, ReactorKit 
Hello, ReactorKit 
Suyeol Jeon
 
Introduction to Frida
Introduction to FridaIntroduction to Frida
Introduction to Frida
AbhishekJaiswal270
 
Git training v10
Git training v10Git training v10
Git training v10
Skander Hamza
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
Alexey Soshin
 
Letswift19-clean-architecture
Letswift19-clean-architectureLetswift19-clean-architecture
Letswift19-clean-architecture
Jung Kim
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
Varun Raj
 
Android datastorage
Android datastorageAndroid datastorage
Android datastorage
Krazy Koder
 
Git in 10 minutes
Git in 10 minutesGit in 10 minutes
Git in 10 minutes
Safique Ahmed Faruque
 
Spring Boot
Spring BootSpring Boot
Spring Boot
koppenolski
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
Roman Elizarov
 
React state
React  stateReact  state
React state
Ducat
 
Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Formation JPA Avancé / Hibernate gratuite par Ippon 2014Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Ippon
 
Android JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation ControllerAndroid JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation Controller
Leonardo Pirro
 
Git
GitGit
Git
Mouad EL Fakir
 
Java 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional InterfacesJava 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional Interfaces
Ganesh Samarthyam
 
Introduction to react native
Introduction to react nativeIntroduction to react native
Introduction to react native
Dani Akash
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
Embedded Android Workshop with Oreo
Embedded Android Workshop with OreoEmbedded Android Workshop with Oreo
Embedded Android Workshop with Oreo
Opersys inc.
 
React JS - A quick introduction tutorial
React JS - A quick introduction tutorialReact JS - A quick introduction tutorial
React JS - A quick introduction tutorial
Mohammed Fazuluddin
 
Hello, ReactorKit 
Hello, ReactorKit Hello, ReactorKit 
Hello, ReactorKit 
Suyeol Jeon
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
Alexey Soshin
 
Letswift19-clean-architecture
Letswift19-clean-architectureLetswift19-clean-architecture
Letswift19-clean-architecture
Jung Kim
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
Varun Raj
 
Android datastorage
Android datastorageAndroid datastorage
Android datastorage
Krazy Koder
 
Introduction to Kotlin coroutines
Introduction to Kotlin coroutinesIntroduction to Kotlin coroutines
Introduction to Kotlin coroutines
Roman Elizarov
 
React state
React  stateReact  state
React state
Ducat
 
Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Formation JPA Avancé / Hibernate gratuite par Ippon 2014Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Formation JPA Avancé / Hibernate gratuite par Ippon 2014
Ippon
 
Android JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation ControllerAndroid JetPack: easy navigation with the new Navigation Controller
Android JetPack: easy navigation with the new Navigation Controller
Leonardo Pirro
 
Java 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional InterfacesJava 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional Interfaces
Ganesh Samarthyam
 
Introduction to react native
Introduction to react nativeIntroduction to react native
Introduction to react native
Dani Akash
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
Embedded Android Workshop with Oreo
Embedded Android Workshop with OreoEmbedded Android Workshop with Oreo
Embedded Android Workshop with Oreo
Opersys inc.
 

Viewers also liked (20)

Deep dive into Android Data Binding
Deep dive into Android Data BindingDeep dive into Android Data Binding
Deep dive into Android Data Binding
Radek Piekarz
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
Fabio Collini
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
Fabio Collini
 
Android data binding
Android data bindingAndroid data binding
Android data binding
Sergi Martínez
 
MVVM & Data Binding Library
MVVM & Data Binding Library MVVM & Data Binding Library
MVVM & Data Binding Library
10Clouds
 
Android MVVM
Android MVVMAndroid MVVM
Android MVVM
David Estivariz Pierola
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on android
Rodrigo Bressan
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no Android
Nelson Glauber Leal
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015
Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
Fabio Collini
 
Android Databinding Library
Android Databinding LibraryAndroid Databinding Library
Android Databinding Library
Takuji Nishibayashi
 
Effective Android UI - English
Effective Android UI - EnglishEffective Android UI - English
Effective Android UI - English
Pedro Vicente Gómez Sánchez
 
Kotlin 사용기
Kotlin 사용기Kotlin 사용기
Kotlin 사용기
KyungHo Jung
 
Android MVVM TDD
Android MVVM TDDAndroid MVVM TDD
Android MVVM TDD
KyungHo Jung
 
Let me introduce you: DOTS
Let me introduce you: DOTSLet me introduce you: DOTS
Let me introduce you: DOTS
Frank van der Linden
 
Beyond Android Views - Window,Surface,Special Views,and More
Beyond Android Views - Window,Surface,Special Views,and MoreBeyond Android Views - Window,Surface,Special Views,and More
Beyond Android Views - Window,Surface,Special Views,and More
rogeryi
 
Android Custom Views
Android Custom ViewsAndroid Custom Views
Android Custom Views
Babar Sanah
 
Android Development - ConstraintLayout
Android Development - ConstraintLayoutAndroid Development - ConstraintLayout
Android Development - ConstraintLayout
Manuel Vicente Vivo
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
Fabio Collini
 
Deep dive into Android Data Binding
Deep dive into Android Data BindingDeep dive into Android Data Binding
Deep dive into Android Data Binding
Radek Piekarz
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
Fabio Collini
 
Testable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVMTestable Android Apps using data binding and MVVM
Testable Android Apps using data binding and MVVM
Fabio Collini
 
MVVM & Data Binding Library
MVVM & Data Binding Library MVVM & Data Binding Library
MVVM & Data Binding Library
10Clouds
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on android
Rodrigo Bressan
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no Android
Nelson Glauber Leal
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015
Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
Fabio Collini
 
Kotlin 사용기
Kotlin 사용기Kotlin 사용기
Kotlin 사용기
KyungHo Jung
 
Beyond Android Views - Window,Surface,Special Views,and More
Beyond Android Views - Window,Surface,Special Views,and MoreBeyond Android Views - Window,Surface,Special Views,and More
Beyond Android Views - Window,Surface,Special Views,and More
rogeryi
 
Android Custom Views
Android Custom ViewsAndroid Custom Views
Android Custom Views
Babar Sanah
 
Android Development - ConstraintLayout
Android Development - ConstraintLayoutAndroid Development - ConstraintLayout
Android Development - ConstraintLayout
Manuel Vicente Vivo
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
Fabio Collini
 
Ad

Similar to Android Data Binding in action using MVVM pattern - droidconUK (20)

Data binding 入門淺談
Data binding 入門淺談Data binding 入門淺談
Data binding 入門淺談
awonwon
 
06. Android Basic Widget and Container
06. Android Basic Widget and Container06. Android Basic Widget and Container
06. Android Basic Widget and Container
Oum Saokosal
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
Brenda Cook
 
Desenvolvimento Mobile Híbrido
Desenvolvimento Mobile HíbridoDesenvolvimento Mobile Híbrido
Desenvolvimento Mobile Híbrido
Juliano Martins
 
Upload[1]
Upload[1]Upload[1]
Upload[1]
mirjana stojanova
 
The Structure of Web Code: A Case For Polymer, November 1, 2014
The Structure of Web Code: A Case For Polymer, November 1, 2014The Structure of Web Code: A Case For Polymer, November 1, 2014
The Structure of Web Code: A Case For Polymer, November 1, 2014
Tommie Gannert
 
Taking your Web App for a walk
Taking your Web App for a walkTaking your Web App for a walk
Taking your Web App for a walk
Jens-Christian Fischer
 
Angular directive filter and routing
Angular directive filter and routingAngular directive filter and routing
Angular directive filter and routing
jagriti srivastava
 
Infinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum Android Talks #16 - How to shoot your self in the foot by Dino KovacInfinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum
 
Vaadin Components
Vaadin ComponentsVaadin Components
Vaadin Components
Joonas Lehtinen
 
Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015 Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015
Mario Jorge Pereira
 
Modern android development
Modern android developmentModern android development
Modern android development
Khiem-Kim Ho Xuan
 
Test upload
Test uploadTest upload
Test upload
Darrell Lawson Jr.
 
Developing web applications in 2010
Developing web applications in 2010Developing web applications in 2010
Developing web applications in 2010
Ignacio Coloma
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJS
Marco Vito Moscaritolo
 
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
crokitta
 
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Suzzicks
 
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
MobileMoxie
 
Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2
Filippo Matteo Riggio
 
Cordova training : Day 5 - UI development using Framework7
Cordova training : Day 5 - UI development using Framework7Cordova training : Day 5 - UI development using Framework7
Cordova training : Day 5 - UI development using Framework7
Binu Paul
 
Data binding 入門淺談
Data binding 入門淺談Data binding 入門淺談
Data binding 入門淺談
awonwon
 
06. Android Basic Widget and Container
06. Android Basic Widget and Container06. Android Basic Widget and Container
06. Android Basic Widget and Container
Oum Saokosal
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
Brenda Cook
 
Desenvolvimento Mobile Híbrido
Desenvolvimento Mobile HíbridoDesenvolvimento Mobile Híbrido
Desenvolvimento Mobile Híbrido
Juliano Martins
 
The Structure of Web Code: A Case For Polymer, November 1, 2014
The Structure of Web Code: A Case For Polymer, November 1, 2014The Structure of Web Code: A Case For Polymer, November 1, 2014
The Structure of Web Code: A Case For Polymer, November 1, 2014
Tommie Gannert
 
Angular directive filter and routing
Angular directive filter and routingAngular directive filter and routing
Angular directive filter and routing
jagriti srivastava
 
Infinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum Android Talks #16 - How to shoot your self in the foot by Dino KovacInfinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum Android Talks #16 - How to shoot your self in the foot by Dino Kovac
Infinum
 
Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015 Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015
Mario Jorge Pereira
 
Developing web applications in 2010
Developing web applications in 2010Developing web applications in 2010
Developing web applications in 2010
Ignacio Coloma
 
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
crokitta
 
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Suzzicks
 
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
Why Deep Linking is the Next Big Thing: App Indexing - SMX East 2015
MobileMoxie
 
Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2
Filippo Matteo Riggio
 
Cordova training : Day 5 - UI development using Framework7
Cordova training : Day 5 - UI development using Framework7Cordova training : Day 5 - UI development using Framework7
Cordova training : Day 5 - UI development using Framework7
Binu Paul
 
Ad

More from Fabio Collini (18)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
Fabio Collini
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized project
Fabio Collini
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
Fabio Collini
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
Fabio Collini
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Fabio Collini
 
Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture project
Fabio Collini
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
Fabio Collini
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
Fabio Collini
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
Fabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
Fabio Collini
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
Fabio Collini
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012
Fabio Collini
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011
Fabio Collini
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
Fabio Collini
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized project
Fabio Collini
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
Fabio Collini
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
Fabio Collini
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Fabio Collini
 
Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture project
Fabio Collini
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
Fabio Collini
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
Fabio Collini
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
Fabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
Fabio Collini
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
Fabio Collini
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012
Fabio Collini
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011
Fabio Collini
 

Android Data Binding in action using MVVM pattern - droidconUK

  • 1. #droidconUK Android Data Binding in action using MVVM pattern Fabio Collini droidcon London October 2016
  • 3. 3 Agenda 1. Data Binding basics 2. Custom attributes 3. Components 4. Two Way Data Binding 5. Data Binding + RxJava 6. Model View ViewModel
  • 4. #droidconUK - London - October 2016 - @fabioCollini 4 1Data Binding basics
  • 7. 7 match_result.xml <?xml version="1.0" encoding="utf-8"?>
 <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>
  • 8. 8 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
 }
  • 9. dataBinding {
 enabled = true
 }
 9 build.gradle android {
 //...
 //...
 
 defaultConfig {
 //...
 ____}
 buildTypes {
 //...
 ____} }
  • 10. <?xml version="1.0" encoding="utf-8"?>
 <layout>
 <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>
 </layout> 10 Data Binding layout <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> <?xml version="1.0" encoding="utf-8"?> <layout> </layout>
  • 11. <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> 11 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;
 // ...
 }
  • 12. 12 public class MatchResultActivity extends AppCompatActivity {
 
 private MatchResultBinding binding;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 binding = DataBindingUtil.setContentView(this, R.layout.match_result);
 
 MatchResult result = getIntent().getParcelableExtra("RESULT");
 
 if (result.getHomeTeam() != null) {
 binding.homeTeam.setText(result.getHomeTeam().getName());
 binding.homeGoals.setText( Integer.toString(result.getHomeTeam().getGoals()));
 }if1
 if (result.getAwayTeam() != null) {
 binding.awayTeam.setText(result.getAwayTeam().getName());
 binding.awayGoals.setText( Integer.toString(result.getAwayTeam().getGoals()));
 }if
 Glide.with(this).load(result.getGifUrl())
 .placeholder(R.drawable.loading).into(binding.resultGif);
 }onCreate
 }activity
  • 13. 13 Variable in layout <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android"> Automatic null check <data>
 <variable name="result" type="it.droidcon.databinding.MatchResult"/>
 </data> <LinearLayout style="@style/root_layout">
 <ImageView android:id="@+id/result_gif" style="@style/gif"/>
 
 <LinearLayout style="@style/team_layout">
 <TextView style=“@style/name"
 android:text="@{result.homeTeam.name}"/>
 <TextView style="@style/goals"
 android:text="@{Integer.toString(result.homeTeam.goals)}"/>
 </LinearLayout>
 
 <LinearLayout style="@style/team_layout">
 <TextView style="@style/name"
 android:text="@{result.awayTeam.name}"/>
 <TextView style="@style/goals"
 android:text="@{Integer.toString(result.awayTeam.goals)}"/>
 </LinearLayout>
 </LinearLayout>
 </layout>
  • 14. 14 public class MatchResultActivity extends AppCompatActivity {
 
 private MatchResultBinding binding;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 binding = DataBindingUtil.setContentView(this, R.layout.match_result);
 
 MatchResult result = getIntent().getParcelableExtra("RESULT");
 binding.setResult(result);
 if
 Glide.with(this).load(result.getGifUrl())
 .placeholder(R.drawable.loading).into(binding.resultGif);
 }onCreate
 }activity
  • 15. 15 Code in XML? Are you serious?!?
  • 16. 16 Complex code in XML is NOT a best practice
  • 17. #droidconUK - London - October 2016 - @fabioCollini 17 2Custom attributes
  • 18. 18 @BindingAdapter <ImageView android:id="@+id/result_gif" style="@style/gif"/> Glide.with(this).load(result.getGifUrl())
 .placeholder(R.drawable.loading).into(binding.resultGif);
 <ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/> @BindingAdapter("imageUrl")
 public static void loadImage(ImageView view, String url) {
 Glide.with(view.getContext()).load(url)
 .placeholder(R.drawable.loading).into(view);
 }
  • 19. 19 public class MatchResultActivity extends AppCompatActivity {
 
 private MatchResultBinding binding;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 
 binding = DataBindingUtil.setContentView( this, R.layout.match_result);
 
 MatchResult result = getIntent().getParcelableExtra("RESULT");
 binding.setResult(result);
 }onCreate
 }activity
  • 20. 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
  • 21. @BindingAdapter("goals")
 public static void bindGoals(TextView view, int goals) {
 view.setText(Integer.toString(goals));
 }__
 21 BindingAdapter <TextView style="@style/goals"
 android:text="@{Integer.toString(result.awayTeam.goals)}"/> <TextView style="@style/goals" app:goals="@{result.awayTeam.goals}"/>
  • 22. #droidconUK - London - October 2016 - @fabioCollini 22 3Components
  • 23. 23 <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 <data>
 <variable name="result"
 type="it.droidcon.databinding.MatchResult"/>
 </data>
 <LinearLayout style="@style/root_layout">
 <ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/>
 
 
 <LinearLayout style="@style/team_layout">
 <TextView style="@style/name"
 android:text="@{result.awayTeam.name}"/>
 <TextView style="@style/goals"
 app:goals="@{result.awayTeam.goals}"/>
 </LinearLayout>
 </LinearLayout>
 </layout> <LinearLayout style="@style/team_layout">
 <TextView style="@style/name"
 android:text="@{result.homeTeam.name}"/>
 <TextView style="@style/goals"
 app:goals="@{result.homeTeam.goals}"/>
 </LinearLayout>
  • 25. </data>
 <LinearLayout style="@style/root_layout">
 <ImageView style="@style/gif" app:imageUrl="@{result.gifUrl}"/>
 
 
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:bind="http://schemas.android.com/tools">
 <data>
 <variable name="result"
 type="it.droidcon.databinding.MatchResult"/>
 <include layout="@layout/team_detail"
 /> <include layout="@layout/team_detail"
 /> </LinearLayout>
 </layout> bind:team="@{result.homeTeam}" bind:team="@{result.awayTeam}"
  • 26. #droidconUK - London - October 2016 - @fabioCollini 26 4Two Way Data Binding
  • 27. 27
  • 28. 28 public class QuestionInfo {
 public String answer = "";
 public int countdown = 10; public int decrementCountdown() {
 return --countdown;
 }_ }_
  • 29. 29 public class QuestionInfo {
 public String answer = "";
 public int countdown = 10; public int decrementCountdown() {
 return --countdown;
 }_ }_ <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <variable
 name="info"
 type="it.droidcon.databinding.question.QuestionInfo"/>
 </data>
 
 <LinearLayout style="@style/form_root">
 <TextView style="@style/question"/>
 
 <EditText style="@style/answer" 
 android:text="@{info.answer}" />
 
 <Button style="@style/form_button"
 android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>
 
 <TextView style="@style/countdown"
 android:text="@{Integer.toString(info.countdown)}" />
 </LinearLayout>
 </layout>
  • 30. public class QuestionActivity extends AppCompatActivity {
 private QuestionInfo info;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 QuestionBinding binding = DataBindingUtil.setContentView(this, R.layout.question);
 info = new QuestionInfo();
 binding.setInfo(info); Handler handler = new Handler();
 handler.postDelayed(new Runnable() {
 @Override public void run() {
 int newValue = info.decrementCountdown();
 if (newValue > 0) {
 handler.postDelayed(this, 1000);
 }
 }
 }, 1000);
 } } 30 public class QuestionInfo {
 public String answer = "";
 public int countdown = 10; public int decrementCountdown() {
 return --countdown;
 }_ }_
  • 31. 31
  • 32. 32 Views are not automatically updated :( package android.databinding;
 
 public interface Observable {
 
 void addOnPropertyChangedCallback( OnPropertyChangedCallback callback);
 
 void removeOnPropertyChangedCallback( OnPropertyChangedCallback callback);
 
 abstract class OnPropertyChangedCallback {
 public abstract void onPropertyChanged( Observable sender, int propertyId);
 }
 }
  • 34. 34 public class QuestionInfo extends BaseObservable {_
 private String answer = "";
 
 private int countdown = 10;
 
 public int decrementCountdown() {
 --countdown;
 notifyPropertyChanged(BR.countdown);
 return countdown;
 }__
 
 @Bindable public String getAnswer() {
 return answer;
 }getAnswer
 
 public void setAnswer(String answer) {
 this.answer = answer;
 notifyPropertyChanged(BR.answer);
 }setAnswer
 
 @Bindable public int getCountdown() {
 return countdown;
 }getCountdown
 }___
  • 35. 35 public class QuestionInfo {_
 public final ObservableField<String> answer = new ObservableField<>("");
 
 public final ObservableInt countdown = new ObservableInt(10);
 
 public int decrementCountdown() {
 int value = countdown.get() - 1;
 countdown.set(value);
 return value;
 }__
 }___
  • 36. <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <variable
 name="info"
 type="it.droidcon.databinding.question.QuestionInfo"/>
 </data>
 
 <LinearLayout style="@style/form_root">
 <TextView style="@style/question"/>
 
 <EditText style="@style/answer" 
 android:text="@{info.answer}" />
 
 <Button style="@style/form_button"
 android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>
 
 <TextView style="@style/countdown"
 android:text="@{Integer.toString(info.countdown)}" />
 </LinearLayout>
 </layout> 36 ObservableField<String> ObservableInt ObservableInt public class QuestionInfo {_
 public final ObservableField<String> answer = new ObservableField<>("");
 
 public final ObservableInt countdown = new ObservableInt(10);
 
 public int decrementCountdown() {
 int value = countdown.get() - 1;
 countdown.set(value);
 return value;
 }__
 }___ ObservableField<String>
  • 37. 37
  • 38. 38 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
  • 39. 39
  • 40. <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <variable
 name="info"
 type="it.droidcon.databinding.question.QuestionInfo"/>
 </data>
 
 <LinearLayout style="@style/form_root">
 <TextView style="@style/question"/>
 
 <EditText style="@style/answer" 
 android:text="@={info.answer}" />
 
 <Button style="@style/form_button"
 android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>
 
 <TextView style="@style/countdown"
 android:text="@{Integer.toString(info.countdown)}" />
 </LinearLayout>
 </layout> 40 Two way data binding
  • 41. 41
  • 42. 42
  • 44. #droidconUK - London - October 2016 - @fabioCollini 44 5Data Binding + RxJava
  • 45. 45 <layout xmlns:android="http://schemas.android.com/apk/res/android">
 <data>
 <variable
 name="info"
 type="it.droidcon.databinding.question.QuestionInfo"/>
 </data>
 
 <LinearLayout style="@style/form_root">
 <TextView style="@style/question"/>
 
 <EditText style="@style/answer" 
 android:text="@={info.answer}" />
 
 <Button style="@style/form_button"
 android:enabled="@{info.countdown > 0 &amp;&amp; !info.answer.empty}"/>
 
 <TextView style="@style/countdown"
 android:text="@{Integer.toString(info.countdown)}" />
 </LinearLayout>
 </layout>
  • 46. public class QuestionInfo extends BaseObservable {_
 private String answer = "";
 private int countdown = 10;
 
 public int decrementCountdown() {
 --countdown;
 notifyPropertyChanged(BR.countdown); notifyPropertyChanged(BR.sendEnabled);
 return countdown;
 }__
 
 @Bindable public String getAnswer() {
 return answer;
 }getAnswer
 
 public void setAnswer(String answer) {
 this.answer = answer;
 notifyPropertyChanged(BR.answer);
 notifyPropertyChanged(BR.sendEnabled);
 }setAnswer
 
 @Bindable public int getCountdown() {
 return countdown;
 }getCountdown @Bindable public boolean isSendEnabled() {
 return !answer.isEmpty() && countdown > 0;
 }isButtonEnabled
 }___
  • 47. 47 Not an Observable, View is not updated! public class QuestionInfo {_
 public final ObservableField<String> answer = new ObservableField<>("");
 
 public final ObservableInt countdown = new ObservableInt(10);
 
 public int decrementCountdown() {
 int value = countdown.get() - 1;
 countdown.set(value);
 return value;
 }__ public boolean isSendEnabled() {
 return !answer.get().isEmpty() && countdown.get() > 0;
 }
 }___
  • 50. 50 ObservableField<T> rx.Observable<T> public static <T> rx.Observable<T> toRx(ObservableField<T> observableField) {
 return rx.Observable.fromEmitter(emitter -> {
 emitter.onNext(observableField.get());
 OnPropertyChangedCallback callback = new OnPropertyChangedCallback() {
 @Override
 public void onPropertyChanged(Observable observable, int i) {
 emitter.onNext(((ObservableField<T>) observable).get());
 }
 };
 observableField.addOnPropertyChangedCallback(callback);
 emitter.setCancellation(() -> observableField.removeOnPropertyChangedCallback(callback));
 }, Emitter.BackpressureMode.BUFFER);
 }
  • 51. 51 public class QuestionInfo {_
 public final ObservableField<String> answer = new ObservableField<>("");
 
 public final ObservableInt countdown = new ObservableInt(10); public final ObservableBoolean sendEnabled = new ObservableBoolean();
 
 public int decrementCountdown() {
 int value = countdown.get() - 1;
 countdown.set(value);
 return value;
 }__ }___
  • 52. 52 public class QuestionActivity extends AppCompatActivity {
 
 private QuestionInfo info;
 
 private Subscription subscription;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 //...
 }
 
 @Override protected void onStart() {
 super.onStart();
 subscription = Observable.combineLatest(
 toRx(info.answer),
 toRx(info.countdown),
 (answer, countdown) ->
 !answer.isEmpty() && countdown > 0
 ).subscribe(info.sendEnabled::set);
 }
 
 @Override protected void onStop() {
 super.onStop();
 subscription.unsubscribe();
 }
 }
  • 53. 53 compile 'com.cantrowitz:rxbroadcast:1.0.0' public class ConnectionChecker {
 
 private Context context;
 
 public ConnectionChecker(Context context) {
 this.context = context;
 }
 
 public Observable<Boolean> getConnectionStatus() {
 IntentFilter filter = new IntentFilter( ConnectivityManager.CONNECTIVITY_ACTION);
 return RxBroadcast.fromBroadcast(context, filter)
 .map(i -> getNetworkInfo())
 .map(info -> info != null && info.isConnected())
 .distinctUntilChanged();
 }
 
 private NetworkInfo getNetworkInfo() {
 ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 return connectivityManager.getActiveNetworkInfo();
 }
 }
  • 54. public class QuestionActivity extends AppCompatActivity {
 
 private QuestionInfo info;
 
 private Subscription subscription;
 
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
 //...
 }onCreate
 
 @Override protected void onStart() {
 super.onStart();
 subscription = Observable.combineLatest(
 toRx(info.answer),
 toRx(info.countdown),
 connectionChecker.getConnectionStatus(),
 (answer, countdown, connected) ->
 !answer.isEmpty() && countdown > 0 && connected
 ).subscribe(info.sendEnabled::set);
 }onStart
 
 @Override protected void onStop() {
 super.onStop();
 subscription.unsubscribe();
 }onStop
 }_ 54
  • 55. #droidconUK - London - October 2016 - @fabioCollini 55 6MVVM
  • 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);
 });
 }
 }
  • 57. <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:bind="http://schemas.android.com/tools">
 <data>
 <variable name="viewModel"
 type="it.droidcon.databinding.MatchResultViewModel"/>
 </data>
 <LinearLayout style="@style/root_layout">
 <ImageView style="@style/gif" app:imageUrl="@{viewModel.result.gifUrl}"/>
 
 
 <include layout="@layout/team_detail"
 /> <include layout="@layout/team_detail"
 /> </LinearLayout>
 </layout> bind:team="@{viewModel.result.homeTeam}" bind:team="@{viewModel.result.awayTeam}" ObservableField
  • 58. 58 Visibility <FrameLayout style="@style/progress_layout"
 android:visibility= "@{viewModel.loading ? View.VISIBLE : View.GONE}">
 <ProgressBar style="@style/progress"/>
 </FrameLayout> <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:bind="http://schemas.android.com/tools">
 <data> <import type="android.view.View"/>
 <variable name="viewModel"
 type="it.droidcon.databinding.MatchResultViewModel"/> </data>
 <FrameLayout style="@style/main_container"> 
 <LinearLayout style="@style/root_layout">
 <!-- ... -->
 </LinearLayout>
 
 
 </FrameLayout>
 </layout>
  • 59. 59 Visibility <FrameLayout style="@style/progress_layout"
 app:visibleOrGone="@{viewModel.loading}">
 <ProgressBar style="@style/progress"/>
 </FrameLayout> @BindingAdapter("visibleOrGone")
 public static void bindVisibleOrGone(View view, boolean b) {
 view.setVisibility(b ? View.VISIBLE : View.GONE);
 }____ @BindingAdapter("visible")
 public static void bindVisible(View view, boolean b) {
 view.setVisibility(b ? View.VISIBLE : View.INVISIBLE);
 }
  • 60. <LinearLayout style="@style/root_layout"
 android:onClick="@{???}">
 <!-- ... -->
 </LinearLayout>
 60 }___ 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);
 });
 }__
  • 61. <LinearLayout style="@style/root_layout"
 android:onClick="@{v -> viewModel.reload()}">
 <!-- ... -->
 </LinearLayout>
 61 public void reload() { //..
 }__ <LinearLayout style="@style/root_layout"
 android:onClick=“@{viewModel::reload}”>
 <!-- ... -->
 </LinearLayout>
 public void reload(View v) { //..
 }__ @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=“@{viewModel::reload}”>
 <!-- ... -->
 </LinearLayout>
 public void reload() { //..
 }__
  • 62. 62 Final layout <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:bind="http://schemas.android.com/tools">
 <data>
 <!-- ... -->
 </data>
 <FrameLayout style="@style/main_container"> 
 <LinearLayout style="@style/root_layout"
 android:onClick=“@{viewModel::reload}”>
 <!-- ... -->
 </LinearLayout>
 
 <FrameLayout style="@style/progress_layout"
 app:visibleOrGone="@{viewModel.loading}">
 <ProgressBar style="@style/progress"/>
 </FrameLayout>
 
 </FrameLayout>
 </layout>
  • 63. 63 Model View ViewModel View ViewModel Model DataBinding Retained on configuration change Saved in Activity or Fragment state Activity or Fragment
  • 65. MVVM MVPVs Less Java code if (view != null) A/B testing on View Sometimes we need an Activity :( Testable code Testable code Less XML No more
  • 67. Custom attributes Reusable UI code 67 Data binding Includes UI components RxJava Easy composition
  • 68. 68 Links developer.android.com/tools/data-binding/guide.html Google I/O 2015 - What's new in Android Data Binding -- Write Apps Faster (Android Dev Summit 2015) Advanced Data Binding - Google I/O 2016 George Mount medium profile Radosław Piekarz: RxJava meets Android Data Binding Florina Muntenescu: A Journey Through MV Wonderland Bill Phillips: Shades of MVVM
  • 69. 69 Thanks for your attention! Questions? This presentation will be soon available on the droidcon London website at uk.droidcon.com/#skillscasts