{"id":22413553,"url":"https://github.com/nextfaze/power-adapters","last_synced_at":"2025-07-31T23:31:32.365Z","repository":{"id":57726815,"uuid":"50154902","full_name":"NextFaze/power-adapters","owner":"NextFaze","description":"Composable adapters for Android RecyclerViews and ListViews","archived":false,"fork":false,"pushed_at":"2020-05-05T05:41:00.000Z","size":7183,"stargazers_count":108,"open_issues_count":19,"forks_count":13,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-04-20T13:41:00.391Z","etag":null,"topics":["adapter-composition","android","asynchronously","fluent-chaining-methods","multiple-adaptors","nested-adapters","recyclerview-adapter"],"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/NextFaze.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-01-22T03:27:12.000Z","updated_at":"2024-04-20T13:41:00.392Z","dependencies_parsed_at":"2022-09-26T21:51:49.665Z","dependency_job_id":null,"html_url":"https://github.com/NextFaze/power-adapters","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NextFaze%2Fpower-adapters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NextFaze%2Fpower-adapters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NextFaze%2Fpower-adapters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NextFaze%2Fpower-adapters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NextFaze","download_url":"https://codeload.github.com/NextFaze/power-adapters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228312126,"owners_count":17900219,"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":["adapter-composition","android","asynchronously","fluent-chaining-methods","multiple-adaptors","nested-adapters","recyclerview-adapter"],"created_at":"2024-12-05T14:13:25.848Z","updated_at":"2024-12-05T14:13:28.268Z","avatar_url":"https://github.com/NextFaze.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg align=\"right\" src=\"../gh-pages/showcase.gif\"\u003e\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n**Table of Contents**\n\n- [Power Adapters](#power-adapters)\n- [Feature Summary](#feature-summary)\n- [Usage](#usage)\n  - [Basic](#basic)\n  - [RxJava](#rxjava)\n  - [Kotlin](#kotlin)\n  - [Adapter Composition](#adapter-composition)\n  - [Headers and Footers](#headers-and-footers)\n  - [Data Type Binding](#data-type-binding)\n    - [Binder](#binder)\n    - [Mapper](#mapper)\n  - [Conversion](#conversion)\n  - [Nested Adapters](#nested-adapters)\n  - [Asynchronous Data Loading](#asynchronous-data-loading)\n    - [Basic Data Usage](#basic-data-usage)\n    - [Invalidating and Reloading](#invalidating-and-reloading)\n    - [DataLayout](#datalayout)\n    - [RxJava Module](#rxjava-module)\n    - [Data Views](#data-views)\n  - [Samples](#samples)\n- [Build](#build)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n# Power Adapters\n\nPresenting large data sets efficiently can be a challenging part of Android development. It gets more complicated as we\nbegin to handle edge cases and add additional decorations like headers. We also often find ourselves repeating\nundesirable boilerplate as we write adapters for each data source. In addition, Android doesn't provide a clean\nobject-oriented, reusable way of presenting collections of multiple types.\n\n# Feature Summary\n\nThis library provides the following features:\n\n* Present **multiple data types** within an adapter in a **type-safe** manner\n* **Concatenate** multiple adapters together\n* Show **headers** and **footers**\n* Show a **loading indicator** to indicate a loading state\n* Show an **empty item** to indicate an empty underlying data set\n* Add **dividers** in between items of an existing adapter\n* Show an adapter or item range only when a **condition** evaluates to `true`\n* Present **nested** adapters, a powerful substitute for `ExpandableListView` without any limitation of nesting level\n* Load from remote or slow data sources **asynchronously**\n* Backed up by **unit tests**, verifying the correct notifications are issued and state maintained\n* Minimal **dependencies**; doesn't include any unnecessary transitive dependencies\n* All adapters issue the correct insertion/removal/change notifications needed for full `RecyclerView` animation support\n* Kotlin extension modules, which add idiomatic Kotlin APIs\n* RxJava extension modules, adding easy integration with `Observable`s, etc\n\nPower adapters are compatible with the following collection view classes:\n* `androidx.recyclerview.widget.RecyclerView`\n* `android.widget.ListView`\n* `android.widget.GridView`\n* `android.support.v4.view.ViewPager`\n* Any other view that accepts a `android.widget.Adapter`\n\n# Usage\n\nGet it from Maven Central, using Gradle:\n\n```groovy\nimplementation 'com.nextfaze.poweradapters:power-adapters:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-recyclerview-v7:0.26.0'\n```\n\n## Basic\n\n```java\n// Declare a binder for your item type\nclass TweetHolder extends ViewHolder {\n\n    TextView textView;\n\n    TweetHolder(View view) {\n        super(view);\n        textView = (TextView) view.findViewById(R.id.text);\n    }\n}\n\nBinder\u003cTweet, View\u003e tweetBinder = \n        ViewHolderBinder.create(R.layout.tweet, TweetHolder::new, (container, tweet, tweetHolder, holder) -\u003e {\n    tweetHolder.textView.setText(tweet.getText());\n});\n\n// Construct your \"core\" adapter\nListBindingAdapter\u003cTweet\u003e tweetsAdapter = new ListBindingAdapter\u003c\u003e(tweetBinder);\n\n// Assign to your RecyclerView\nrecyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(tweetsAdapter));\n```\n\n## RxJava\n\nRxJava modules are available. Simply append `-rxjava2` to get the RxJava module:\n\n```groovy\nimplementation 'com.nextfaze.poweradapters:power-adapters-rxjava2:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-data-rxjava2:0.26.0'\n```\n\n## Kotlin\n\nKotlin modules are also provided for most modules. Append `-kotlin` to get the Kotlin module:\n\n```groovy\nimplementation 'com.nextfaze.poweradapters:power-adapters-kotlin:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-data-kotlin:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-rxjava2-kotlin:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-data-rxjava2-kotlin:0.26.0'\nimplementation 'com.nextfaze.poweradapters:power-adapters-recyclerview-v7-kotlin:0.26.0'\n```\n\nSome of the Kotlin APIs include:\n\n- Top-level factory functions:\n    ```kotlin\n    val data = data { api.getPosts() }\n    ```\n    ```kotlin\n    val data = cursorData({ db.getUsers() }, ::User)\n    ```\n    ```kotlin\n    val header = viewFactory\u003cTextView\u003e(R.layout.header) {\n        text = \"News\"\n    }\n    ```\n- Extension functions:\n    ```kotlin\n    recyclerView.adapter = myPowerAdapter.toRecyclerAdapter()\n    ```\n- `PowerAdapter` and `Data` Factory methods: `adapterOf()`, `dataOf()`\n- `Binder` factory methods:\n    ```kotlin\n    val binder = binder\u003cItem, ItemView\u003e(R.layout.item) { container, item, holder -\u003e\n        title = item.name\n        imageUri = item.imageUri\n    }\n    ```\n- Operator overloads:\n    ```kotlin\n    adapter.showOnlyWhile(empty and !anotherThing)\n    ```\n    ```\n    val adapter = itemsAdapter + anotherAdapter\n    ```\n    ```kotlin\n    data += dataObserver { updateViews() }\n    ```\n- Property delegates:\n    ```kotlin\n    val condition = ValueCondition()\n    var enabled by condition\n    val adapter = myAdapter.showOnlyWhile(condition)\n    // Reassign property to control visibility of adapter\n    enabled = false\n    ```\n- Type-safe builder: \n    ```kotlin\n    adapter {\n        layoutResource(R.layout.header)\n        +myItemsAdapter\n        layoutResource(R.layout.footer)\n    }\n    ```\n\n## Adapter Composition\n\nPower Adapters can be composed by using the fluent chaining methods.\nFor example, say you want to present a list of tweets, with a loading indicator, but show an empty message when there\nare no tweets, you can write the following:\n\n```java\nPowerAdapter adapter = tweetsAdapter\n    .limit(10) // Only show up to 10 tweets\n    .append(\n        // Show empty item while no tweets have loaded\n        asAdapter(R.layout.tweets_empty_item).showOnlyWhile(noTweets()),\n        // Show loading indicator while loading\n        asAdapter(R.layout.loading_indicator).showOnlyWhile(tweetsAreLoading())\n    )\nrecyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));\n```\n\nThis lets you write a simple `TweetAdapter` class, the only responsibility of which is to present tweets. By using\n`PowerAdapter.append` as such, the `TweetAdapter` need not be modified, and can be potentially reused elsewhere more\neasily. The use of `showOnlyWhile` applies a condition to the empty footer item, so it remains hidden unless the\nunderlying list of tweets is empty.\n\n## Headers and Footers\n\nHeaders and footers can be added using `prepend` and `append`:\n\n```java\n// Prepend a header view.\nPowerAdapter adapter = tweetAdapter.prepend(R.layout.header);\n```\n\n```java\n// Append a footer view.\nPowerAdapter adapter = tweetAdapter.append(R.layout.footer);\n```\n\n## Data Type Binding\n\nIncluded in Power Adapters is the ability to bind elements in your data set to views in a reusable, readable, and\ntype-safe manner.\n\n### Binder\n\nThe primary class needed to achieve this is a `Binder`. The responsibilities of a `Binder` include:\n\n* Construct a `View` to be bound, and re-used by the adapter/recycler view\n* Bind an object and/or data set index to the `View`\n\nMultiple types of commonly required binders are supplied. If you prefer the widely used view holder pattern, use\na `ViewHolderBinder`:\n\n```java\nBinder\u003cBlogPost, View\u003e blogPostBinder = \n        ViewHolderBinder.create(R.layout.post, BlogPostHolder::new, (container, blogPost, blogPostHolder, holder) -\u003e {\n    blogPostHolder.labelView.setText(\"Blog: \" + blogPost.getTitle());\n});\n\nclass BlogPostHolder extends ViewHolder {\n\n    TextView labelView;\n\n    BlogPostHolder(View view) {\n        super(view);\n        labelView = (TextView) view.findViewById(android.R.id.text1);\n    }\n}\n```\n\nIf you use custom views for each of your data models, use `Binder.create`. It takes a layout resource or a `ViewFactory`.\nThe view returned by the `ViewFactory` is passed to subsequent `bindView` calls, saving you from writing a separate `ViewHolder`.\n\nFor example:\n\n```java\nBinder\u003cTweet, TweetView\u003e tweetBinder = Binder.create(R.layout.tweet_item, ((container, sample, v, holder) -\u003e {\n    v.setTweet(tweet);\n    v.setOnClickListener(v -\u003e onTweetClick(tweet));\n}))\n```\n\n\n### Mapper\n\nThe examples above have all dealt with a single item type, and so there has only been a single `Binder`. When you want your list to contain multiple items, a `Mapper` is consulted to determine which `Binder` to use for presenting each particular item. Typically you'll use `MapperBuilder` to declaratively assign your model classes to\nbinders:\n\n```java\nMapper mapper = new MapperBuilder()\n    .bind(Tweet.class, new TweetBinder())\n    .bind(Ad.class, new AdBinder())\n    .bind(Video.class, new VideoBinder())\n    .build();\nListBindingAdapter\u003cObject\u003e adapter = new ListBindingAdapter\u003c\u003e(mapper);\nadapter.add(new Tweet());\nadapter.add(new Ad());\nadapter.add(new Video());\n```\n\n## Conversion\n\nPowerAdapter is designed to be used with different collection view implementations, so a final step is converting it to implement the expected adapter interface. This would usually be done as soon as the collection view is created, say in `onViewCreated`:\n\n```java\nrecyclerView.setAdapter(toRecyclerAdapter(powerAdapter));\n```\n\nThe following conversion methods are provided:\n\n|Collection View    |Converter                                  |Extension Module                                           |\n|:------------------|------------------------------------------:|:---------------------------------------------------------:|\n|`ListView`         |            `PowerAdapters.toListAdapter()`|None                                                       |\n|`RecyclerView`     |`RecyclerPowerAdapters.toRecyclerAdapter()`|`power-adapters-recyclerview-v7`                           |\n|`ViewPager`        |`SupportPowerAdapters.toPagerAdapter()`    |`power-adapters-support-v4`                                |\n\n## Nested Adapters\n\nThe `TreeAdapter` class allows you to present hierarchical data structures with no intrinsic depth limit. Each layer is\ncomprised of just another adapter - your children can themselves can be `TreeAdapter`s!\n\n```java\nPowerAdapter rootAdapter = new FileAdapter(new File(\"/\"));\nTreeAdapter treeAdapter = new TreeAdapter(rootAdapter, position -\u003e {\n    // Create a child adapter for this position in the root data set.\n    // Can be another TreeAdapter!\n   return createChildAdapter(position);\n});\ntreeAdapter.setExpanded(15, true);\n```\n\n## Asynchronous Data Loading\n\nImplementing a UI for presenting the contents of a remote collection, like a list of comments or products, requires\nseveral different mechanics. Among them are:\n* Perform requests asynchronously to avoid blocking the UI thread\n* Presenting a loading indicator to give the user feedback on progress\n* Allow the user to page through results\n* Handle and present errors as they occur\n* Dispatch change notifications to your adapter so your `RecyclerView` or `ListView` can react to content changes\n\nThe `power-adapters-data` extension module aims to simplify this by encapsulating the above concerns into a single\nobject: `Data\u003cT\u003e`. In doing so, it allows you to retain one object when a config change occurs, like an orientation\nchange. This way you don't need to reload or parcel/unparcel all of your list results when that occurs. The `Data\u003cT\u003e`\nobject comprises much of the repetitive asynchronous UI \"glue\" code you'd otherwise have to write (and debug) yourself.\n\n```groovy\nimplementation 'com.nextfaze.poweradapters:power-adapters-data:0.26.0'\n```\n\n### Basic Data Usage\n\nThe recommended usage pattern is to instantiate a `Data\u003cT\u003e` object in your retained `Fragment`. The `Data.fromList` \nfactory method supports the simplest use case, fetching a list of items asynchronously:\n\n```java\npublic final class ProductListFragment extends Fragment {\n\n    private final Data\u003cProduct\u003e products = Data.fromList(() -\u003e api.getProducts());\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        // Retain this fragment so we don't need to reload the products after a config change\n        setRetainInstance(true);\n    }\n}\n```\n\nNow hook up your `Data\u003cProduct\u003e` instance and a `Binder` with your `RecyclerView`:\n\n```java\n@Override\npublic void onViewCreated(View view, Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n    PowerAdapter adapter = new DataBindingAdapter(products, productBinder);\n    recyclerView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));\n}\n```\n\n### Invalidating and Reloading\n\nAt some stage you'll want to request a reload of the elements from the remote source. You can do this using `reload()`,\n`refresh()`, or `invalidate()`. The behaviour of these methods differ slightly, but ultimately they all result in your\nitems being reloaded from the source. See the `Data` javadoc for how they differ.\n\n### DataLayout\n\n`DataLayout` aids in presenting the various states of a `Data` instance, by hiding and showing contents, empty, error,\nand loading child views.\nIt's a `RelativeLayout` subclass, and it works by accepting a `Data` instance, then registering to receive change\nnotifications. If the contents are empty, your marked empty view will be shown instead of the list view. If an error \noccurs, the error view will be shown until a reload is triggered. `DataLayout` has several extension points to customize\n this behaviour to suite the needs of your application.\n\nHere's an example of how to declare a `DataLayout` in XML. Notice the `layout_component` attributes:\n\n```xml\n\u003ccom.nextfaze.poweradapters.data.widget.DataLayout\n            xmlns:android=\"http://schemas.android.com/apk/res/android\"\n            xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n            android:id=\"@+id/news_fragment_data_layout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\u003e\n\n    \u003cListView\n            android:id=\"@+id/list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layout_component=\"content\"/\u003e\n\n    \u003cProgressBar\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            app:layout_component=\"loading\"/\u003e\n\n    \u003cTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            app:layout_component=\"empty\"\n            android:text=\"No items!\"/\u003e\n\n    \u003cTextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            app:layout_component=\"error\"\n            android:textColor=\"#ffff0000\"/\u003e\n\n\u003c/com.nextfaze.poweradapters.data.widget.DataLayout\u003e\n```\n\nThe `DataLayout` observes state changes of the `Data` to know when to update the view visibility. Connecting to your \n`DataLayout` and `RecyclerView` in Java code:\n\n```java\n@Override\npublic void onViewCreated(View view, Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n    PowerAdapter adapter = new DataBindingAdapter(products, productBinder);\n    listView.setAdapter(RecyclerPowerAdapters.toRecyclerAdapter(adapter));\n    dataLayout.setData(products);\n}\n```\n\n### RxJava Module\n\nAn RxJava module is provided: `power-adapters-data-rxjava2`. This is a simple adapter library that provides `Observable`s\nfor properties of `Data`:\n\n```java\nRxData.inserts(products).subscribe(event -\u003e handleProductInsert(event));\n```\n\n### Data Views\n\n`Data` has fluent chaining methods for providing filtered, transformed, or sorted views of its contents:\n\n```java\nData\u003cString\u003e names = ...\nData\u003cInteger\u003e lengths = names.transform(name -\u003e name.length);\n```\n\n```java\nData\u003cPost\u003e allPosts = ...\nData\u003cPost\u003e todaysPosts = names.filter(post -\u003e isToday(post.getDate()));\n```\n\n## Samples\n\nCheck the included sample project for a range of usage pattern examples.\n\n# Build\n\nBuilding instructions:\n\n```bash\n$ git clone git@github.com:NextFaze/power-adapters.git\n$ cd power-adapters\n$ ./gradlew clean build\n\n```\n\n# License\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextfaze%2Fpower-adapters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnextfaze%2Fpower-adapters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextfaze%2Fpower-adapters/lists"}