MVVM & Data Binding Library

31
MVVM & Data Binding Library Wojciech Topolski

Transcript of MVVM & Data Binding Library

Page 1: MVVM & Data Binding Library

MVVM & Data Binding Library

Wojciech Topolski

Page 2: MVVM & Data Binding Library

Agenda• Theory of MVVM

• Data Binding Library

• Custom MVVM(C) implementation

• View Model Composition

• Binding & RecycleView

• Grabbing value directly from View

• Links

Page 3: MVVM & Data Binding Library

Theory of MVVM

• "MVVM facilitates a separation of development of the graphical user interface from development of the business logic. The view model of MVVM is a value converter; meaning the view model is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented. In this respect, the view model is more model than view, and handles most if not all of the view's display logic. The view model may implement a mediator pattern, organizing access to the back-end logic around the set of use cases supported by the view.” (Wikipedia)

• Model - Business data layer, it could be set of POJO objects or layer operating on database/network/cache. Must be independent of user interface.

• View - User interface.

• View Model - It is responsible for converting data between Model and View, visibility of View components.

Page 4: MVVM & Data Binding Library

Data Binding Libraryprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MainActivityBinding binding; binding = DataBindingUtil.setContentView(this, R.layout.main_activity); User user = new User("Test", "User"); binding.setUser(user); }

<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout ...> <TextView android:text="@{user.firstName}"/> <TextView android:text="@{user.lastName}"/> </LinearLayout></layout>

BY EXAMPLE

Android Plugin for Gradle 2.1+ https://developer.android.com/topic/libraries/data-binding/index.html

Page 5: MVVM & Data Binding Library

Data Binding LibraryVariables and imports in <data>...</data> section.

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <import type="android.view.View" /> <variable name="viewModel" type="com.github.wtopolski.libmvvm.viewmodel.ButtonViewModel"/> </data> <Button . . . /> </layout>

FEATURE 1 / 15

Page 6: MVVM & Data Binding Library

Data Binding LibraryWidgets are available by binding.id expression, where id is equal to content of android:id attribute. However, android:id is not obligatory at all.

<TextView android:id=“@+id/someTextView" />

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

binding.someTextView.setText("XYZ");

FEATURE 2 / 15

Page 7: MVVM & Data Binding Library

Data Binding LibrarySetting almost every widget XML attribute is possible. Directly by setter paired with attribute like android:enabled and view.setEnabled(boolean). Or indirectly using BindingAdapter or BindingMethods.

<EditText android:enabled=“@{viewModel.enabled}" />

FEATURE 3 / 15

Page 8: MVVM & Data Binding Library

Data Binding LibraryObservable variables. Every data change could be reflected to View. "You can change your data model in a background thread as long as it is not a collection." public class BaseViewModel extends BaseObservable { private boolean enabled; public void setEnabled(boolean enabled) { this.enabled = enabled; notifyPropertyChanged(BR.enabled); } @Bindable public boolean getEnabled() { return enabled; } }

<EditText android:enabled=“@{viewModel.enabled}" />

FEATURE 4 / 15

Page 9: MVVM & Data Binding Library

Data Binding LibraryTwo-way data binding allows synchronization widget's state between user interface and passed variable. Main restriction is XML attribute must have listener indicating change of value. <EditText android:text="@={viewModel.value}" />

FEATURE 5 / 15

Page 10: MVVM & Data Binding Library

Data Binding LibraryEventHandling::MethodReference • "listener implementation is created when the data is bound”

• "expression is processed at compile time, so if the method does not exist or its signature is not correct, you receive a compile time error.”

• "method must match the parameters of the event listener”

<Button android:onClick="@{viewModel::onButtonClick}” />

