{"id":13702238,"url":"https://github.com/manas-chaudhari/android-mvvm","last_synced_at":"2026-01-14T03:21:04.382Z","repository":{"id":46027443,"uuid":"62959750","full_name":"manas-chaudhari/android-mvvm","owner":"manas-chaudhari","description":"MVVM on Android using RxJava and Data Binding","archived":false,"fork":false,"pushed_at":"2021-11-19T06:25:49.000Z","size":387,"stargazers_count":500,"open_issues_count":16,"forks_count":79,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-05-05T04:36:13.974Z","etag":null,"topics":["android-architecture","databinding","mvvm","rxjava"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manas-chaudhari.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-09T16:58:45.000Z","updated_at":"2025-04-26T00:23:40.000Z","dependencies_parsed_at":"2022-09-26T18:41:59.228Z","dependency_job_id":null,"html_url":"https://github.com/manas-chaudhari/android-mvvm","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/manas-chaudhari/android-mvvm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manas-chaudhari%2Fandroid-mvvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manas-chaudhari%2Fandroid-mvvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manas-chaudhari%2Fandroid-mvvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manas-chaudhari%2Fandroid-mvvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manas-chaudhari","download_url":"https://codeload.github.com/manas-chaudhari/android-mvvm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manas-chaudhari%2Fandroid-mvvm/sbom","scorecard":{"id":614692,"data":{"date":"2025-08-11","repo":{"name":"github.com/manas-chaudhari/android-mvvm","commit":"1ac808e7f60a34df94f04551c4b75ed258fddb1f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 2/23 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 13 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T03:35:28.627Z","repository_id":46027443,"created_at":"2025-08-21T03:35:28.628Z","updated_at":"2025-08-21T03:35:28.628Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408825,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-architecture","databinding","mvvm","rxjava"],"created_at":"2024-08-02T21:00:32.666Z","updated_at":"2026-01-14T03:21:04.367Z","avatar_url":"https://github.com/manas-chaudhari.png","language":"Java","funding_links":[],"categories":["Uncategorized","Java"],"sub_categories":["Uncategorized"],"readme":"# Android MVVM\n[![Build Status](https://travis-ci.org/manas-chaudhari/android-mvvm.svg?branch=master)](https://travis-ci.org/manas-chaudhari/android-mvvm) [ ![Download](https://api.bintray.com/packages/manas-chaudhari/maven/android-mvvm/images/download.svg) ](https://bintray.com/manas-chaudhari/maven/android-mvvm/_latestVersion)\n\nContents:\n\n1. Sample project to demonstrate a coding pattern based on MVVM with focus on\n    1. Easy composition of views\n    1. Minimising boilerplate of setting up views\n    1. Reuse of presentation logic\n1. A library with essential tools for the pattern\n\n## Prerequisites\n\n1. [Android Data Binding](https://developer.android.com/topic/libraries/data-binding/index.html)\n1. [RxJava](https://github.com/ReactiveX/RxJava)\n    - [2-minute intro to Rx](https://medium.com/@andrestaltz/2-minute-introduction-to-rx-24c8ca793877#.dh92eypp8)\n    - [Grokking RxJava](http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/)\n    - [Intro to Reactive Programming by André Staltz](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)\n    - [Comprehensive list](http://reactivex.io/tutorials.html)\n1. [Introduction to this pattern @DroidconIN](https://www.youtube.com/watch?v=JaB8SXCSbgg\u0026t=1s)\n\n## Quick Tutorial\n\n[Getting Started](Documentation/GettingStarted.md) provides a tutorial to setup the library and gives an idea about its functionality. As the main deliverable of this library is the pattern, it is important to understand the principles behind it, so that the pattern can be applied even at places where the library APIs aren't available.\n\n## MVVM Implementation\n\nThis pattern makes use of Data Binding, such that views contain exactly 1 variable `vm` i.e. ViewModel. Idea is that the ViewModel should have all information required to display the View. Multiple views can share a single view model. This helps in reusing functionality for a different layout.\n\nUsing a single variable `vm` provides a consistent mechanism to configure any View:\n```\nviewBinding.setVariable(BR.vm, viewModel)\n```\n\nNote that this mechanism needs to be configured by providing an instance of `ViewModelBinder` interface. This interface describes how a ViewModel is bound to a View. The variable name to be used in xmls (`vm` in this example) should be specified here.\n\n```java\nBindingUtils.setDefaultBinder(new ViewModelBinder() {\n    @Override\n    public void bind(ViewDataBinding viewDataBinding, ViewModel viewModel) {\n        viewDataBinding.setVariable(BR.vm, viewModel);\n    }\n});\n```\nThis code can be placed in your `onCreate` method of Application/Activity.\n\n\n## Creating a View Model\nA view model is a POJO formed by fields for storing data (for input/output) and event listeners. In MVVM, interaction between ViewModel and View happens via changes in data OR method calls. These interactions can be categorized based on direction \u0026 type of data flow:\n\n1. **Output Data (ViewModel -\u003e View)** ViewModel changes its data, View listens for changes \u0026 updates itself\n    Example: ViewModel is running a timer, and updating its `timeText` variable. View is listening for changes and updating itself whenever it gets a callback.\n\n1. **Input Data (View -\u003e ViewModel)** View requests ViewModel to update its data (such as capturing text input)\n    Example: Form containing various inputs like `text` for `EditText`, `isChecked` for `Checkbox`.\n\n1. **Input Events (View -\u003e ViewModel)** View invokes functions on ViewModel when certain events occur (such as onSwipe/onClick/onFocused)\n    Note the difference between `Data` and `Event`. Any input can be represented as an `Event`. However, wherever there is persistence in input, representing them as `Data` is convenient.\n    For example, if a user has typed `Googl` in EditText, this input is represented as a String variable with value `Googl`. When user types `e`, the text changed \"event\" causes the data to change to `Google`. Working with data is better in this example.\n    Consider an example of a Button click. There is no data change that's implicitly happening on click. Hence, this input is represented as an `Event`.\n\nNow, we'll see how these three interactions are implemented in this pattern.\n\n### Output Data\nThis is stored as a field in the ViewModel class. If the value is constant, it can be simply declared as a `final TYPE`. If its changing, it is declared as an `ObservableField\u003cTYPE\u003e`, provided by data binding. Example for displaying text (String)\n\n```java\npublic class OutputDataViewModel {\n  public final String constantOutput = \"\";\n  public final ObservableField\u003cString\u003e changingOutput = new ObservableField\u003cString\u003e(\"\");\n}\n```\n\u003e Note that everything is `final`. To modify the `changingOutput`, its `set` function is used. Example: `this.changingOutput.set(\"new value\");`. Data binding takes care of adding listeners and updating the view.\n\nIn XML, this data is displayed by referring to these fields.\n\n```xml\n\u003cTextView ...\n  android:text=\"@{vm.constantOutput}\" /\u003e\n\n\u003cTextView ...\n  android:text=\"@{vm.changingOutput}\" /\u003e\n```\n\n\n### Input Data\nAs input data implies that it cannot be constant, it is always stored as an `ObservableField\u003cTYPE\u003e`.\n\n```java\npublic class InputDataViewModel {\n  public final ObservableField\u003cString\u003e inputText = new ObservableField\u003cString\u003e(\"\");\n}\n```\n\nBecause we want the view to update the value of this `ObservableField`, [Two Way Binding](https://medium.com/google-developers/android-data-binding-lets-flip-this-thing-dc17792d6c24#.eee8cuo08) is used.\n```xml\n\u003cEditText ...\n  android:text=\"@={vm.inputText}\" /\u003e\n```\n\u003e The `@=` enables two way binding\n\n\n### Input Events\nEventListeners can be implemented simply as methods in ViewModel\n```java\npublic class EventViewModel {\n  MessageHelper messageHelper; // This is an external dependency\n\n  public void onClick() {\n    messageHelper.show(\"Something got clicked\");\n  }  \n}\n```\n```xml\n\u003cButton\n  android:onClick=\"@{(v) -\u003e vm.onClick()}\"/\u003e\n```\n\n#### Event Listeners using `Runnable`\nAnother approach is to implement listeners as `Runnable` fields inside ViewModel.\n\n```java\npublic class EventViewModel {\n  MessageHelper messageHelper; // This is an external dependency\n\n  public final Runnable onClick = () -\u003e {\n    messageHelper.show(\"Something got clicked\");    \n  };\n}\n```\n\n```xml\n\u003cButton\n  android:onClick=\"@{vm.onClick}\"/\u003e\n```\n\nFor this to work, a `BindingConversion` between `View.OnClickListener` and `Runnable` needs to be defined.\n```java\n@BindingConversion\npublic static View.OnClickListener toOnClickListener(final Runnable runnable) {\n    if (runnable != null) {\n        return () -\u003e runnable.call();\n    } else {\n        return null;\n    }\n}\n```\nThis static function can be placed anywhere in your code.\n\n#### Event Listeners using `rx.PublishSubject`\n\nUsing a `BindingAdapter`/`BindingConversion`, click event can be bound through a `PublishSubject`.\n```java\nclass ItemViewModel {\n  public final PublishSubject\u003cItemViewModel\u003e onSelect;\n}\n\n// When working with a list of view models, its easy to merge events.\nObservable\u003cItem\u003e onAnyItemSelect = itemViewModels.map { vm -\u003e vm.onSubmit }.merge().map { vm -\u003e vm.item };\n```\n\n\u003e There are various ways to define Event handlers. There are no compelling points to stick to a fixed way. Use the approach which suits you the best\n\n## ViewModel is unaware about the View\n\nThis is the core principle behind MVVM. Naturally, ViewModel cannot have a reference to `View` or any subclass. In addition to these, ViewModel cannot know about any platform-specific implementation details. Thus, Android classes such as `Activity`, `Fragment`, `Context` cannot be referenced.\n\n\u003e This also means that the ViewModel logic is same across different platforms such as iOS or web\n\nAny platform-dependent functionality is abstracted into an `interface` and then provided to ViewModel. In the above example, `MessageHelper` is an `interface` for displaying a message to user. The activity can choose to implement it using `Toast` or any other mechanism. Keeping these implementations outside allows sharing them. For example, `MessageHelper` can be implemented in a `BaseActivity`, which could be a base class for all activities.\n\nIn a deep hierarchy, fulfilling such dependencies can result in a lot of boilerplate. In that case, it is recommended to use dependency injection libraries to keep things clean. A minimal example using [Dagger2](http://google.github.io/dagger/) is available on [extras/dagger](https://github.com/manas-chaudhari/android-mvvm/tree/extras/dagger) branch.\n\n### Testability\n\nAs ViewModels do not reference Android classes, testing them is straight forward. Tests are written as plain Unit Tests. As all dependencies are interfaces, they get easily mocked.\n\n```java\n@Test\npublic void detailsPage_isOpened_onClick() throws Exception {\n    Item item = new Item(\"Item 1\", null);\n    Navigator mockNavigator = mock(Navigator.class);\n    ItemViewModel viewModel = new ItemViewModel(item, mockNavigator);\n\n    viewModel.itemClicked.call();\n\n    verify(mockNavigator).openDetailsPage(item);\n}\n```\n\u003e This example uses [Mockito](https://github.com/mockito/mockito) framework for mocking\n\n\n## Using RxJava\n\nRxJava provides a great bunch of operators for handling changes. For example, if a dynamic field of a model needs to be formatted before displaying, it is convenient to store it as an `io.reactivex.Observable\u003cTYPE\u003e`. The `map` operator can be used to format the values.\n\n`FieldUtils` class provides methods for converting between RxJava's `Observable` and Data Binding's `ObservableField` types. This allows the use of RxJava's operators to manipulate the data.\n\n### Rx -\u003e DataBinding\n\nExample: `Cart` has a `getTotalAmount()` method that returns an `Observable\u003cFloat\u003e`. Amount needs to be formatted before displaying. This can be implemented as follows:\n\n```java\npublic final ReadOnlyField\u003cString\u003e totalAmountText;\npublic CartViewModel(Cart cart) {\n  totalAmountText = FieldUtils.toField(cart.getTotalAmount().map(a -\u003e a + \" Rs\"));\n}\n```\n\nThe `toField` method returns an instance of `ReadOnlyField` which extends `ObservableField`. Note that `set` method in `ReadOnlyField` does nothing. See [Observables And Setters](Documentation/ObservablesAndSetters.md) for the rationale behind this.\n\n\n### DataBinding -\u003e Rx\n\nExample: Error needs to be shown if input text is empty.\n\n```java\nstatic import FieldUtils.toObservable;\nstatic import FieldUtils.toField;\n\npublic final ObservableField\u003cString\u003e inputText = new ObservableField\u003c\u003e(\"\");\npublic final ReadOnlyField\u003cBoolean\u003e errorVisible = toField(toObservable(inputText).map(text -\u003e text.isEmpty()));\n```\n\n```xml\n\u003cEditText\n  android:text=\"@={vm.inputText}\"/\u003e\n\u003cErrorView\n  android:visibility=\"@{vm.errorVisible}\"/\u003e\n```\n\nA binding adapter would be required to use boolean for `visibility` attribute.\n```java\n@BindingAdapter(\"android:visibility\")\npublic static void bindVisibility(@NonNull View view, @Nullable Boolean visible) {\n    int visibility = (visible != null \u0026\u0026 visible) ? View.VISIBLE : View.GONE;\n    view.setVisibility(visibility);\n}\n```\n\nSee [SearchViewModel.java](sample/src/main/java/com/manaschaudhari/android_mvvm/sample/two_way_binding/SearchViewModel.java) and the corresponding [activity_search.xml](sample/src/main/res/layout/activity_search.xml) for another example in which the search results get updated as user updates the query.\n\n### RxJava versions\n\nBoth RxJava 1.x and 2.x are supported. For RxJava 1.x support, an additional dependency needs to be added:\n```\ncompile 'com.manaschaudhari.android-mvvm:rxjava-compat:x.y.z'\n\n// Under android config\nandroid {\n    packagingOptions {\n        exclude 'META-INF/rxjava.properties'\n    }\n}\n```\n\u003e Note that rxjava-compat internally depends on both RxJava2 and RxJava\n\nThis adds equivalent [BindingAdapters](#supported-attributes) for `rx.Observable`. This also includes `rxjava.FieldUtils` class for conversions between `rx.Observable` and `databinding.ObservableField`.\nThus, the syntax is same except for the imports.\n\nSee [SearchViewModel.java(rxjava-compat)](sample/src/main/java/com/manaschaudhari/android_mvvm/sample/two_way_binding/rxjava/SearchViewModel.java) for an example implemented using `rxjava-compat`.\n\n\n## Code-less View Setup\n\n**Views are setup using XML ONLY**. This is the core idea behind this pattern. `BindingAdapter`s are used to create a declarative API with essential arguments.\n\nThus, whenever it is required to write code for setting up a view, create a `BindingAdapter` instead and use XML attributes.\n\nThis also applies to complex views such as `RecyclerView`/`ViewPager` which need setting up adapters. These views are containers which display (multiple) child views. This particular operation of nesting views is termed as `View Composition`. As the choice of pattern (MVVM/MVP) greatly affects how `View Composition` is done, this library provides [tools](#view-composition) to deal with this aspect. However, there are other scenarios where code is required to setup views. Such code gets moved to a `BindingAdapter` and the view is setup using XML attributes.\n\nThe [BindingUtils](android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java) file contains `BindingAdapter`s provided by this library. These are good examples to learn about this approach.\n\n\u003e If this is not doable in some case, it implies that you need a functionality that's not provided by existing views. Create a custom View in that case.\n\n\n## View Composition\n\nJust like code, UI also needs to be reused at multiple pages. Just like classes are split into smaller classes, views are split into smaller views to make them reusable.\n\nLets say a page requires to combine 3 functionalities. There can be 1 ViewModel to represent each functionality. Similar to how layout hierarchy is created using `\u003cinclude\u003e`, a parent ViewModel is created that contains child ViewModels as fields. Data Binding allows binding included layout's variables.\n\n```xml\n\u003cParentLayout\u003e\n  \u003cinclude layout=\"@layout/child_view\"\n    bind:vm=\"@{vm.childVm}\" /\u003e\n\u003c/ParentLayout/\u003e\n```\n\nThus, with a simple `\u003cinclude\u003e`, this layout gets added on the page. The only Java code required is to add the `childVm` field in the outer ViewModel.\n\n### Composing a dynamic list of functionalities\n\nIt is very common to display a dynamic number of views in a RecyclerView or a ViewPager. The type of each child view could also vary based on some data. Writing a new adapter to support different view types results in duplicate code.\n\nWith MVVM, as we have a consistent mechanism to setup any view, it is now possible to write abstract adapters, which can be used for displaying any type of views. This reduces a lot of boilerplate. For example, a RecyclerView can be setup with these two inputs:\n- `Observable\u003cList\u003cViewModel\u003e\u003e`: A list of ViewModels. The adapter notifies itself when the list updates\n- `ViewProvider`: An interface which decides which View should be used for a ViewModel\n\nUsing Data Binding, we can create attributes so that these inputs can be provided in XML:\n\n```xml\n\u003candroid.support.v7.widget.RecyclerView\n    bind:items=\"@{vm.itemVms}\"\n    bind:view_provider=\"@{@layout/row_item_without_image}\" /\u003e\n```\n\nThis creates a nice declarative API to setup views like RecyclerView/ViewPager.\n\n#### Using different views\n\nStatic methods are defined which return custom instances of `ViewProvider`.\n```java\npublic class ViewProviders {\n  public static ViewProvider getItemListing() {\n    return new ViewProvider() {\n              @Override\n              public int getView(ViewModel vm) {\n                if (vm instanceof ItemViewModel) {\n                  return ((ItemViewModel) vm).hasImage() ? R.layout.row_item_with_image : R.layout.row_item_without_image;\n                } else if (vm instanceof SomeOtherViewModel) {\n                  return R.layout.some_other_view;\n                }\n                return 0;\n              }\n            };\n  }\n}\n```\n\nThis method is referenced in XML when setting up the view.\n```xml\n\u003cimport type=\"ViewProviders\" /\u003e\n\n\u003c!--Example with dynamic views--\u003e\n\u003candroid.support.v7.widget.RecyclerView\n    bind:items=\"@{vm.itemVms}\"\n    bind:layout_vertical=\"@{true}\"\n    bind:view_provider=\"@{ViewProviders.itemListing}\" /\u003e\n```\n\n\n### Supported Attributes\n\nFollowing attributes are provided with this library.\n\n```java\n@BindingAdapter({\"items\", \"view_provider\"})\npublic static void bindAdapterWithDefaultBinder(RecyclerView recyclerView, Observable\u003cList\u003cViewModel\u003e\u003e items, ViewProvider viewProvider);\n\n@BindingAdapter({\"items\", \"view_provider\"})\npublic static void bindAdapterWithDefaultBinder(ViewPager viewPager, Observable\u003cList\u003cViewModel\u003e\u003e items, ViewProvider viewProvider);\n\n@BindingConversion\npublic static ViewProvider getViewProviderForStaticLayout(@LayoutRes final int layoutId);\n\n@BindingConversion\npublic static \u003cT extends ViewModel\u003e Observable\u003cList\u003cViewModel\u003e\u003e toListObservable List\u003cT\u003e specificList);\n\n@BindingAdapter(\"layout_vertical\")\npublic static void bindLayoutManager(RecyclerView recyclerView, boolean vertical);\n```\n\nCheck the source ([BindingUtils.java](android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java)) to know how these work.\n\n#### What if I need to use some other View?\n\nEvery application has different requirements. It may not be feasible to create a generic API that works well for all usecases. This project aims to provide a pattern so you can build your own custom XML attributes that fulfill your usecase. You can use [BindingUtils.java](android-mvvm/src/main/java/com/manaschaudhari/android_mvvm/utils/BindingUtils.java) as a reference to roll out your own attributes.\n\n#### What if I want to customize these adapters?\nAlthough BindingAdapters can be overridden, it hasn't been specified how databinding resolves the conflicts. Based on experiments, adapters in client project are preferred over adapters from library. However, having identical adapters in a same module will result in undeterministic results.\n\nThe sample project overrides these BindingAdapters to check memory leaks.\n\n\n### Composition Strategy\n\nHere are some scenarios, and the way in which this pattern resolves them:\n\n### Multiple layouts for displaying same information\nA common view model that can bind to all views.\n\n### Two layouts, which share some common functionality\nThere are many ways depending on the situation.\n- Extract common functionality into one child ViewModel. Both view models keep a reference of child ViewModel\n- Two view models, one extending the other\n- Two view models which extend from a common base\n- Single view model with all functionality\n  - This approach is possible only because ViewModel has no dependency to a view. In architectures like MVP, this is difficult, as the Presenter has a dependency on a View\n\n\n## Functional ViewModels\n\nRxJava provides several operators to compose Observables. Conversion between `rx.Observable` and `ViewModel` fields, enables the use of all these operators in ViewModels, which eliminates the need for mutable state (in most cases).\n\nConsider an example of showing ProgressBar while api is loading. Traditional example:\n```java\nvoid load() {\n  displayProgress();\n  service.loadData(new Callback\u003cString\u003e() {\n    @Override\n    void onSuccess(String data) {\n      displayData(data);\n    }\n\n    @Override\n    void onError() {\n      displayError();\n    }\n  });\n}\n```\n\nBy keeping loadedData as an `Observable`, we can derive progressVisibility by making use of the [Using](http://reactivex.io/documentation/operators/using.html) operator. From `progressVisibility` and `loadedData`, `errorVisibility` can be derived. Thus, there are no mutable states, only mapping from one Observable to other. Also, note that there is no need for subscriptions inside ViewModel as View will subscribe to the data after binding.\n\nSee [DataLoadingViewModel.java](sample/src/main/java/com/manaschaudhari/android_mvvm/sample/functional/DataLoadingViewModel.java) for this example.\n\n### Lifecycle\n\nViewModels are unaware about lifecycle of View. This means that ViewModel code comes into action only when View invokes it. ViewModel simply defines the logic of transforming inputs to outputs. This is similar to `pure functions` from functional programming, which provide output based on its inputs only.\n\nThere are scenarios where ViewModel needs to know about lifecycle of the View. This feature is in the roadmap. Do contribute!\n\n## More Information\n[Wiki](https://github.com/manas-chaudhari/android-mvvm/wiki) contains links to more content around this topic.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanas-chaudhari%2Fandroid-mvvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanas-chaudhari%2Fandroid-mvvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanas-chaudhari%2Fandroid-mvvm/lists"}