{"id":13642717,"url":"https://github.com/tumblr/Graywater","last_synced_at":"2025-04-20T20:32:31.064Z","repository":{"id":43961930,"uuid":"90886387","full_name":"tumblr/Graywater","owner":"tumblr","description":"An Android library for decomposing RecyclerView layouts to improve scroll performance.","archived":false,"fork":false,"pushed_at":"2018-01-03T19:21:49.000Z","size":140,"stargazers_count":1209,"open_issues_count":1,"forks_count":84,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-12T22:38:39.885Z","etag":null,"topics":["android","android-library","performance","recyclerview-adapter"],"latest_commit_sha":null,"homepage":null,"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/tumblr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-10T16:37:37.000Z","updated_at":"2024-09-11T08:19:49.000Z","dependencies_parsed_at":"2022-09-08T21:51:51.502Z","dependency_job_id":null,"html_url":"https://github.com/tumblr/Graywater","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tumblr%2FGraywater","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tumblr%2FGraywater/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tumblr%2FGraywater/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tumblr%2FGraywater/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tumblr","download_url":"https://codeload.github.com/tumblr/Graywater/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249958942,"owners_count":21351740,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","android-library","performance","recyclerview-adapter"],"created_at":"2024-08-02T01:01:35.424Z","updated_at":"2025-04-20T20:32:31.029Z","avatar_url":"https://github.com/tumblr.png","language":"Java","funding_links":[],"categories":["RecyclerView"],"sub_categories":[],"readme":"# Graywater: an android library for performant lists\n\nGraywater is a [`RecyclerView`](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html) adapter that facilitates the performant decomposition of complex and varied list items. It does this by mapping large data models to multiple viewholders, splitting the work needed to create a complex list item over multiple frames.\n\nThe concept is based off of [Facebook's post on a faster news feed](https://code.facebook.com/posts/879498888759525/fast-rendering-news-feed-on-android/) and [Components for Android](https://code.facebook.com/posts/531104390396423/components-for-android-a-declarative-framework-for-efficient-uis/), which have been realized as [Litho](http://fblitho.com).\n\nTumblr developed Graywater to improve scroll performance, reduce memory usage, and lay the foundation for a more modular codebase.\n\nThe name \"Graywater\" comes from [the process of recycling water](https://en.wikipedia.org/wiki/Greywater).\n\n* [What is it?](#what-is-it)\n* [How do you use it?](#how-do-you-use-it)\n* [How does it work?](#how-does-it-work)\n* [Other features](#other-features)\n* [An addendum on binders and generics](#an-addendum-on-binders-and-generics)\n\n## What is it?\n\nAn adapter basically takes a list of models (of type `T`) and maps them to a list of viewholders (of type `VH extends RecyclerView.ViewHolder`).\n\nOne naive solution is to map models directly to viewholders. For example, a list of \"posts\" can have a viewholder for each post. But this architecture quickly becomes slow and unwieldy if there is either a large variety of posts or if individual posts are complex.\n\nSo to improve performance, the parts of a post that are offscreen can be recycled.\n\n```\n   model       views\n+---------+   +------+ \n|         |   | head | \u003c------- does not exist\n|         |   +------+ \u003c------------+\n| item #1 |   | body | \u003c---+        |\n|         |   +------+     |        |\n|         |   | foot |     |        |\n+---------+   +------+     |        |\n                           screen   view hierarchy\n+---------+   +------+     |        |\n|         |   | head |     |        |\n|         |   +------+     |        |\n| item #2 |   | body | \u003c---+        |\n|         |   +------+ \u003c------------+\n|         |   | body | \u003c------- does not exist\n+---------+   +------+\n```\n\nDue to Tumblr's needs, there are additional features that help improve performance and reduce memory usage:\n\n* Viewholders are shared between models of the same and different types, _e.g. a body viewholder can be shared between a item #1 and item #2_.\n* Models can have multiple viewholders of the same type, _e.g. an item can have an unlimited number of body viewholders_.\n\nThis results in a minimal number of viewholders to maximize cache effectiveness and reduce memory pressure.\n\nIn order to accomplish this, we introduce the concept of a **Binder**, which takes a model (`T`) and binds it to a viewholder (`VH`).\n\n```\n+-------+     +--------+     +------------+\n| Model | --\u003e | Binder | --\u003e | ViewHolder |\n+-------+     +--------+     +------------+\n```\n\nWe no longer desire the one-to-one relationship between models and viewholders that, because monolithic models result in monolithic viewholders. For example, a video post (`VideoPost`) used to have a corresponding `VideoPostViewHolder`. Instead, we want `VideoPost` to be composed of a header, body, and footer.\n\n```\n                           +--------+     +------------+\n                      /--\u003e | Binder | --\u003e | ViewHolder |\n+-------+     +---+  /     +--------+     +------------+\n| Model | --\u003e | ? | *----\u003e | Binder | --\u003e | ViewHolder |\n+-------+     +---+  \\     +--------+     +------------+\n                      \\--\u003e | Binder | --\u003e | ViewHolder |\n                           +--------+     +------------+\n```\n\nTo manage this relationship, we introduce the concept of an **ItemBinder**, which aggregates the binders needed to display a post. It takes a model (`T`) and returns a list of binders, each of which bind the model to a specific viewholder.\n\n```\n                 +------------+\n         /-----\u003e | ItemBinder |\n        /        +------------+\n       /               v\n      /            +--------+     +------------+\n     /      /----\u003e | Binder | --\u003e | ViewHolder |\n+-------+  /       +--------+     +------------+\n| Model | *------\u003e | Binder | --\u003e | ViewHolder |\n+-------+  \\       +--------+     +------------+\n            \\----\u003e | Binder | --\u003e | ViewHolder |\n                   +--------+     +------------+\n```\n\n* `ItemBinder\u003c? extends T, ? extends VH\u003e` takes a model `T` and maps it to a list of binders of type `Binder\u003cT, ? extends VH\u003e`.\n* `Binder\u003c? super T, ? extends VH\u003e` takes a model of type `T` and maps it to a `ViewHolder` of type `VH`\n\nA minor design point is that `RecyclerView.Adapter#onCreate()` creates the viewholders, so some sort of mechanism for creating viewholders is necessary. This is where **ViewHolderCreator** comes in - it is a model-independent way of creating viewholders (in other libraries with a one-to-one relationship between models and viewholders, this code would live in the model - e.g. [Epoxy](https://github.com/airbnb/epoxy#epoxy-models)).\n\n```\n                 +------------+\n         /-----\u003e | ItemBinder |\n        /        +------------+\n       /               v\n      /            +--------+     +------------+     +-------------------+\n     /      /----\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n+-------+  /       +--------+     +------------+     +-------------------+\n| Model | *------\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n+-------+  \\       +--------+     +------------+     +-------------------+\n            \\----\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n                   +--------+     +------------+     +-------------------+\n```\n\n### Dependency Injection with Dagger 2 Map Multibindings\n\nFor Graywater to know about the ItemBinders and ViewHolderCreators, each of them needs to be registered when the adapter is created. When there are a substantial number of both, there can be a significant impact on the time it takes to initialize the adapter.\n\nOne solution is to use [Dagger 2 map multibindings](https://google.github.io/dagger/multibindings#map-multibindings). This allows you to use the full power of dependency injection to control which binders a given screen will support, as well as the ability to inject different versions of the same binder on different screens to facilitate screen-dependent behavior.\n\n```\n                 +------------+                       +----------------+\n         /-----\u003e | ItemBinder | \u003c-------------------- | Dagger 2 Maps  |\n        /        +------------+                       +----------------+\n       /               v                                       v\n      /            +--------+     +------------+     +-------------------+\n     /      /----\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n+-------+  /       +--------+     +------------+     +-------------------+\n| Model | *------\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n+-------+  \\       +--------+     +------------+     +-------------------+\n            \\----\u003e | Binder | --\u003e | ViewHolder | \u003c-- | ViewHolderCreator |\n                   +--------+     +------------+     +-------------------+\n```\n\nBut using Dagger 2 by itself does not improve startup time, because the maps are created at injection time, which requires all the binders to also be created. This can be somewhat alleviated with `Lazy\u003cMap\u003e`, but another benefit of Dagger 2 is the automatic support for `Map\u003cK, Provider\u003cV\u003e\u003e`. When applied to `ItemBinders`, this allows each `ItemBinder` to be constructed on demand.\n\n_Note that Graywater does not have built-in support for Dagger 2._\n\n### Lazy Loading Binders\n\nNormally, when an item is added to the adapter, the corresponding `ItemBinder` is loaded as well as all the necessary `Binder` classes.\n\n```\n Binders             ItemBinders                Items             Screen  \n+--------+          +------------+          +-----------+       +--------+\n| Photo  | -------- |            |       /- | TextPost  |       | Header |\n+--------+    /---- | Photo Post | -\\   /   +-----------+       +--------+\n| Footer | --x /--- |            |   \\----- | PhotoPost |       |        |\n+--------+    x     +------------+    /     +-----------+       |        |\n| Header | --x \\--- |            | --/   /- | TextPost  |       | Text   |\n+--------+    \\---- | Text Post  |      /   +-----------+       |        |\n| Text   | -------- |            | ----/                        |        |\n+--------+          +------------+                              +--------+\n```\n\nBut on-screen, only the first item is visible, and out of the first item, only two components are visible. So in the above example, there is no need to load the \"Footer\" binder. This is what `List\u003cProvider\u003cBinder\u003e\u003e` facilitates.\n\n```\n Binders             ItemBinders                Items             Screen  \n+--------+          +------------+          +-----------+       +--------+\n| Photo  |          |            |      /-- | TextPost  | -x--- | Header |\n+--------+          | Photo Post |     /    +-----------+   \\   +--------+\n| Footer |          |            |    /     | PhotoPost |    \\- |        |\n+--------+          +------------+   /      +-----------+       |        |\n| Header | ---\\     |            | -/       | TextPost  |       | Text   |\n+--------+     \\--- | Text Post  |          +-----------+       |        |\n| Text   | -------- |            |                              |        |\n+--------+          +------------+                              +--------+\n```\n\nThis is very useful for improving initialization performance when loading long cached lists by deferring binder creation until the binder is nearly on screen.\n\n## How do you use it?\n\nGraywater relies heavily on generics for type safety - here are the major type parameters:\n\n  * `T` is the base model type.\n  * `VH` is the base viewholder type.\n  * `MT` is the type of the model type (e.g. `Class\u003c?\u003e`).\n\nAlthough this may seem overly generic, it is convenient if your base model or viewholder type has methods you need to access.\n\nAdd a model that subclasses `T`.\n\n```java\nclass Text {\n  String text;\n}\n```\n\nCreate the viewholder(s).\n\n```java\nclass TextViewHolder extends RecyclerView.ViewHolder {\n\n  TextView textView;\n\n  public TextViewHolder(View view) {\n    super(view);\n    textView = (TextView) view.findViewById(R.id.text);\n  }\n}\n```\n\nCreate the corresponding `ViewHolderCreator` implementations.\n\n```java\nclass TextViewHolderCreator implements GraywaterAdapter.ViewHolderCreator {\n\n  public TextViewHolder create(final ViewGroup parent) {\n    return new TextViewHolder(GraywaterAdapter.inflate(parent, R.layout.item_text));\n  }\n\n  public int getViewType() {\n    return R.layout.item_text;\n  }\n}\n```\n\nCreate the `Binder\u003cT, ? extends VH\u003e` implementations for each `ViewHolder`.\n\n```java\nclass TextBinder implements GraywaterAdapter.Binder\u003cText, TextViewHolder\u003e {\n\n  public Class\u003cTextViewHolder\u003e getViewHolderType() {\n    return TextViewHolder.class;\n  }\n\n  public void prepare(final Text model, \n                      final List\u003cGraywaterAdapter.Binder\u003c? super Text, ? extends TextViewHolder\u003e\u003e binders, \n                      final int binderIndex) {\n    \n  }\n\n  public void bind(final Text model, \n                   final TextViewHolder holder, \n                   final List\u003cGraywaterAdapter.Binder\u003c? super Text, ? extends TextViewHolder\u003e\u003e binders, \n                   final int binderIndex, \n                   final GraywaterAdapter.ActionListener\u003cText, TextViewHolder\u003e actionListener) {\n    holder.textView.setText(model.text);\n  }\n\n  public void unbind(final TextViewHolder holder) {\n    holder.textView.setText(null);\n  }\n}\n```\n\nCreate the `ItemBinder` that returns the list of binders for the model.\n\n```java\nclass TextItemBinder implements GraywaterAdapter.ItemBinder\u003cText, RecyclerView.ViewHolder\u003e {\n    \n  TextBinder textBinder;\n\n  public TextItemBinder(TextBinder textBinder) {\n    this.textBinder = textBinder;\n  }\n\n  public List\u003cGraywaterAdapter.Binder\u003c? super Text, ? extends RecyclerView.ViewHolder\u003e\u003e getBinderList(\n      final Text model, \n      final int position) {\n    return new ArrayList\u003cGraywaterAdapter.Binder\u003c? super Text, ? extends RecyclerView.ViewHolder\u003e\u003e() {{\n      add(textBinder);\n      add(textBinder);\n    }};\n  }\n}\n```\n\nLastly, subclass `GraywaterAdapter` and register the created classes!\n\n```java\nprivate static class TextAdapter extends GraywaterAdapter\u003cText, RecyclerView.ViewHolder, Class\u003c?\u003e\u003e {\n\n  public TextAdapter() {\n    register(new TextViewHolderCreator(), TextViewHolder.class);\n\n    final TextBinder textBinder = new TextBinder();\n\n    register(String.class, new TextItemBinder(textBinder), null);\n  }\n\n  @Override\n  protected Class\u003c?\u003e getModelType(final Text model) {\n    return model.getClass();\n  }\n}\n```\n\nYou can then add items using `GraywaterAdapter.add()` or remove them with `GraywaterAdapter.remove()`. Note that `getItemCount()` will return the number of viewholders, not the number of model objects in your list. `getModelType(MT)` will generally have the example implementation, but it may be useful to have a custom implementation if subtypes have different definitions across types, or if you need a \"default\" type.\n\nIn example code, an adapter is created that repeats each item in the list once.\n\n## How does it work?\n\nAt its core, Graywater maps models to viewholders, which basically means it is a just a dictionary. These are the fields used in a dictionary-like way:\n\n* `List\u003cT\u003e mItems` - the list of items (or a map of position to item)\n* `Map\u003cClass\u003c? extends VH\u003e, ViewHolderCreator\u003e mViewHolderCreatorMap` - the map of viewholder class to `ViewHolderCreator`.\n* `Map\u003cMT, ItemBinder\u003c? extends T, ? extends VH\u003e\u003e mItemBinderMap` - the map of `MT` (model type) to `ItemBinder`\n* `Map\u003cMT, ActionListener\u003c? extends T, ? extends VH\u003e\u003e mActionListenerMap` - the map of `MT` (model type) to `ActionListener`\n\nSo in `add()`, the new model is added to the list of items. In `register()`, the parameters are added to the respective map.\n\nA simple optimization is to cache the ItemBinders. This is done by `binderListCache`, which is of type `List\u003cList\u003cBinder\u003c? super T, ? extends VH\u003e\u003e\u003e`. Every time `add()` is called, `getBinderList()` is called and the return value is added to the cache.\n\nWhat is `MT`?\n\n```java\nprotected abstract MT getModelType(T model);\n```\n\nInstead of automatically using the class of the model as the model's type, it can be anything (preferably a similar property of the model).\n\nNote that `RecyclerView.Adapter` has these methods:\n\n```java\nabstract class Adapter\u003cVH extends ViewHolder\u003e {\n  abstract VH onCreateViewHolder(ViewGroup parent, int viewType);\n  abstract void onBindViewHolder(VH holder, int position);\n  int getItemViewType(int position);\n  abstract int getItemCount();\n}\n```\n\nIt is important to note that `position` in the above methods is the _viewholder_ position, not the _model_ position. This distinction is extremely important, because when we are given the _viewholder_ position when we need the _model_ position.\n\nFor now, we assume that `viewType` has a one-to-one correspondence to the viewholder class.\n\nHere is a visualization of the model and viewholder positions:\n\n```\n model      viewholder\n position   position\n\n +-----+    +-----+\n |     |    |  0  |\n |     |    +-----+\n |     |    |  1  |\n |  0  |    +-----+\n |     |    |  2  |\n |     |    +-----+\n |     |    |  3  |\n +-----+    +-----+\n\n +-----+    +-----+\n |     |    |  4  |\n |  1  |    +-----+\n |     |    |  5  |\n +-----+    +-----+\n```\n\nIf we are given a viewholder position of `5`, we need to arrive at the model position of `1`, that way we can grab the model from the backing data store.\n\nThe way to do this is to iterate through the models, going to the corresponding `ItemBinder` and accumulating the size of the list that is returned. Unfortunately, this is slow.\n\nBut in order to make it fast, we need to cache a lot of intermediary state.\nOn `add()`, we compute these two caches, `viewHolderToItemPosition` and `itemPositionToFirstViewHolderPosition`. Note that the code uses _item_ to refer to the _model_ position.\n\n```\n model      viewholder    viewHolderToItemPos   itemPosToFirstViewHolderPos\n position   position\n\n +-----+    +-----+\n |     |    |  0  |           { 0, 0 }\n |     |    +-----+\n |     |    |  1  |           { 1, 0 }\n |  0  |    +-----+                                    { 0, 0 }\n |     |    |  2  |           { 2, 0 }\n |     |    +-----+\n |     |    |  3  |           { 3, 0 }\n +-----+    +-----+\n\n +-----+    +-----+\n |     |    |  4  |           { 4, 1 }\n |  1  |    +-----+                                    { 1, 4 }\n |     |    |  5  |           { 5, 1 }\n +-----+    +-----+\n```\n\n`viewHolderToItemPositionCache` is also used for `getItemCount()`.\n\n`itemPositionToFirstViewHolderPosition` is primarily used for one purpose: to determine the position of the viewholder and associated binder in the list of viewholders _for a given model_. In the above example, the viewholder at position `5` is the 2nd viewholder for the 2nd model. This is important when there is more than one instance of a viewholder for a model, such as reblog comments.\n\n`getItemViewType()` works by tracking the registered `ViewHolderCreators`, which have this interface:\n\n```java\ninterface ViewHolderCreator {\n  RecyclerView.ViewHolder create(ViewGroup parent);\n  int getViewType();\n}\n```\n\nWhen a new `ViewHolderCreator` is registered, it is added to `viewHolderCreatorList`, which is of type `SparseArray\u003cClass\u003c? extends VH\u003e\u003e`, and associates the `viewType` with the correct class. The class is then associated with the `ViewHolderCreator` via `viewHolderCreatorMap`, which is of type `Map\u003cClass\u003c? extends VH\u003e, ViewHolderCreator\u003e`.\n\n```\n     viewHolderCreatorList                     viewHolderCreatorMap\n+------------------------------+     +---------------------------------------+\n| viewtype -\u003e ViewHolder.class |     | ViewHolder.class -\u003e ViewHolderCreator |\n+==============================+     +=======================================+\n|  8324    -\u003e Header.class     |     |  Header.class    -\u003e HeaderCreator     |\n+------------------------------+     +---------------------------------------+\n|  9802    -\u003e Body.class       |     |  Body.class      -\u003e BodyCreator       |\n+------------------------------+     +---------------------------------------+\n|  2383    -\u003e Footer.class     |     |  Footer.class    -\u003e FooterCreator     |\n+------------------------------+     +---------------------------------------+\n```\n\nTo implement `getItemViewType()` So when given a _viewholder_ position, \n\n1. `viewHolderToItemPos` is used to retrieve the model position.\n2. `binderListCache` and the model position is used to get the list of binders.\n3. `itemPosToFirstViewHolderPos` is used to retrieve the position of the first viewholder.\n4. The binder position in the list of binders is computed.\n5. The correct binder for the viewholder position is retrieved.\n6. `viewHolderCreatorMap` is passed the `Binder.getViewHolderType()` to get the `ViewHolderCreator`.\n7. `ViewHolderCreator.getViewType()` is called to get the `viewType`.\n\n`onCreateViewHolder(ViewGroup parent, int viewType)` is quite a bit simpler:\n\n```java\nreturn (VH) viewHolderCreatorMap.get(getViewHolderClass(viewType)).create(parent);\n```\n\n1. `viewHolderCreatorList` is used to get the class from the `viewType`\n2. `viewHolderCreatorMap` is used to get the `ViewHolderCreator`\n3. The `ViewHolderCreator` creates the new viewholder.\n\n`onBindViewHolder(VH holder, int viewHolderPosition)` is implemented by following the first 5 steps of `getItemViewType()`, and then calling `Binder.bind()` with the model and the viewholder.\n\n## Other features\n\nIn `bind()`, the adapter looks ahead to the next `numViewHoldersToPrepare()` viewholders (default 3) and calls `Binder.prepare()` on them. Note that it does not call `prepare()` more than once, unless `unbind()` is called. This state is stored in `viewHolderPreparedCache` which stores the indices of viewholders that have been prepared.\n\nThis also works in both directions of the `RecyclerView`. It checks the order of `bind()` operations to determine which direction to prepare viewholders in.\n\n`ActionListener` is a bit of an experimental feature to avoid creating extra `ClickListener` objects on every `bind()` call.\n\n## An addendum on binders and generics\n\n`ItemBinder#getBinderList()` has a somewhat complex return type:\n\n```java\nList\u003cBinder\u003c? super T, ? extends VH\u003e\u003e getBinderList(@NonNull T model, int position)\n```\n\nIn particular, `Binder\u003c? super T, ? extends VH\u003e` is quite confusing.\n\nWhen you write your binder, try to parameterize the binder with the least-restrictive model it can take and the most restrictive viewholder it can bind to.\n\nIf these were your type hierarchies:\n\n```\n Model Type              ViewHolder Type\n Hierarchy               Hierarchy \n\n     A                        1    \n   /   \\    \u003c- Binder -\u003e    /   \\  \n  B     C                  2     3 \n```\n\n* A binder should be written to take `A` if possible, `B` or `C` if necessary.\n* A binder can only be written to take `2` or `3` (note that `1` should never be registered, because then it is ambiguous).\n\nThis can be illustrated with an example\n\n```\n      Post                     BaseViewHolder\n     /    \\      \u003c- Binder -\u003e      /    \\    \n  Text    Photo                Header   Body \n```\n\nIf every post has a header, it makes sense to have\n\n* `HeaderBinder extends Binder\u003cPost, Header\u003e`\n* `PhotoBinder extends Binder\u003cPhoto, Body\u003e`\n* `TextBinder extends Binder\u003cText, Body\u003e`\n\nThat way `HeaderBinder` can take a `Text` or a `Photo`, while `PhotoBinder` won't ever take a `Text`.\n\nSo what does the `ItemBinder\u003cT, VH\u003e` look like?\n\n* `TextItemBinder extends ItemBinder\u003cText, BaseViewHolder\u003e`\n  - `HeaderBinder`\n  - `TextBinder`\n* `PhotoItemBinder extends ItemBinder\u003cPhoto, BaseViewHolder\u003e`\n  - `HeaderBinder`\n  - `PhotoBinder`\n\nYou can see that `HeaderBinder` binds `Post`, which is a superclass of `Text`, while `TextBinder` binds to `Body`, which is a subclass of `BaseViewHolder`. \n\nWhen registering:\n\n* `register(Text.class, textItemBinder)`\n* `register(Photo.class, photoItemBInder)`\n\nassuming the class is the model type.\n\nThe adapter should be of type `GraywaterAdapter\u003cPost, BaseViewHolder\u003e`, the superclasses for all the types used.\n\n## Contact\n\n* [Eric Leong](mailto:ericleong@tumblr.com)\n\n## License\n\nCopyright 2017 Tumblr, Inc.\n\nLicensed under the Apache License, Version 2.0 (the “License”); you may not use\nthis file except in compliance with the License. You may obtain a copy of the\nLicense at [apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).\n\n\u003e Unless required by applicable law or agreed to in writing, software\n\u003e distributed under the License is distributed on an “AS IS” BASIS, WITHOUT\n\u003e WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n\u003e License for the specific language governing permissions and limitations under\n\u003e the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftumblr%2FGraywater","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftumblr%2FGraywater","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftumblr%2FGraywater/lists"}