Slide 1

Slide 1 text

How to use MVVM pattern on Android +RadoslawPiekarz @radzio

Slide 2

Slide 2 text

Agenda • The need for better tools • How can we make things better • MVP & MVVM - design patterns you should use • MVVM on Android – different approaches • Pros & Cons • Summary • Q&A

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Butterknife public class MyActivity extends Activity { @InjectView(R.id.button) Button button; @OnClick(R.id.button) void onButtonClicked() { // some black magic } //... }

Slide 6

Slide 6 text

Android Annotations @EActivity(R.layout.main_activity) public class MyActivity extends Activity { @ViewById(R.id.button) Button button; @Click(R.id.button) public void onButtonClicked() { //... } //... } Android Annotations

Slide 7

Slide 7 text

Is it enough?

Slide 8

Slide 8 text

NO

Slide 9

Slide 9 text

Model View Presenter ● The model is an interface defining the data to be displayed ● The view is a passive interface that displays data and routes user commands (events) to the presenter to act upon that data. ● The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view. Passive View Model Presenter user events updates model updates view state-change event

Slide 10

Slide 10 text

Model View Presenter on Android #1 public interface MyView { void hideLoading(); void showLoading(); void renderItems(final Collection items); } Let’s define view interface

Slide 11

Slide 11 text

roid #2 public class MyViewPresenter extends Presenter { private MyView view; public void setView(MyView view) { this.view = view; } @Override public void initialize() { view.showLoading(); //do some heavy operations view.renderItems(...); view.hideLoading(); } Implement presenter

Slide 12

Slide 12 text

roid #2 Implement our view public class MyViewImpl extends Activity implements MyView { private MyViewPresenter presenter; @InjectView(R.id.progress_bar) ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.home_view); ButterKnife.inject(this); this.presenter = new MyViewPresenter(this); this.presenter.initialize(); } //TODO implement rest of MyView methods

Slide 13

Slide 13 text

BUT CAN BE BETTER!

Slide 14

Slide 14 text

MVVM?

Slide 15

Slide 15 text

Say hello to Model-View-ViewModel

Slide 16

Slide 16 text

Model-View-ViewModel VIEW VIEWMODEL MODEL UI Events PropertyChanged events ViewModel data Read Update Model change events

Slide 17

Slide 17 text

MVVMCross

Slide 18

Slide 18 text

MVVMCross - Activity (View) namespace App.Views { [Activity(Label = "MyView")] public class MyView : MvxActivity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.LunchDetails); } } }

Slide 19

Slide 19 text

MVVMCross - Activity (View) namespace App.Views { [Activity(Label = "MyView")] public class MyView : MvxActivity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.LunchDetails); } } }

Slide 20

Slide 20 text

MVVMCross - Activity (Layout)

Slide 21

Slide 21 text

MVVMCross - Activity (Layout)

Slide 22

Slide 22 text