public void onButtonClick(View view) { // Some action}

FEATURE 6 / 15

Page 11: MVVM & Data Binding Library

Data Binding Library() -> EventHandling.ListenerBindings() • "listener implementation is created when event is triggered”

• "return value must match the expected return value of the listener (unless it is expecting void)”

<Button android:onClick=“@{() -> viewModel.onButtonClick()}” />

public void onButtonClick() { // Some action}

FEATURE 7 / 15

Page 12: MVVM & Data Binding Library

Data Binding LibraryBindingAdapter allows to set any data using custom XML attribute. The same solution helps to write attributes for custom widgets. No more attrs.xml files! @BindingAdapter({"errorEnabled"}) public static void errorEnabled(TextInputLayout view, String value) { view.setError(value); view.setErrorEnabled(!TextUtils.isEmpty(value)); }

<android.support.design.widget.TextInputLayout bind:errorEnabled="@{viewModel.valueError}">

FEATURE 8 / 15

Page 13: MVVM & Data Binding Library

Data Binding LibraryBindingMethods "Some attributes have setters that don't match by name… For example, the android:tint attribute is really associated with setImageTintList(ColorStateList), not setTint."

@BindingMethods({ @BindingMethod( type = "android.widget.ImageView", attribute = "android:tint", method = "setImageTintList") })

FEATURE 9 / 15

Page 14: MVVM & Data Binding Library

Data Binding LibraryConverter helps to translate given value format into format which is expected by specific XML tag.

@BindingConversionpublic static ColorDrawable convertColorToDrawable(String color) { return new ColorDrawable(Color.parseColor(color)); }

<LinearLayout android:background="@{viewModel.color}">

FEATURE 10 / 15

Page 15: MVVM & Data Binding Library

Data Binding LibraryIn custom XML attributes we can use application resources.

<TextView bind:textColor="@{@color/green}" /> <resources> <color name="green">#009900</color></resources>

FEATURE 11 / 15

Page 16: MVVM & Data Binding Library

Data Binding LibraryNull Coalescing Operator

<TextView android:text=“@{user.displayName ?? user.lastName}” />

FEATURE 12 / 15

Page 17: MVVM & Data Binding Library

Data Binding LibraryIncluding layouts. More in View Model Composition section.

<include layout="@layout/text_view" android:layout_width=“match_parent" android:layout_height=“wrap_content" bind:viewModel="@{viewModel.redDesc}" bind:textColor="@{@color/red}" />

FEATURE 13 / 15

Page 18: MVVM & Data Binding Library

Data Binding LibraryImmediate Binding. "When a variable or observable changes, the binding will be scheduled to change before the next frame...To force execution, use the executePendingBindings() method."

binding.executePendingBindings();

FEATURE 14 / 15

Page 19: MVVM & Data Binding Library

Data Binding LibraryData Binding doesn't use reflection. Binding classes are generated almost in real time by Android Studio, and they are part of application. You can find them in build directory.

.../build/intermediates/classes/debug/package_name/databinding/*

FEATURE 15 / 15

Page 20: MVVM & Data Binding Library

Custom MVVM(C) implementation• Bases on Data Binding Library and RxJava

• Layers:

• Model - business data layer (POJO, Database, Cache, Backend)

• View - user interface (XML and Binding)

• View Model - conversion between displayed data and stored data (model), validation of forms, etc…

• What’s about Activity and Fragment? Is it View or View Model?

• Or maybe MVVMC? Where:

• Controller - is responsible for global actions like open new Activities

• View Model - takes care about operation on single screen

https://github.com/wtopolski/androidmvvm

Page 21: MVVM & Data Binding Library

View Model Composition

Real app screens are complicated. Usually they contain several input and output widgets like EditText, Button, SeekBar, RecycleView, etc.

It would be useful to create set of Views and View Models. And then reuse them to composite user interface.

Page 22: MVVM & Data Binding Library

View Model Composition

Page 23: MVVM & Data Binding Library

View Model CompositionSeekBar View <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <import type="android.view.View" /> <variable name="viewModel" type="com.github.wtopolski.libmvvm.viewmodel.SeekBarViewModel"/> </data> <SeekBar android:layout_width="match_parent" android:layout_height="wrap_content" android:max="@{viewModel.max}" android:progress="@={viewModel.progress}" android:visibility="@{viewModel.visibility ? View.VISIBLE : View.GONE}" android:enabled="@{viewModel.enabled}" /></layout>

Page 24: MVVM & Data Binding Library

View Model CompositionSeekBar View Model public class SeekBarViewModel extends BaseViewModel { private int progress; private int max; private PublishSubject<Integer> progressObservable = PublishSubject.create(); public SeekBarViewModel() { super(); } public SeekBarViewModel(boolean enabled, boolean visibility) { super(enabled, visibility); } public void setProgress(int progress) { this.progress = progress; progressObservable.onNext(progress); notifyPropertyChanged(BR.progress); } @Bindable public int getProgress() { return progress; } public void setMax(int max) { this.max = max; notifyPropertyChanged(BR.max); } @Bindable public int getMax() { return max; } public Observable<Integer> rxProgress() { return progressObservable.asObservable(); } }

Page 25: MVVM & Data Binding Library

View Model Composition

activity_color_form.xml ColorFormViewModel.java

Page 26: MVVM & Data Binding Library

Binding & RecycleViewWe have to remember that this kind of screen has two sets of MVVM elements. One for list screen itself. And one for every item on the list.colors = generateColors();adapter = new ColorListAdapter(colors, getApplicationContext(), this);ActivityColorListBinding binding;binding = DataBindingUtil.setContentView(this, R.layout.activity_color_list);ColorListViewModel viewModel = new ColorListViewModel();binding.setViewModel(viewModel);viewModel.configure(true, new DefaultItemAnimator(), new LinearLayoutManager(this));viewModel.setAdapter(adapter);

https://github.com/wtopolski/androidmvvm

Page 27: MVVM & Data Binding Library

Binding & RecycleViewMore interesting is MVVM for list elements. First of all, our adapter is much more simple. No more custom ViewHolders for RecycleView. All we need is BindingViewHolder. public class BindingViewHolder extends RecyclerView.ViewHolder { private ViewDataBinding binding; public BindingViewHolder(@NonNull ViewDataBinding binding) { super(binding.getRoot()); this.binding = binding; } public ViewDataBinding getBinding() { return binding; } }

Page 28: MVVM & Data Binding Library

Binding & RecycleViewonCreateViewHolder

@Overridepublic BindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ActivityColorListItemBinding binding = DataBindingUtil.inflate( LayoutInflater.from(parent.getContext()), R.layout.activity_color_list_item, parent, false); return new BindingViewHolder(binding); }

Page 29: MVVM & Data Binding Library

Binding & RecycleViewonBindViewHolder @Overridepublic void onBindViewHolder(BindingViewHolder holder, int position) { ActivityColorListItemBinding binding = (ActivityColorListItemBinding) holder.getBinding(); ColorListItemViewModel viewModel = binding.getViewModel(); // Init new view model object if (viewModel == null) { viewModel = new ColorListItemViewModel(); binding.setViewModel(viewModel); binding.setTextColor(ContextCompat.getColor(context, R.color.colorAccent)); viewModel.setListener(listener); } ColorForm form = data.get(position); viewModel.setAdapterPosition(holder.getAdapterPosition()); viewModel.setData(form); // Immediate Binding binding.executePendingBindings(); }

Page 30: MVVM & Data Binding Library

Grabbing value directly from View

Getting value from View which is not available in ViewModel. private void checkEnabledStateOnView() { viewModel.confirm.setEditableObserver(new Subscriber<Boolean>() { . . . @Override public void onNext(Boolean isEnabled) { // Some code } }); binding.executePendingBindings(); }

bind:editableObserver="@{viewModel.editableObserver}"

@BindingAdapter({"editableObserver"}) public static void editableObserver(View view, Subscriber<Boolean> subscriber) { if (subscriber != null) { subscriber.onNext(view.isEnabled()); subscriber.onCompleted(); } }