Just another one easy-to-use adapter for RecyclerView :rocket:

Overview

Elementary RecyclerView Adapter

Maven Central API License: Apache 2

Another one easy-to-use adapter for RecyclerView 🚀

Features:

  • DSL-like methods for building adapters similar to Jetpack Compose but intended for RecyclerView
  • no view holders; bind any model object directly to auto-generated view bindings
  • support of multiple item types
  • build-in click listeners
  • the library uses DiffUtil under the hood for fast updating of your list
  • support of integration either with your own adapters or with third-party adapters

cats-screenshot

Usage example

This library adds a few methods for easier implementation of ListAdapter. It relies on View Binding so you don't need to create view holders.

Simple example (1 item type)

Let's image you have Cat model class and R.layout.item_cat (View Binding generates ItemCatBinding class for this layout). Then you can write the following code:

val adapter = simpleAdapter<Cat, ItemCatBinding> {
     areItemsSame = { oldCat, newCat -> oldCat.id == newCat.id }
     bind { cat ->
         catNameTextView.text = cat.name
         catDescriptionTextView.text = cat.description
     }
     listeners {
         root.onClick { cat ->
             showCatDetails(cat)
         }
     }
 }

 recyclerView.adapter = adapter

 viewModel.catsLiveData.observe(viewLifecycleOwner) { list ->
     adapter.submitList(list)
 }

As you see, simpleAdapter<Item, ViewBinding> accepts 2 types:

  • any type of your model (Cat)
  • an implementation of ViewBinding which you don't need to write because the official View Binding library can do it.

Then use bind and listeners methods to bind your item to views and assign listeners respectively. You can access all views from you binding class inside the bind and the listeners sections by this reference (which can be also omitted):

val adapter = simpleAdapter<Cat, ItemCatBinding> {
    bind { cat -> // <--- your item to bind
        // access views by 'this' reference
        this.myTextView.text = cat.name
        // or directly by name in the generated binding class:
        myTextView.text = cat.name
    }
}

It's highly recommended to use a separate listeners section to assign click and long-click listeners to your views to avoid unnecessary object creation during item binding:

val adapter = simpleAdapter<Cat, ItemCatBinding> {
    ...
    listeners {
        // onClick for clicks
        deleteButton.onClick { cat ->
            viewModel.delete(cat)
        }
        // onLongClick for long clicks
        root.onLongClick { cat ->
            Toast.makeText(requireContext(), "Oooops", Toast.LENGTH_SHORT).show()
            true
        }
    }
}

Optionally you can adjust the logic of comparing old and new items by using areItemsSame and areContentsSame properties. They work in the same way as methods of DiffUtil.ItemCallback (click here for details). By default areItemsSame and areContentsSame compare items in terms of equals/hashCode so usually you don't need to use areContentsSame for data classes. But it's recommended to implement at least areItemsSame to compare your items by identifiers.

Typical example:

val adapter = simpleAdapter<Cat, ItemCatBinding> {
    // compare by ID
    areItemsSame = { oldCat, newCat -> oldCat.id == newCat.id }
    // compare content
    areContentsSame = { oldCat, newCat -> oldCat == newCat }
}

Another example (2 item types)

Let's add headers after every 10th cat to the list. For example, we can define the following structure:

sealed class ListItem {

    data class Header(
        val id: Int,
        val fromIndex: Int,
        val toIndex: Int
    ) : ListItem()

    data class Cat(
        val id: Long
        val name: String
        val description: String
    ) : ListItem()

}

Add layout for each item type: R.layout.item_cat (ItemCatBinding will be generated) and R.layout.item_header (ItemHeaderBinding will be generated).

Then we can write an adapter by using adapter and addBinding methods:

val adapter = adapter<ListItem> { // <--- Base type

    // map concrete subtype ListItem.Cat to the ItemCatBinding:
    addBinding<ListItem.Cat, ItemCatBinding> {
        areItemsSame = { oldCat, newCat -> oldCat.id == newCat.id }
        bind { cat ->
            catNameTextView.text = cat.name
            catDescriptionTextView.text = cat.description
        }
        listeners {
            deleteImageView.onClick(viewModel::deleteCat)
            root.onClick { cat ->
                viewModel.openDetails(cat)
            }
        }
    }

    // map concrete subtype ListItem.Header to the ItemHeaderBinding:
    addBinding<ListItem.Header, ItemHeaderBinding> {
        areItemsSame = { oldHeader, newHeader -> oldHeader.id == newHeader.id }
        bind { header ->
            titleTextView.text = "Cats ${header.fromIndex}...${header.toIndex}"
        }
    }
}