MVVMCross - ViewModel public class MyViewModel : MvxViewModel { public IMvxCommand AddNewCommand { get { return new MvxCommand(Open); } } private List _items; public List Items { get { return _items; } set { _items = value; RaisePropertyChanged(() => Items); } } // rest of code

Slide 23

Slide 23 text

MVVMCross - ViewModel public class MyViewModel : MvxViewModel { public IMvxCommand AddNewCommand { get { return new MvxCommand(Open); } } private List _items; public List Items { get { return _items; } set { _items = value; RaisePropertyChanged(() => Items); } } // rest of code

Slide 24

Slide 24 text

MVVMCross • Pros • TwoWay binding • Easy to extend • Supports Lists • Reuse ViewModel across iOS/Android/Windows Phone • Used by many developers, many apps published to Google Play/AppStore etc. • True MVVM similar to WPF • Cons • Only for Xamarin/C# • May need some additional work in order to make it work with AppCompat Library

Slide 25

Slide 25 text

RoboBinding

Slide 26

Slide 26 text

RoboBinding - setup #1 buildscript { dependencies { classpath 'com.android.tools.build:gradle:xxx' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } }

Slide 27

Slide 27 text

RoboBinding - setup #2 apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' apt("org.robobinding:codegen:$robobindingVersion") { exclude group: 'com.google.android', module: 'android' } compile("org.robobinding:robobinding:$robobindingVersion") { exclude group: 'com.google.android', module: 'android' }

Slide 28

Slide 28 text

Android & RoboBinding - hello world @PresentationModel public class MainViewViewModel implements HasPresentationModelChangeSupport { private PresentationModelChangeSupport changeSupport; private String name; public MainViewViewModel() { changeSupport = new PresentationModelChangeSupport(this); } public String getHello() { return name + ": hello Android MVVM(Presentation Model)!"; } public String getName()..... public void setName(String name) …. public void sayHello() { changeSupport.firePropertyChange("hello"); } @Override public PresentationModelChangeSupport getPresentationModelChangeSupport() { return changeSupport;

Slide 29

Slide 29 text

Android & RoboBinding - hello world @PresentationModel public class MainViewViewModel implements HasPresentationModelChangeSupport { private PresentationModelChangeSupport changeSupport; private String name; public MainViewViewModel() { changeSupport = new PresentationModelChangeSupport(this); } public String getHello() { return name + ": hello Android MVVM(Presentation Model)!"; } public String getName()..... public void setName(String name) …. public void sayHello() { changeSupport.firePropertyChange("hello"); } @Override public PresentationModelChangeSupport getPresentationModelChangeSupport() { return changeSupport;

Slide 30

Slide 30 text

Android & Robobinding- hello world public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainViewViewModel presentationModel = new MainViewViewModel(); View rootView = Binders.inflateAndBindWithoutPreInitializingViews(this, R.layout.activity_main, presentationModel); setContentView(rootView); } }

Slide 31

Slide 31 text

Android & Robobinding - hello world public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainViewViewModel presentationModel = new MainViewViewModel(); View rootView = Binders.inflateAndBindWithoutPreInitializingViews(this, R.layout.activity_main, presentationModel); setContentView(rootView); } }

Slide 32

Slide 32 text

Android & Robobinding - hello world

Slide 33

Slide 33 text

Android & Robobinding - hello world

Slide 34

Slide 34 text

RoboBinding • Pros • TwoWay binding • Code generation • Easy to extend • Supports Lists • Cons • No support for RecyclerView • Problems with AppCompat library • No support for Lists with two and more types of item layout https://github.com/radzio/RoboBindingDemo

Slide 35

Slide 35 text

ngAndroid

Slide 36

Slide 36 text

ngAndroid @NgScope public class NgModelFragment extends Fragment { private final NgAndroid ng = NgAndroid.getInstance(); @NgModel NgMod mod; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return ng.inflate(this, inflater, R.layout.activity_ng_model, container, false); } }

Slide 37

Slide 37 text

ngAndroid @NgScope public class NgModelFragment extends Fragment { private final NgAndroid ng = NgAndroid.getInstance(); @NgModel NgMod mod; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return ng.inflate(this, inflater, R.layout.activity_ng_model, container, false); } }

Slide 38

Slide 38 text

ngAndroid public class NgMod { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } }

Slide 39

Slide 39 text

ngAndroid // tools:ignore="MissingPrefix"

Slide 40

Slide 40 text

ngAndroid // tools:ignore="MissingPrefix" TextView android:id="@+id/tvStr" style="@style/TextStyle" ng:ngModel="mod.str" />

Slide 41

Slide 41 text

• Pros • TwoWay binding • Code generation • Cons • No support for lists / RecyclerView • It is NOT a production ready library ngAndroid

Slide 42

Slide 42 text

Bindroid

Slide 43

Slide 43 text

Bindroid public class ViewModel { private TrackableField stringValue = new TrackableField("Hello, world!"); private TrackableField> dates = new TrackableField>(); public String getStringValue() { return stringValue.get(); } public void setStringValue(String value) { stringValue.set(value); } public TrackableCollection getDates() { return dates.get(); }

Slide 44

Slide 44 text

Bindroid public class ViewModel { private TrackableField stringValue = new TrackableField("Hello, world!"); private TrackableField> dates = new TrackableField>(); public String getStringValue() { return stringValue.get(); } public void setStringValue(String value) { stringValue.set(value); } public TrackableCollection getDates() { return dates.get(); }

Slide 45

Slide 45 text

Bindroid @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewModel model = new ViewModel(); UiBinder.bind(new EditTextTextProperty(this.findViewById(R.id.TextField)), model, "StringValue", BindingMode.TWO_WAY); UiBinder.bind(this, R.id.ListView, "Adapter", model, "Dates", BindingMode.ONE_WAY, new AdapterConverter(DateView.class)); }

Slide 46

Slide 46 text

Bindroid @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewModel model = new ViewModel(); UiBinder.bind(new EditTextTextProperty(this.findViewById(R.id.TextField)), model, "StringValue", BindingMode.TWO_WAY); UiBinder.bind(this, R.id.ListView, "Adapter", model, "Dates", BindingMode.ONE_WAY, new AdapterConverter(DateView.class)); }

