{"id":18465146,"url":"https://github.com/natario1/elements-deprecated","last_synced_at":"2025-04-08T08:31:44.869Z","repository":{"id":99104430,"uuid":"88114455","full_name":"natario1/Elements-Deprecated","owner":"natario1","description":"A library of reusable components for RecyclerView, simplifying the development of complex adapters.","archived":false,"fork":false,"pushed_at":"2018-06-03T02:26:43.000Z","size":5315,"stargazers_count":11,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-23T09:24:37.613Z","etag":null,"topics":["adapter","android","presenter","recyclerview","recyclerview-adapter","reusable-components"],"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/natario1.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":"2017-04-13T02:03:48.000Z","updated_at":"2023-04-26T10:17:22.000Z","dependencies_parsed_at":"2023-06-03T20:30:08.930Z","dependency_job_id":null,"html_url":"https://github.com/natario1/Elements-Deprecated","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natario1%2FElements-Deprecated","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natario1%2FElements-Deprecated/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natario1%2FElements-Deprecated/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natario1%2FElements-Deprecated/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/natario1","download_url":"https://codeload.github.com/natario1/Elements-Deprecated/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247804521,"owners_count":20999002,"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","android","presenter","recyclerview","recyclerview-adapter","reusable-components"],"created_at":"2024-11-06T09:12:06.553Z","updated_at":"2025-04-08T08:31:44.858Z","avatar_url":"https://github.com/natario1.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"*This library is deprecated. Please take a look at https://github.com/natario1/Elements for a new, modern version, based on the Architecture components and written in Kotlin.\nYou can still access the old version at a new location, see below.*\n\n\u003c!-- markdown-preview README.md --\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"art/elements.png\" vspace=\"10\" width=\"180\" height=\"180\"\u003e\n\u003c/p\u003e\n\n# Elements\n\n```groovy\ncompile 'com.otaliastudios:elements-deprecated:1.0.3'\n```\n\nA low-level library of reusable components for `RecyclerView`, with built-in awareness of / support for\npagination, state saving and restoration, asynchronous loading, dependencies and relative ordering of items.\n\nAt a higher level, Elements makes it easy to implement common patterns: \"endless adapters\",\nplaceholders for empty/loading lists, mixed content lists (headers, footers, sections, ads)\nwith dynamic animated items. Logic for loading, ordering and binding is split into separate components\nthat can be reused efficiently.\n\nTake a look at the [sample](https://github.com/natario1/Elements/tree/master/sample) app for a showcase.\n\n\u003cimg src=\"art/0.png\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/1.png\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/2.png\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/3.png\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/4.png\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/stories0.gif\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/stories1.gif\" width=\"20%\" hspace=\"5\"\u003e\u003cimg src=\"art/stories2.gif\" width=\"20%\" hspace=\"5\"\u003e\n\n## Contributing\n\nYou are welcome to contribute with issues, PRs or suggestions. This is missing a few improvements\nat the moment, and most urgently a test environment.\n\n# Quickstart\n\nInstantiate `ElementAdapter`, and simply set your `ElementSource` for data and your `ElementPresenter`\nfor presentation.\n\n```java\n// onCreate...\nElementAdapter adapter = new ElementAdapter();\nadapter.setSource(new SimpleSource());\nadapter.setPresenter(new SimplePresenter());\nadapter.restoreState(savedInstanceState);\nrecyclerView.setAdapter(adapter);\n\n// onSaveInstanceState\nadapter.saveState(outState);\n```\n\nThis is a simple implementation of `ElementSource` that provides strings:\n\n```java\npublic class SimpleSource extends ElementSource {\n\n  @Override protected Task\u003cList\u003cObject\u003e\u003e find(Pager.Page page) {\n    return Task.callInBackground(new Callable\u003cList\u003cObject\u003e\u003e() {\n      \n      @Override\n      public List\u003cObject\u003e call() throws Exception {\n        // Running in a background thread...\n        List\u003cObject\u003e list = new ArrayList\u003c\u003e();\n        list.add(\"This is\");\n        list.add(\"a simple list\");\n        list.add(\"for page \"+page.getPageNumber());\n        return list; \n      }\n    });\n  }\n\n  @Override protected boolean dependsOn(ElementSource other) {\n    return false;\n  }\n  \n  @Override protected ElementSerializer instantiateSerializer() {\n    return new StringSerializer(); // For state restoration\n  }\n}\n```\n\nAnd this is a simple implementation of `ElementPresenter`:\n\n```java\npublic class SimplePresenter extends ElementPresenter {\n\n  @Override protected View onCreateView(ViewGroup parent, int elementType) {\n    return new TextView(getContext()); // Or inflate a layout.\n  }\n\n  @Override protected void onBind(Pager.Page page, Holder holder, Element element) {\n    super.onBind(page, holder, element);\n    TextView textView = ((TextView) holder.getRoot());\n    textView.setText((String) element.getData());\n  }\n}\n```\n\nThe adapter will automatically load page 0 once it gets the `RecyclerView`.\nThat's enough to get you started. Keep reading to understand more about the concepts and how\nthis can be helpful in complex lists.\n\n# In-depth docs\n\n\u003c!-- doctoc README.md --github --notitle --\u003e\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\n\n- [Concepts](#concepts)\n- [ElementAdapter](#elementadapter)\n  - [Pages](#pages)\n  - [Coordination](#coordination)\n  - [Tasks](#tasks)\n- [Element](#element)\n- [ElementSource](#elementsource)\n  - [Find behavior](#find-behavior)\n  - [Ordering behavior](#ordering-behavior)\n- [ElementPresenter](#elementpresenter)\n  - [Binding process](#binding-process)\n  - [State restoration](#state-restoration)\n  - [Clicks](#clicks)\n  - [Example presenter](#example-presenter)\n- [ElementSerializer](#elementserializer)\n- [BaseSource / BasePresenter](#basesource--basepresenter)\n  - [Pagination placeholders](#pagination-placeholders)\n  - [Empty placeholders](#empty-placeholders)\n  - [Error placeholders](#error-placeholders)\n  - [Loading placeholders](#loading-placeholders)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Concepts\n\nElements comes useful in complex apps that display lists with mixed content or particular ordering\n/ fetching behavior. By providing simple component units, you won't be writing huge adapter classes\nanymore (in fact, you don't extend `RecyclerView.Adapter` at all).\n\nWhat you **will not** do:\n\n- Extend `RecyclerView.Adapter`\n- Extend `ViewHolder`\n- Use `AsyncTask`s\n- Use `notify*` methods\n- Incapsulate loading, ordering, display and callbacks logic in a single adapter class\n  or, worse, in the external container (e.g. fragment).\n- Fetch data again on configuration changes\n\nWhat you **will** do:\n\n- Create one or more `ElementSource`s that fetch data asyncrhonously from your database\n  and talk with each other if needed\n- Create one or more `ElementPresenter`s that hold views and fill them with model data returned by\n  sources\n- Use (or create) a `ElementSerializer` to serialize your data during configuration changes\n\n## ElementAdapter\n\n`ElementAdapter` is `final`. All you should do is instantiate, set sources, set presenters, and\nsave / restore state when needed.\n\n```java\n// onCreate...\nElementAdapter adapter = new ElementAdapter();\nadapter.setSource(...);\nadapter.setPresenter(...);\nadapter.restoreState(savedInstanceState);\nrecyclerView.setAdapter(adapter);\n\n// onSaveInstanceState\nadapter.saveState(outState);\n```\n\nThe adapter cares about page management, coordination between components, and task management.\n\n### Pages\n\nThe adapter will start to make you think in terms of smaller groups of items called pages, through\nmethods like `#getCurrentPage()`, `#loadSinglePage(int)` and so on.\nThis is convenient because you don't query the whole database, plus you can always work with 0-based\npositions for each page.\n\nYou will usually load one page at a time starting at 0. When a page is requested,\n\n- all the registered `ElementSource`s are asked for elements for that page\n- all the registered `ElementSource` have a chance to define relative ordering policy\n- elements are laid out by one (or more) `ElementPresenter` instance(s)\n\nElements can get their way into a page also through the `Pager.Page` object, that provides\nutility methods (insert, remove, replace) that update the dataset and internally call notify* on the\nadapter. **notify methods should never be called by you; it will likely break the internal state**.\nJust use sources or a `Pager.Page` modification, that will provide animations as well.\n\n### Coordination\n\nSources and presenters are designed to be reusable. A single adapter can have multiple source and\na single presenter, a single source and multiple presenters, or whatever you wish.\nWhat matters is that every `Element` gets at least a `ElementPresenter` to be laid out.\nThe logic goes as follows:\n\n- sources provide a list of objects for a certain page\n- `int ElementSource#getElementType(Object)` is called to get an **element type** (kind of a view type in the ordinary recycler) for that object\n- presenters register to deal with a certain element type through `List\u003cInteger\u003e ElementPresenter#getElementTypes()`\n\nIf no presenter is found for a certain type, an exception is thrown.\n\n### Tasks\n\nThe library is built on top of Facebook's lightweight [bolts library](https://github.com/BoltsFramework/Bolts-Android).\n`Task`s are entities similar to Javascript Promises, representing actions yet to be completed.\nYou will have to learn how to write a `Task` for your queries, but that is straightforward:\n\n```java\nreturn Task.callInBackground(new Callable\u003cList\u003cObject\u003e\u003e() {\n  @Override\n  public List\u003cObject\u003e call() {\n    // This runs in a background thread.\n    // Do sync stuff...\n    return data;\n  }\n})\n```\n\u003c!--\nOr more generally,\n\n```java\nfinal TaskCompletionSource\u003cList\u003cObject\u003e\u003e tcs = new TaskCompletionSource\u003c\u003e();\n// Do something async\ndatabase.setCallback(new Callback() {\n  public void done(List\u003cObject\u003e data, Exception e) {\n    if (e == null) {\n      tcs.setResult(data);\n    } else {\n      tcs.setError(e);\n    }\n  }\n});\ndatabase.query();\nreturn tcs.getTask(); // Our task that will complete later.\n``` --\u003e\n\nEach `ElementSource` will provide a task for fetching objects for a certain page of the list.\n`ElementAdapter` tries to bundle these tasks together: they are executed concurrently by\ndefault, but you can declare dependencies between sources such that, for example, one source task will\nbe run after the others have completed (e.g. for adding internal headers to a list).\n\n## Element\n\nAn `Element` is the base block of the library. It can't be instantiated directly, but you will find\nelements in various callbacks. Each element basically holds an `Object` representing data for that\nitem, and an `elementType` as returned by the source that originated that.\n\nElements should be created internally with the objects that your source finds, but if you really have\nto create a new `Element` in a different time (e.g. to insert it to a `Pager.Page`), there are a few\nways to do so, namely `ElementAdapter#createElement()` and `Element#cloneWithData()`.\n\n## ElementSource\n\n`ElementSource` is the component that asynchronously fetches data to be modeled and later laid out by\n`ElementPresenter`s. The key point about sources is that (**if needed**) they can declare relative\ndependencies with each other, in a `CoordinatorLayout` fashion, through the `boolean dependsOn(other)` method.\n\nThis can be overriden to declare a dependency and has direct consequences explained below.\n\n### Find behavior\n\nWhen adapter is asked for a page, each source will go through different callbacks.\n\n1. (if we declared some dependency) `onDependencyAfterFind(Pager.Page, ElementSource, Task)`:\n   Notifies that a source we depend on has just finished its find task. At this point we are given\n   the option to alter the other source results, before we find our own.\n2. `onPrepareFind(Pager.Page, List)`: lets this source prepare the find call.\n   If we declared some dependencies, `List` is passed with all the objects found by sources we depend on\n   for this same page.\n3. `Task\u003cList\u003cObject\u003e\u003e find(Pager.Page)`: this is abstract and must be filled. Asks this source for\n   its elements, in a asynchronous action embedded in a `Task`.\n4. `onAfterFind(Pager.Page, Task)`: called right after find. The passed task is completed and can be\n   queried and altered with `Task#getResult()`. It is legit to alter our own results here.\n   *After this ends, other sources that depend on this source will also be able to alter our results\n   through their own `onDependencyAfterFind`.*\n5. `onFindError(Pager.Page, Exception)`: notifies that, after the whole chain, an error was encountered.\n   This makes it possible to provide replacement data (an error view, cached data...).\n\nImplicitly, this means that declaring a dependency ensures that this source's find will be called\n**after** the dependency find has finished. This can be very useful (think of header letters for a\nalphabetically sorted list).\n\n### Ordering behavior\n\nThe default behavior is that elements are added to the page in the order they come from the async tasks.\nFor instance, if there are two independent sources, their find methods will be called concurrently.\nThe one who completes first will have the objects added first to the recycler.\n\nThings can get trickier if we declare some dependency: in that case, we have a chance to wait for other\nsources elements to be laid out, and order our elements with a relative logic:\n\n- `onPrepareOrder(Pager.Page, List, List)` let this source prepare the order call, based on the list\n  of dependent sources elements that are already ordered\n- `int orderBefore(Pager.Page, int, Element)` and similarly `orderAfter` let this source order 0+\n  items right before / after the given dependency element\n\nThese callbacks will make you think in terms of an imaginary page made just of this source objects,\nand this source's dependencies objects, with simple 0-based indexes. So if we want an `Ad` every 10\n`Items`, just declare `AdSource` to be dependent on `ItemsSource` and implement one-line logic in\n`orderBefore` or `orderAfter`.\n\n```java\n@Override\nprotected int orderBefore(Pager.Page page, int position, Element dependencyElement) {\n  // Release an Ad every 10 items from the source we depend on\n  return (position \u003e 0 \u0026\u0026 position % 10 == 0) ? 1 : 0;\n}\n```\n\n## ElementPresenter\n\n`ElementPresenter` is the component that takes model data as returned by `ElementSource#find()`,\nand binds data to actual views. In the ordinary `RecyclerView` world, a presenter holds the logic\nfor both view holder classes and onCreate / onBindViewHolder.\n\n### Binding process\n\n- `getElementTypes()` is called to understand which Elements should be bound by this presenter.\n  For simple situations, the default can be used by both presenter and source similar method.\n- `onCreateView(ViewGroup, int)` is called to inflate a view for the requested `elementType`.\n  It is up to you whether to use the same view or different views.\n- `onRegisterChildViews(int)` is called to create references to inner views that will be used later.\n  This improves performance, so that later you can call `Holder#getView(id)` instead of expensive findViewByIds.\n- `onInitialize(Holder)`: this is the point where you want to do operations that do not depend on\n  input data (e.g. color filters to drawables). Think of this as `onCreateViewHolder` in the adapter.\n- `onBind(Pager.Page, Holder, Element)`: performs actual binding.\n  You can access page informations, holder views through `Holder#getView(id)`, and model data through\n  `Element#getData()`.\n\n### State restoration\n\nThis is meant to be a stateful component, to store external state such as selected / checked items in\na list. For this reason, `saveState(Bundle)` and `restoreState(Bundle)` callbacks are provided.\n\n### Clicks\n\nBy default, presenter applies a click listener to the root view of each item, and you will receive\na call to `onElementClick(Pager.Page page, Holder holder, Element element)`. See\n`ElementPresenter#setOnClickListener()` for info.\n\n### Example presenter\n\n```java\npublic class Presenter extends ElementPresenter {\n  private int color;\n\n  public Presenter(Context context, int color) {\n    super(context);\n    this.color = color;\n  }\n\n  @Override protected View onCreateView(ViewGroup parent, int elementType) {\n    return new TextView(getContext()); // Or inflate a layout.\n  }\n\n  @Override protected void onInitialize(Holder holder) {\n    TextView textView = ((TextView) holder.getRoot());\n    textView.setTextColor(accentColor);\n  }\n\n  @Override protected void onBind(Pager.Page page, Holder holder, Element element) {\n    super.onBind(page, holder, element);\n    TextView textView = ((TextView) holder.getRoot());\n    textView.setText((String) element.getData());\n  }\n}\n```\n\n## ElementSerializer\n\n`ElementSerializer` is the component responsible of saving and restoring the dataset for a certain\npage and a certain source. Depending on what data you are modeling, you can use one of the built in\nserializers or create your own. The interface is simple:\n\n```java\npublic interface ElementSerializer {\n\n    void saveElements(Pager.Page page, ElementSource source, List\u003cObject\u003e elements, Bundle state);\n\n    List\u003cObject\u003e restoreElements(Pager.Page page, ElementSource source, Bundle state);\n}\n```\n\nThe library provides `ParcelableSerializer` (recommended), `SerializableSerializer`,\n`StringSerializer` and `StaticSerializer`. The static serializer should be your very last resource.\n\n## BaseSource / BasePresenter\n\nElements provides two basic (still abstract) implementations called `BaseSource` and `BasePresenter`.\nTogether, they allow the display of some special placeholders. Each placeholder will have a layout\nresource (override the defaults with `set*ViewRes()`) and private lifecycle callbacks if you need.\n\n### Pagination placeholders\n\nSpecial views that indicate the user that there is more content to be seen (a new page).\nUI wise, this can be a progress bar or a button saying \"Load more\". The policy for pagination must\nbe implemented in your source. The most reasonable is:\n\n- If you want to show 20 objects per page, ask the server for 21\n- If 21 are returned, remove the last one and call `BaseSource#appendPaginationPlaceholder(List)`.\n\nThe source will care about inserting a special object with a special `elementType`.\n`BasePresenter` will respond to this and provide default UI behavior.\nYou can customize the behavior and layout with `setPaginationMode()`:\n\n- `PAGINATION_MODE_ONBIND`: new page is requested when the placeholder is bound, with a small delay.\n- `PAGINATION_MODE_ONCLICK`: new page is requested when the placeholder is clicked.\n\n### Empty placeholders\n\nA special view that is shown when there's no content to be seen.\nThis works out of the box when, after loading page 0, the source returns no results.\n\n### Error placeholders\n\nA special view that is shown when the `Task` for objects has failed (e.g. an exception was thrown).\nYou can, for example, return a failed task when there is no connectivity, and show a network error.\n\n### Loading placeholders\n\nA spacial view that is shown *while* the `Task` for objects is going on, and removed when objects\nare found. This is typically a Progress Bar.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatario1%2Felements-deprecated","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnatario1%2Felements-deprecated","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatario1%2Felements-deprecated/lists"}