Then assign the list with cats and headers to the adapter by using submitList method:

val list: List<ListItem> = getListFromSomewhere()
adapter.submitList(list)

Advanced usage

Custom listeners

Sometimes simple clicks and long clicks are not enough for your list items. To integrate custom listeners, you can use onCustomListener { ... } method.

Usage example (let's assume some view can accept a double tap listener):

val adapter = simpleAdapter<Cat, ItemCatBinding> {
    ...
    listeners {
        someDoubleTapView.onCustomListener {
            someDoubleTapView.setOnDoubleTapListener { // <-- this is a method of the view
                // use item() call for getting the current item data
                val cat = item()
                viewModel.onDoubleTap(cat)
            }
        }
    }
}

Integration with other libraries

It's possible to tie together your own adapters or adapters from other third-party libraries with this library. You can use adapterDelegate() or simpleAdapterDelegate() calls in order to create a bridge between libraries.

For example, you can tie the PagingDataAdapter (see Paging Library V3) and this library.

Usage example:

  1. Implement a subclass of PagingDataAdapter (add AdapterDelegate to the constructor):

    class PagingDataAdapterBridge<T : Any>(
        private val delegate: AdapterDelegate<T>
    ) : PagingDataAdapter<T, BindingHolder>(
        delegate.itemCallback()
    ){
    
        override fun onBindViewHolder(holder: BindingHolder, position: Int) {
            // please note, NULL values are not supported!
            delegate.onBindViewHolder(holder, getItem(position)!!)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder {
            return delegate.onCreateViewHolder(parent, viewType)
        }
    
        override fun getItemViewType(position: Int): Int {
            // please note, NULL values are not supported!
            return delegate.getItemViewType(getItem(position)!!)
        }
    
    }
  2. Write a method for creating instances of PagingDataAdapter:

    inline fun <reified T : Any, reified B : ViewBinding> pagingAdapter(
        noinline block: ConcreteItemTypeScope<T, B>.() -> Unit
    ): PagingDataAdapter<T, BindingHolder> {
        val delegate = simpleAdapterDelegate(block)
        return PagingDataAdapterBridge(delegate)
    }
  3. Now you can use pagingAdapter { ... } call for creating instances of PagingDataAdapter from Paging Library V3

    val adapter = pagingAdapter<Cat, ItemCatBinding> {
        areItemsSame = { oldCat, newCat -> oldCat.id == newCat.id }
        bind { cat ->
            catNameTextView.text = cat.name
            catDescriptionTextView.text = cat.description
        }
        listeners {
            root.onClick { cat ->
                Toast.makeText(context(), "${cat.name} meow-meows", Toast.LENGTH_SHORT).show()
            }
        }
    }
    
    recyclerView.adapter = adapter
    
    lifecycleScope.launch {
        viewModel.catsPagingDataFlow.collectLatest {
            adapter.submitData(it)
        }
    }
    

Installation

  • Add View Binding to your build.gradle file:

    android {
        ...
        buildFeatures {
            viewBinding true
        }
        ...
    }
    
  • Add the library to the dependencies section of your build.gradle script:

    dependencies {
        ...
        implementation 'com.elveum:element-adapter:0.3.1'
    }
    

Changelog

v0.3

  • Added a couple extension method for getting resources to the bind and listeners block
  • Added onCustomListener { ... } method for assigning custom listeners
  • Added adapterDelegate { ... } and simpleAdapterDelegate { ... } methods for easier integration with third-party adapters

v0.2

  • Added context() extension method
  • Updated minSDK from 23 to 21

v0.1

  • The first release

License

Apache License 2.0

You might also like...
kotlin dsl for kids to simplify RecyclerView.Adapter logic

KidAdapter RecyclerView adapter for kids. A kotlin dsl mechanism to simplify and reduce boilerplate logic of a RecyclerView.Adapter. With KidAdapter y

A RecyclerView Adapter which allows you to have an Infinite scrolling list in your apps
A RecyclerView Adapter which allows you to have an Infinite scrolling list in your apps

Infinite Recycler View A RecyclerView Adapter which allows you to have an Infinite scrolling list in your apps. This library offers you a custom adapt

Reproducible sample with Fix for Memory Leak in RecyclerView Adapter
Reproducible sample with Fix for Memory Leak in RecyclerView Adapter

Memory Leak RecyclerView Adapter Reproducible Sample with Fix Video Instructions: https://www.youtube.com/c/awesomedevnotes Code Only the relevant and

RecyclerView With No Adapter | Available For Jetpack Compose
RecyclerView With No Adapter | Available For Jetpack Compose

About This Project Available on Google Dev Library Click Here RecyclerView No Adapter (Adapter Has Been Handled) RecyclerView No Adapter Using ViewBin

A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView.
A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView.

RecyclerViewSwipeDismiss A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView. Preview How to use Add these lines to yo

A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView.
A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView.

RecyclerViewSwipeDismiss A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView. Preview How to use Add these lines to yo

ANDROID. ChipsLayoutManager (SpanLayoutManager, FlowLayoutManager). A custom layout manager for RecyclerView which mimicric TextView span behaviour, flow layouts behaviour with support of amazing recyclerView features
ANDROID. ChipsLayoutManager (SpanLayoutManager, FlowLayoutManager). A custom layout manager for RecyclerView which mimicric TextView span behaviour, flow layouts behaviour with support of amazing recyclerView features

ChipsLayoutManager This is ChipsLayoutManager - custom Recycler View's LayoutManager which moves item to the next line when no space left on the curre

Carousel Recyclerview let's you create carousel layout with the power of recyclerview by creating custom layout manager.
Carousel Recyclerview let's you create carousel layout with the power of recyclerview by creating custom layout manager.

Carousel Recyclerview let's you create carousel layout with the power of recyclerview by creating custom layout manager.

RecyclerView : SleepQualityTracker with RecyclerView app
RecyclerView : SleepQualityTracker with RecyclerView app

RecyclerView - SleepQualityTracker with RecyclerView app SleepQualityTracker with RecyclerView This app builds on the SleepQualityTracker developed pr

Comments
  • Stable ids support

    Stable ids support

    Added support for stable ids in recycler view. It might be helpful for optimizations, singe/multiple item selection and drag and dropping functionality

    opened by Dsyncer 2
Owner
Roman Andrushchenko
Roman Andrushchenko
Easy RecyclerView Adapter

GenericAdapter Easy RecyclerView Adapter Getting started build.gradle allprojects { repositories { // ... maven { url 'https://jit

JaredDoge 4 Dec 3, 2021
Yet another adapter delegate library.

Yet another adapter delegate library. repositories { ... maven { url 'https://jitpack.io' } } ... dependencies { implementation("com.git

Mike 10 Dec 26, 2022
Elegant design and convenient to use RecyclerView adapter library based on Kotlin DSL.

xAdapter: Kotlin DSL 风格的 Adapter 封装 1、简介 该项目是 KotlinDSL 风格的 Adapter 框架封装,用来简化 Adapter 调用,思想是采用工厂和构建者方式获取 Adapter 避免代码中定义大量的 Adapter 类。该项目在 BRVAH 的 Ada

ShouHeng 17 Oct 9, 2022
Add RecyclerView, use Adapter class and ViewHolder to display data.

فكرة المشروع في هذا المشروع سنقوم بعرض قائمة من البيانات للطلاب على واجهة تطبيق Android بإستخدام: مفهوم RecyclerView مفهوم Adapter مفهوم ViewModel محت

Shaima Alghamdi 3 Nov 18, 2021
Pagination-RecyclerView - Simple and easy way to Paginating a RecyclerView

Pagination-RecyclerView Simple and easy way to Paginating a RecyclerView Android

Rakshit Nawani 0 Jan 3, 2022
A RecyclerView that implements pullrefresh and loadingmore featrues.you can use it like a standard RecyclerView

XRecyclerView a RecyclerView that implements pullrefresh , loadingmore and header featrues.you can use it like a standard RecyclerView. you don't need

XRecyclerView 5.3k Dec 26, 2022
A RecyclerView that implements pullrefresh and loadingmore featrues.you can use it like a standard RecyclerView

XRecyclerView a RecyclerView that implements pullrefresh , loadingmore and header featrues.you can use it like a standard RecyclerView. you don't need

XRecyclerView 5.3k Dec 26, 2022
Don't write a RecyclerView adapter again. Not even a ViewHolder!

LastAdapter Don't write a RecyclerView adapter again. Not even a ViewHolder! Based on Android Data Binding Written in Kotlin No need to write the adap

Miguel Ángel Moreno 781 Dec 19, 2022
Android library defining adapter classes of RecyclerView to manage multiple view types

RecyclerView-MultipleViewTypeAdapter RecyclerView adapter classes for managing multiple view types Release Note [Release Note] (https://github.com/yqr

Yoshihito Ikeda 414 Nov 21, 2022
A Common RecyclerView.Adapter implementation which supports all kind of items and has useful data operating APIs such as remove,add,etc.

##PowerfulRecyclerViewAdapter A Common RecyclerView.Adapter implementation which supports any kind of items and has useful data operating APIs such as

null 313 Nov 12, 2022