Slide 47

Slide 47 text

Bindroid • Pros • TwoWay binding • Works with AppCompat library • Converters support • Cons • No code generation • No compile-time check • Too much code for binding

Slide 48

Slide 48 text

https://i.imgur.com/Lf7MNXE.jpg One Ring Library to rule them all, One Ring Library to bring them all and (….) bind them

Slide 49

Slide 49 text

Android Data Binding Library

Slide 50

Slide 50 text

Android Data Binding Library • Announced at Google IO 15 • Created by Google • Currently available as beta

Slide 51

Slide 51 text

Android Data Binding Library – how to start classpath "com.android.tools.build:gradle:1.2.3" classpath "com.android.databinding:dataBinder:1.0-rc0" apply plugin: ‘com.android.application' apply plugin: 'com.android.databinding'

Slide 52

Slide 52 text

Android Data Binding Library – how to start classpath "com.android.tools.build:gradle:1.2.3" classpath "com.android.databinding:dataBinder:1.0-rc0" apply plugin: ‘com.android.application' apply plugin: 'com.android.databinding'

Slide 53

Slide 53 text

Android Data Binding Library – how to start

Slide 54

Slide 54 text

Android Data Binding Library – how to start

Slide 55

Slide 55 text

Android Data Binding Library – how to start public class UsersViewModel extends BaseObservable { @Bindable public ObservableArrayList users; public UsersViewModel() { this.users = new ObservableArrayList<>(); } public void addUser(String name, String surname) { this.users.add(new UserViewModel(new User(name, surname))); } }

Slide 56

Slide 56 text

Android Data Binding Library – how to start public class UsersViewModel extends BaseObservable { @Bindable public ObservableArrayList users; public UsersViewModel() { this.users = new ObservableArrayList<>(); } public void addUser(String name, String surname) { this.users.add(new UserViewModel(new User(name, surname))); } }

Slide 57

Slide 57 text

Android Data Binding Library – how to start @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); usersViewModel = new UsersViewModel(); usersViewModel.users.add(new SuperUserViewModel(new User("Android", "Dev"))); binding = DataBindingUtil.setContentView(this, R.layout.users_view); binding.setUsersViewModel(usersViewModel); binding.setView(this); binding.activityUsersRecycler.setLayoutManager(new LinearLayoutManager(this)); }

Slide 58

Slide 58 text

Android Data Binding Library – how to start @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); usersViewModel = new UsersViewModel(); usersViewModel.users.add(new SuperUserViewModel(new User("Android", "Dev"))); binding = DataBindingUtil.setContentView(this, R.layout.users_view); binding.setUsersViewModel(usersViewModel); binding.setView(this); binding.activityUsersRecycler.setLayoutManager(new LinearLayoutManager(this)); }

Slide 59

Slide 59 text

Android Data Binding Library – there is more! • Expression language android:text='@{String.format("Hello %s",viewModel.field )}‚ • Imports • Support for resources android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}„ • Built in ButterKnife functionality binding.myButton.setOnClickListener(new …); • Ability to create custom bindings • And more!

Slide 60

Slide 60 text

https://github.com/radzio/android-data-binding-recyclerview Demo

Slide 61

Slide 61 text

Android Data Binding Library • Pros • Official library from Google  • Code generation • Compile time check • Easy to use and extend • Expression language! • New standard • Cons • No TwoWay binding (yet) • Currently NO IDE support • A lot of false positive errors in Android Studio, but code is compiling and running

Slide 62

Slide 62 text

Takeaway #1 • Learn and use MVP pattern • Get familiar with MVVM and try Android Data Binding Library • Write Unit Tests for your Presenters and ViewModels 

Slide 63

Slide 63 text

Takeaway #2 https://github.com/depoll/bindroid https://github.com/davityle/ngAndroid https://github.com/RoboBinding/RoboBinding https://github.com/MvvmCross/MvvmCross https://github.com/radzio/android-data-binding-recyclerview https://github.com/radzio/RoboBindingDemo https://developer.android.com/tools/data-binding/guide.html +RadoslawPiekarz @radzio

Slide 64

Slide 64 text

Q&A +RadoslawPiekarz @radzio

Slide 65

Slide 65 text

Thank you! +RadoslawPiekarz @radzio