SlideShare une entreprise Scribd logo
1  sur  69
Télécharger pour lire hors ligne
#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

Contenu connexe

Tendances

Intro to vue.js
Intro to vue.jsIntro to vue.js
Intro to vue.jsTechMagic
 
Doxygen 사용법
Doxygen 사용법Doxygen 사용법
Doxygen 사용법YoungSu Son
 
Thread presentation
Thread presentationThread presentation
Thread presentationAAshish Ojha
 
Presentation of framework Angular
Presentation of framework AngularPresentation of framework Angular
Presentation of framework AngularLhouceine OUHAMZA
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring bootAntoine Rey
 
Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSam Brannen
 
Use Node.js to create a REST API
Use Node.js to create a REST APIUse Node.js to create a REST API
Use Node.js to create a REST APIFabien Vauchelles
 
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...Edureka!
 
Modernizing Web Apps with .NET 6.pptx
Modernizing Web Apps with .NET 6.pptxModernizing Web Apps with .NET 6.pptx
Modernizing Web Apps with .NET 6.pptxEd Charbeneau
 
Java EE 再入門
Java EE 再入門Java EE 再入門
Java EE 再入門minazou67
 

Tendances (20)

WordPress Architecture For Beginners
WordPress Architecture For Beginners WordPress Architecture For Beginners
WordPress Architecture For Beginners
 
Intro to vue.js
Intro to vue.jsIntro to vue.js
Intro to vue.js
 
Doxygen 사용법
Doxygen 사용법Doxygen 사용법
Doxygen 사용법
 
Introduction to Spring Boot
Introduction to Spring BootIntroduction to Spring Boot
Introduction to Spring Boot
 
React
React React
React
 
Thread presentation
Thread presentationThread presentation
Thread presentation
 
Vue.js
Vue.jsVue.js
Vue.js
 
Presentation of framework Angular
Presentation of framework AngularPresentation of framework Angular
Presentation of framework Angular
 
Introduction à spring boot
Introduction à spring bootIntroduction à spring boot
Introduction à spring boot
 
React js
React jsReact js
React js
 
Spring mvc
Spring mvcSpring mvc
Spring mvc
 
Firebase slide
Firebase slideFirebase slide
Firebase slide
 
Spring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. RESTSpring Web Services: SOAP vs. REST
Spring Web Services: SOAP vs. REST
 
Use Node.js to create a REST API
Use Node.js to create a REST APIUse Node.js to create a REST API
Use Node.js to create a REST API
 
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
 
Spring AOP
Spring AOPSpring AOP
Spring AOP
 
Maven
MavenMaven
Maven
 
Modernizing Web Apps with .NET 6.pptx
Modernizing Web Apps with .NET 6.pptxModernizing Web Apps with .NET 6.pptx
Modernizing Web Apps with .NET 6.pptx
 
Spring ioc
Spring iocSpring ioc
Spring ioc
 
Java EE 再入門
Java EE 再入門Java EE 再入門
Java EE 再入門
 

En vedette

Deep dive into Android Data Binding
Deep dive into Android Data BindingDeep dive into Android Data Binding
Deep dive into Android Data BindingRadek 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 patternFabio 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 MVVMFabio 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 RxJavaFabio Collini
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on androidRodrigo Bressan
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no AndroidNelson Glauber Leal
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeFabio 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 Morerogeryi
 
Android Custom Views
Android Custom ViewsAndroid Custom Views
Android Custom ViewsBabar Sanah
 
Android Development - ConstraintLayout
Android Development - ConstraintLayoutAndroid Development - ConstraintLayout
Android Development - ConstraintLayoutManuel Vicente Vivo
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Fabio Collini
 

En vedette (20)

Deep dive into Android Data Binding
Deep dive into Android Data BindingDeep dive into Android Data Binding
Deep dive into Android Data Binding
 
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
 
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
 
Android data binding
Android data bindingAndroid data binding
Android data binding
 
MVVM & Data Binding Library
MVVM & Data Binding Library MVVM & Data Binding Library
MVVM & Data Binding Library
 
Android MVVM
Android MVVMAndroid MVVM
Android MVVM
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on android
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no Android
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
 
Android Databinding Library
Android Databinding LibraryAndroid Databinding Library
Android Databinding Library
 
Effective Android UI - English
Effective Android UI - EnglishEffective Android UI - English
Effective Android UI - English
 
Kotlin 사용기
Kotlin 사용기Kotlin 사용기
Kotlin 사용기
 
Android MVVM TDD
Android MVVM TDDAndroid MVVM TDD
Android MVVM TDD
 
Let me introduce you: DOTS
Let me introduce you: DOTSLet me introduce you: DOTS
Let me introduce you: DOTS
 
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
 
Android Custom Views
Android Custom ViewsAndroid Custom Views
Android Custom Views
 
Android Development - ConstraintLayout
Android Development - ConstraintLayoutAndroid Development - ConstraintLayout
Android Development - ConstraintLayout
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
 

Similaire à Android Data Binding in action using MVVM pattern - droidconUK

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 ContainerOum 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íbridoJuliano 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, 2014Tommie Gannert
 
Angular directive filter and routing
Angular directive filter and routingAngular directive filter and routing
Angular directive filter and routingjagriti 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 KovacInfinum
 
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 2010Ignacio 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 2012crokitta
 
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 2015Suzzicks
 
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 2015MobileMoxie
 
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 2Filippo 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 Framework7Binu Paul
 

Similaire à Android Data Binding in action using MVVM pattern - droidconUK (20)

Data binding 入門淺談
Data binding 入門淺談Data binding 入門淺談
Data binding 入門淺談
 
06. Android Basic Widget and Container
06. Android Basic Widget and Container06. Android Basic Widget and Container
06. Android Basic Widget and Container
 
Fragments: Why, How, What For?
Fragments: Why, How, What For?Fragments: Why, How, What For?
Fragments: Why, How, What For?
 
Desenvolvimento Mobile Híbrido
Desenvolvimento Mobile HíbridoDesenvolvimento Mobile Híbrido
Desenvolvimento Mobile Híbrido
 
Upload[1]
Upload[1]Upload[1]
Upload[1]
 
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
 
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
 
Angular directive filter and routing
Angular directive filter and routingAngular directive filter and routing
Angular directive filter and routing
 
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
 
Vaadin Components
Vaadin ComponentsVaadin Components
Vaadin Components
 
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
 
Modern android development
Modern android developmentModern android development
Modern android development
 
Test upload
Test uploadTest upload
Test upload
 
Developing web applications in 2010
Developing web applications in 2010Developing web applications in 2010
Developing web applications in 2010
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJS
 
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
 
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
 
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
 
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
 
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
 

Plus de Fabio Collini

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose worldFabio Collini
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized projectFabio Collini
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutinesFabio 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 confFabio 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 StockholmFabio 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 projectFabio 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 ItalyFabio 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 RomagnaFabio 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 ArchitectureFabio 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 MilanFabio Collini
 
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 TurinFabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018Fabio 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 italyFabio 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+kFabio 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 UKFabio Collini
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Fabio 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 RxJavaFabio 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 2011Fabio Collini
 

Plus de Fabio Collini (19)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Using hilt in a modularized project
Using hilt in a modularized projectUsing hilt in a modularized project
Using hilt in a modularized project
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
 
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
 
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
 
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
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
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
 
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
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011
 

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