{"id":15030943,"url":"https://github.com/sockeqwe/adapterdelegates","last_synced_at":"2025-05-14T10:08:38.765Z","repository":{"id":35615964,"uuid":"39889599","full_name":"sockeqwe/AdapterDelegates","owner":"sockeqwe","description":"\"Favor composition over inheritance\" for RecyclerView Adapters","archived":false,"fork":false,"pushed_at":"2024-04-18T09:55:42.000Z","size":1001,"stargazers_count":2938,"open_issues_count":17,"forks_count":315,"subscribers_count":74,"default_branch":"master","last_synced_at":"2025-04-04T11:38:53.338Z","etag":null,"topics":["adapterdelegates","composition-over-inheritance","delegate","recyclerview","recyclerview-adapter"],"latest_commit_sha":null,"homepage":"http://hannesdorfmann.com/android/adapter-delegates","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/sockeqwe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-07-29T11:19:56.000Z","updated_at":"2025-04-03T07:05:24.000Z","dependencies_parsed_at":"2024-06-20T21:49:47.203Z","dependency_job_id":"f8cccbb6-e2f9-49ac-9678-279ba26ab102","html_url":"https://github.com/sockeqwe/AdapterDelegates","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sockeqwe%2FAdapterDelegates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sockeqwe%2FAdapterDelegates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sockeqwe%2FAdapterDelegates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sockeqwe%2FAdapterDelegates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sockeqwe","download_url":"https://codeload.github.com/sockeqwe/AdapterDelegates/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248448314,"owners_count":21105277,"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":["adapterdelegates","composition-over-inheritance","delegate","recyclerview","recyclerview-adapter"],"created_at":"2024-09-24T20:14:35.537Z","updated_at":"2025-04-11T17:23:57.658Z","avatar_url":"https://github.com/sockeqwe.png","language":"Java","readme":"# AdapterDelegates\nRead the motivation for this project in [my blog post](http://hannesdorfmann.com/android/adapter-delegates).\n\nThe idea of this library is to build your adapters by composing reusable components.\n\n    Favor composition over inheritance\n\nThe idea is that you define an AdapterDelegate for each view type. \nThis delegate is responsible for creating ViewHolder and binding ViewHolder for a certain viewtype. \nThen you can compose your RecyclerView Adapter by registering the AdapterDelegates that you really need.\n\n### Changelog\nSee [releases section](https://github.com/sockeqwe/AdapterDelegates/releases)\n\n\n# Quickstart: Kotlin DSL\nThere are 2 artifacts for kotlin users that allow you to write Adapter Delegates more convenient by providing a `DSL`:\n\n## Dependencies\n\n```\nimplementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'\n\n// If you use Kotlin Android Extensions and synthetic properties (alternative to findViewById())\nimplementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-layoutcontainer:4.3.2'\n\n// If you use ViewBinding\nimplementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'\n```\n\n## How to use it\n\n```kotlin\nfun catAdapterDelegate(itemClickedListener : (Cat) -\u003e Unit) = adapterDelegate\u003cCat, Animal\u003e(R.layout.item_cat) {\n\n    // This is the initializer block where you initialize the ViewHolder.\n    // Its called one time only in onCreateViewHolder.\n    // this is where you can call findViewById() and setup click listeners etc.\n\n    val name : TextView = findViewById(R.id.name)\n    name.setClickListener { itemClickedListener(item) } // Item is automatically set for you. It's set lazily though (set in onBindViewHolder()). So only use it for deferred calls like clickListeners.\n\n    bind { diffPayloads -\u003e // diffPayloads is a List\u003cAny\u003e containing the Payload from your DiffUtils\n        // This is called anytime onBindViewHolder() is called\n        name.text = item.name // Item is of type Cat and is the current bound item.\n    }\n}\n```\n\nIn case you want to use kotlin android extensions and synthetic properties (as alternative to findViewById()) use `adapterDelegateLayoutContainer` instead of `adapterDelegate` like this:\n\n```kotlin\nfun catAdapterDelegate(itemClickedListener : (Cat) -\u003e Unit) = adapterDelegateLayoutContainer\u003cCat, Animal\u003e(R.layout.item_cat) {\n\n    name.setClickListener { itemClickedListener(item) } // no need for findViewById(). Name is imported as synthetic property from kotlinx.android.synthetic.main.item_cat\n\n    bind { diffPayloads -\u003e\n        name.text = item.name\n    }\n}\n```\n\nIf you use `adapterDelegateLayoutContainer()` don't forget to add\n```\nandroidExtensions {\n    experimental = true\n}\n```\n\nto your build.gradle to enable LayoutContainer.\n\nIn case you want to use ViewBinding\\DataBinding use `adapterDelegateViewBinding` instead of `adapterDelegate` like this:\n\n```kotlin\nfun cat2AdapterDelegate(itemClickedListener : (Cat) -\u003e Unit) = adapterDelegateViewBinding\u003cCat, DisplayableItem, ItemCatBinding\u003e(\n    { layoutInflater, root -\u003e ItemCatBinding.inflate(layoutInflater, root, false) }\n) {\n    binding.name.setOnClickListener {\n        itemClickedListener(item)\n    }\n    bind {\n        binding.name.text = item.name\n    }\n}\n```\n\nYou have to specify if a specific AdapterDelegate is responsible for a specific item.\nPer default this is done with an `instanceof` check like  `item instanceof Cat`.\nYou can override this if you want to handle it in a custom way by setting the `on` lambda\nand return true or false:\n\n```kotlin\nadapterDelegate\u003cCat, Animal\u003e (\n    layout = R.layout.item_cat,\n    on = { item: Animal, items: List, position: Int -\u003e\n        if (item is Cat \u0026\u0026 position == 0)\n            true // return true: this adapterDelegate handles it\n        else\n            false // return false\n    }\n){\n    ...\n    bind { ... }\n}\n```\n\nThe same `on` parameter is available for `adapterDelegateLayoutContainer()` and `adapterDelegateViewBinding()` DSL.\n\n### Compose your Adapter\nFinally, you can compose your `RecyclerView Adapter` by registering your AdapterDelegates like this:\n\n```kotlin\nval adapter = ListDelegationAdapter\u003cList\u003cAnimal\u003e\u003e(\n    catAdapterDelegate(...),\n    dogAdapterDelegate(),\n    snakeAdapterDelegate()\n)\n```\n\n### `fun` vs. `val`\nYou could define your AdapterDelegate also as TopLevel `val` like this:\n\n```kotlin\n// top level property inside CatDelegate.kt\nval catDelegate = adapterDelegate\u003cCat, Animal\u003e {\n    ...\n    bind { ... }\n}\n```\n\nbut a top level val is a static field at the end so that this adapter delegate will be kept for the\nlifetime of your application in memory.\nTherefore, we would recommend to prefer write your AdapterDelegate as a function and call this function to\nactually instantiate the AdapterDelegate.\nThen the AdapterDelegate can be garbage collected as soon as the user leaves the screen the\nAdapterDelegate is used in.\n\n\n```kotlin\n// top level function inside CatDelegate.kt\nfun catAdapterDelegate() = adapterDelegate\u003cCat, Animal\u003e {\n   ...\n   bind { ... }\n}\n```\n\n\n## Dependencies if you don't use Kotlin DSL\nThis library is available on maven central:\n\n```groovy\nimplementation 'com.hannesdorfmann:adapterdelegates4:4.3.2'\n```\n[![Build Status](https://travis-ci.org/sockeqwe/AdapterDelegates.svg?branch=master)](https://travis-ci.org/sockeqwe/AdapterDelegates)\n\nPlease note that since 4.0 the group id has been changed to `adapterdelegates4`.\n\n### Snapshot\n\n```groovy\nimplementation 'com.hannesdorfmann:adapterdelegates4:4.3.3-SNAPSHOT'\n```\n\nYou also have to add the url to the snapshot repository:\n\n```groovy\nallprojects {\n  repositories {\n    ...\n\n    maven { url \"https://oss.sonatype.org/content/repositories/snapshots/\" }\n}\n```\n\n\n## How to use it in Java\n\nThe idea of this library is to build your adapters by composing reusable components.\n\n\u003e Favor composition over inheritance\n\nThe idea is that you define an `AdapterDelegate` for each view type. This delegate is responsible for creating ViewHolder and binding ViewHolder for a certain viewtype.\nAn `AdapterDelegate` get added to an `AdapterDelegatesManager`. This manager is the man in the middle between `RecyclerView.Adapter` and each `AdapterDelegate`.\n\nFor example:\n```java\npublic class CatAdapterDelegate extends AdapterDelegate\u003cList\u003cAnimal\u003e\u003e {\n\n  private LayoutInflater inflater;\n\n  public CatAdapterDelegate(Activity activity) {\n    inflater = activity.getLayoutInflater();\n  }\n\n  @Override public boolean isForViewType(@NonNull List\u003cAnimal\u003e items, int position) {\n    return items.get(position) instanceof Cat;\n  }\n\n  @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {\n    return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));\n  }\n\n  @Override public void onBindViewHolder(@NonNull List\u003cAnimal\u003e items, int position,\n      @NonNull RecyclerView.ViewHolder holder, @Nullable List\u003cObject\u003e payloads) {\n\n    CatViewHolder vh = (CatViewHolder) holder;\n    Cat cat = (Cat) items.get(position);\n\n    vh.name.setText(cat.getName());\n  }\n\n  static class CatViewHolder extends RecyclerView.ViewHolder {\n\n    public TextView name;\n\n    public CatViewHolder(View itemView) {\n      super(itemView);\n      name = (TextView) itemView.findViewById(R.id.name);\n    }\n  }\n}\n```\n\nPlease note that `onBindViewHolder()` last parameter `payloads` is null unless you use from `adapter.notify`.  There are more methods like `onViewRecycled(ViewHolder)`, `onFailedToRecycleView(ViewHolder)`,\n`onViewAttachedToWindow(ViewHolder)` and `onViewDetachedFromWindow(ViewHolder)` you can override in your `AdapterDelegate` class.\n\n\nThen an `AnimalAdapter` could look like this:\n\n```java\npublic class AnimalAdapter extends RecyclerView.Adapter {\n\n  private AdapterDelegatesManager\u003cList\u003cAnimal\u003e\u003e delegatesManager;\n  private List\u003cAnimal\u003e items;\n\n  public AnimalAdapter(Activity activity, List\u003cAnimal\u003e items) {\n    this.items = items;\n\n    delegatesManager = new AdapterDelegatesManager\u003c\u003e();\n\n    // AdapterDelegatesManager internally assigns view types integers\n    delegatesManager.addDelegate(new CatAdapterDelegate(activity))\n                    .addDelegate(new DogAdapterDelegate(activity))\n                    .addDelegate(new GeckoAdapterDelegate(activity));\n\n    // You can explicitly assign integer view type if you want to\n    delegatesManager.addDelegate(23, new SnakeAdapterDelegate(activity));\n  }\n\n  @Override public int getItemViewType(int position) {\n    return delegatesManager.getItemViewType(items, position);\n  }\n\n  @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n    return delegatesManager.onCreateViewHolder(parent, viewType);\n  }\n\n  @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n    delegatesManager.onBindViewHolder(items, position, holder);\n  }\n\n  @Override public int getItemCount() {\n    return items.size();\n  }\n}\n```\n\n## Reducing boilerplate code\nAs you have seen in the code snippet above this may require to write the same boiler plate code again and again to hook in `AdapterDelegatesManager` to `Adapter`.\nThis can be reduced by extending either from `ListDelegationAdapter` if the data source the adapter displays is `java.util.List\u003c?\u003e` or `AbsDelegationAdapter` which is a more general one (not limited to `java.util.List`)\n\n#### ListDelegationAdapter\nFor example the same `AnimalAdapter` from above could be simplified as follows by extending from `ListDelegationAdapter`:\n\n```java\npublic class AnimalAdapter extends ListDelegationAdapter\u003cList\u003cAnimal\u003e\u003e {\n\n  public AnimalAdapter(Activity activity, List\u003cAnimal\u003e items) {\n\n    // DelegatesManager is a protected Field in ListDelegationAdapter\n    delegatesManager.addDelegate(new CatAdapterDelegate(activity))\n                    .addDelegate(new DogAdapterDelegate(activity))\n                    .addDelegate(new GeckoAdapterDelegate(activity))\n                    .addDelegate(23, new SnakeAdapterDelegate(activity));\n\n    // Set the items from super class.\n    setItems(items);\n  }\n}\n```\n#### AbsListItemAdapterDelegate\nAlso you may have noticed that you often have to write boilerplate code to cast items and ViewHolders when working with list of items as adapters dataset source.\n`AbsListItemAdapterDelegate` can help you here. Let's take this class to create a `CatListItemAdapterDelegate` similar to the `CatAdapterDelegate` from top of this page but without the code for casting items.\n\n```java\npublic class CatListItemAdapterDelegate extends AbsListItemAdapterDelegate\u003cCat, Animal, CatViewHolder\u003e {\n\n  private LayoutInflater inflater;\n\n  public CatAdapterDelegate(Activity activity) {\n    inflater = activity.getLayoutInflater();\n  }\n\n  @Override public boolean isForViewType(Animal item, List\u003cAnimal\u003e items, int position) {\n    return item instanceof Cat;\n  }\n\n  @Override public CatViewHolder onCreateViewHolder(ViewGroup parent) {\n    return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));\n  }\n\n  @Override public void onBindViewHolder(Cat item, CatViewHolder vh, @Nullable List\u003cObject\u003e payloads) {\n    vh.name.setText(item.getName());\n  }\n\n  static class CatViewHolder extends RecyclerView.ViewHolder {\n\n    public TextView name;\n\n    public CatViewHolder(View itemView) {\n      super(itemView);\n      name = (TextView) itemView.findViewById(R.id.name);\n    }\n  }\n}\n```\n\nAs you see, instead of writing code that casts list item to Cat we can use `AbsListItemAdapterDelegate` to do the same job (by declaring generic types).\n\n## DiffUtil \u0026 ListAdapter = AsyncListDifferDelegationAdapter\nSupport library 27.0.1 introduced `ListAdapter` - the new extension of `RecyclerView.Adapter` that uses `AsyncListDiffer` internally. It does calculating diff in the background thread by default and does all particular animations for you items collection. Hence you don't need carry about `notify*` methods, `AsyncListDiffer` does all the job for you. And AdapterDelegates supports it too. \n\nThis library offers the equivalent to `ListAdapter` which is called `AsyncListDifferDelegationAdapter` that can be used together with any regular `AdapterDelegate`.\n\n```java\npublic class DiffAdapter extends AsyncListDifferDelegationAdapter\u003cAnimal\u003e {\n    public DiffAdapter() {\n        super(DIFF_CALLBACK) // Your diff callback for diff utils\n        delegatesManager\n            .addDelegate(new DogAdapterDelegate());\n            .addDelegate(new CatAdapterDelegate());\n    }\n}\n```\n\n\n## Pagination\nThere is an additional artifact for the pagination library:\n\n```gradle\nimplementation 'com.hannesdorfmann:adapterdelegates4-pagination:4.3.2'\n```\n\nUse `PagedListDelegationAdapter`.\n\n## Fallback AdapterDelegate\nWhat if your adapter's data source contains a certain element you don't have registered an `AdapterDelegate` for? In this case the `AdapterDelegateManager` will throw an exception at runtime. However, this is not always what you want. You can specify a fallback `AdapterDelegate` that will be used if no other `AdapterDelegate` has been found to handle a certain view type.\n\n```java\nAdapterDelegate fallbackDelegate = ...;\nadapterDelegateManager.setFallbackDelegate( fallbackDelegate );\n```\nNote also that boolean return type of `isForViewType()` of a fallback delegate will be ignored (will not be take into account). So it doesn't matter if you return true or false. You can use `AbsFallbackAdapterDelegate` that already implements `isForViewType()` so that you only have to override `onCreateViewHolder()` and `onBindViewHolder()` for your fallback adapter delegate.\n\n## Version 3.x to 4.0 migration\n`AdapterDelegates3` uses `com.android.support:recyclerview-v7:x.y.z` whereas `AdapterDelegates4` uses\n`androidx.recyclerview:recyclerview:1.0.0`.\nMigration should be easy. Just use IntelliJ IDE or Android Studio 'Replace in Path' (can be found inside `Edit` main menu then `Find` submenu):\nReplace `com.hannesdorfmann.adapterdelegates3` with `com.hannesdorfmann.adapterdelegates4`.\nYou might also have to replace `android.support.v7.widget.RecyclerView` with `androidx.recyclerview.widget.RecyclerView` and\n`android.support.annotation.NonNull` with `androidx.annotation.NonNull`.\n\n## License\n\n```\nCopyright 2015 Hannes Dorfmann\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsockeqwe%2Fadapterdelegates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsockeqwe%2Fadapterdelegates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsockeqwe%2Fadapterdelegates/lists"}