All Projects → manneohlund → Smart Recycler Adapter

manneohlund / Smart Recycler Adapter

Licence: apache-2.0
Small, smart and generic adapter for recycler view with easy and advanced data to ViewHolder binding.

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Smart Recycler Adapter

Fastadapter
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction...
Stars: ✭ 3,512 (+1682.74%)
Mutual labels:  swipe, drag-and-drop, recyclerview-adapter
Recyclerviewevent
RecyclerView onItemClick、onItemLongClick、drag、swipe、divider、reuse disorder RecyclerView 梳理:点击&长按事件、分割线、拖曳排序、滑动删除、优雅解决 EditText 和 CheckBox 复用错乱问题
Stars: ✭ 265 (+34.52%)
Mutual labels:  swipe, recyclerview-adapter
FancyAdapters
A collection of customizable RecyclerView Adapters for Android, that provide various functionality like item selection, contextual action mode controls, drag&drop and swiping, among other.
Stars: ✭ 49 (-75.13%)
Mutual labels:  drag-and-drop, swipe
Dragdropswiperecyclerview
Kotlin Android library that extends RecyclerView to support gestures like drag & drop and swipe, among others. It works with vertical, horizontal and grid lists.
Stars: ✭ 469 (+138.07%)
Mutual labels:  swipe, drag-and-drop
Oneadapter
A Viewholderless Adapter for RecyclerView, who supports builtin diffing, states (paging, empty...), events (clicking, swiping...), and more.
Stars: ✭ 321 (+62.94%)
Mutual labels:  swipe, recyclerview-adapter
Android Advancedrecyclerview
RecyclerView extension library which provides advanced features. (ex. Google's Inbox app like swiping, Play Music app like drag and drop sorting)
Stars: ✭ 5,172 (+2525.38%)
Mutual labels:  swipe, drag-and-drop
Slider
Touch swipe image slider/slideshow/gallery/carousel/banner mobile responsive bootstrap
Stars: ✭ 2,046 (+938.58%)
Mutual labels:  swipe
Kau
An extensive collection of Kotlin Android Utils
Stars: ✭ 182 (-7.61%)
Mutual labels:  swipe
Phpagebuilder
A drag and drop page builder to manage pages in any PHP project.
Stars: ✭ 168 (-14.72%)
Mutual labels:  drag-and-drop
Hswiper Wx
微信小程序swiper插件
Stars: ✭ 167 (-15.23%)
Mutual labels:  swipe
Flap
Flap(灵动),一个基于 RecyclerView 的页面组件化框架。
Stars: ✭ 204 (+3.55%)
Mutual labels:  recyclerview-adapter
Moretype
new method to build data in RecyclerView with Kotlin!
Stars: ✭ 189 (-4.06%)
Mutual labels:  recyclerview-adapter
React Dragtastic
A simple drag and drop library for React which uses the more stable mouseDown/mouseUp event pattern instead of the problematic HTML5 drag and drop API
Stars: ✭ 181 (-8.12%)
Mutual labels:  drag-and-drop
React Native Head Tab View
Add collapsible headers to your tab-view components.
Stars: ✭ 171 (-13.2%)
Mutual labels:  swipe
Grapesjs
Free and Open source Web Builder Framework. Next generation tool for building templates without coding
Stars: ✭ 14,892 (+7459.39%)
Mutual labels:  drag-and-drop
Customadapter
RV Adapter 优雅封装,抽取列表模版,可以快速的添加一个列表,使用组装的方式构建Adapter,抽象Cell 角色,负责创建ViewHolder,绑定数据和逻辑处理。Cell为一个独立的组件。
Stars: ✭ 172 (-12.69%)
Mutual labels:  recyclerview-adapter
Vue Swing
Vue.js wrapper for Swing
Stars: ✭ 193 (-2.03%)
Mutual labels:  swipe
Fastadapter
快速使用的RecyclerView Adapter
Stars: ✭ 170 (-13.71%)
Mutual labels:  recyclerview-adapter
Loadmorewrapper
📦 make recyclerView supports load more and customize the footer view, without changes to the original adater of recyclerView. 在不改动 RecyclerView 原有的 adapter 的情况下,使 RecyclerView 滑动到底部的时候能够加载更多和自定义底部视图。
Stars: ✭ 179 (-9.14%)
Mutual labels:  recyclerview-adapter
Laravel Pagebuilder
A drag and drop pagebuilder to manage pages in any Laravel project.
Stars: ✭ 189 (-4.06%)
Mutual labels:  drag-and-drop

smart-recycler-adapter

Download Android Arsenal Build Status

Never code any boilerplate RecyclerAdapter again! This library will make it easy and painless to map your data item with a target ViewHolder.

Features

OnViewEventListener
ItemTouchHelper Swipe, Drag & Drop extensions
ViewTypeResolver
SmartStateHolder
Sticky header
Nested adapter
Pagination
DiffUtil
Filter

Release overview

  • Extension libraries (ViewEvent, DiffUtil, NestedAdapter, StickyHeader, Filter) v5.0.0-rc01
  • Kotlin + AndroidX (jcenter, jitpack) v4.0.0
  • Java + AndroidX (jcenter, jitpack) v3.0.0
  • Java + AppCompat (jitpack) v2.2.0

Gradle

Add jcenter() or maven { url "https://dl.bintray.com/manneohlund/maven" } to your build.gradle under repositories

Core

dependencies {
  // Core SmartRecyclerAdapter
  implementation 'io.github.manneohlund:smart-recycler-adapter:5.0.0-rc01'
}

Extensions

dependencies {
  // ViewEvent click listeners, multi select, swipe dismiss and drag & drop
  implementation 'io.github.manneohlund:smart-recycler-adapter-viewevent:1.0.0-beta03'
  // DiffUtil extension library
  implementation 'io.github.manneohlund:smart-recycler-adapter-diffutil:1.0.0-beta01'
  // Nested adapter extension library
  implementation 'io.github.manneohlund:smart-recycler-adapter-nestedadapter:1.0.0-beta01'
  // Sticky header extension library
  implementation 'io.github.manneohlund:smart-recycler-adapter-stickyheader:1.0.0-alpha02'
  // Filter extension library
  implementation 'io.github.manneohlund:smart-recycler-adapter-filter:1.0.0-alpha01'
}

Basic

Basic adapter creation

SmartRecyclerAdapter
  .items(items)
  .map(MoviePosterModel::class, PosterViewHolder::class)
  .map(MovieBannerModel::class, BannerViewHolder::class)
  .map(MovieModel::class, MovieViewHolder::class)
  .map(TopNewsModel::class, TopNewsViewHolder::class)
  .add(OnClickEventListener { event: ViewEvent.OnClick -> 
    // Handle event
  })
  .into<SmartRecyclerAdapter>(recyclerView)

SmartViewHolder

Just extend your ViewHolder class with SmartViewHolder and pass in the target type ex SmartViewHolder<Mail>.
Note that the constructor can both take View or ViewGroup as parameter, in this case PosterViewHolder(parentView: ViewGroup) to avoid casting to ViewGroup while inflating.
The parentView is the recyclerView.
The method unbind has an default implementation and is optional.

class PosterViewHolder(parentView: ViewGroup) : 
  SmartViewHolder<MovieModel>(parentView, R.layout.poster_item) {

  override fun bind(movie: MovieModel) {
    Glide.with(imageView)
      .load(model.posterUrl)
      .into(imageView)
  }

  override fun unbind() {
    Glide.with(imageView).clear(imageView)
  }
} 

Works with Android DataBinding! Just add the DataBinding LayoutInflater in super call. 🚀

class PosterViewHolder(parentView: ViewGroup) : 
  SmartViewHolder<MovieModel>(
    LayoutInflater.from(parentView.context)
      .inflate(R.layout.poster_item, parentView, false)
  )

Adapter creation with ViewTypeResolver

If you want to bind one data type with different view holders depending on some attribute you can set a ViewTypeResolver.
Note .map() call not needed in this case but you can combine if you want to.

SmartRecyclerAdapter
  .items(items)
  .setViewTypeResolver{ item, position -> {
    when { 
      item is MovieTrailerModel -> MovieTrailerViewHolder::class
      item is MovieModel && item.isRatedR() -> RMovieViewHolder::class
      else -> MovieViewHolder::class // Add default view if needed, else SmartRecyclerAdapter will look at the base `.map` mapping
    }
  }}
  .into(recyclerView)

SmartEndlessScrollRecyclerAdapter

A popular feature in apps is to have endless scrolling with pagination, in other words load more items when user has scrolled to bottom. With SmartEndlessScrollRecyclerAdapter you can achieve this.

  • setAutoLoadMoreEnabled defines if false load more button should be visible before loading.
  • setLoadMoreLayoutResource can also set your custom loading/loadmore view.
  • OnLoadMoreListener is called when scrolled to the last item and loading view is visible.

Create SmartEndlessScrollRecyclerAdapter

val endlessScrollAdapter: SmartEndlessScrollRecyclerAdapter = SmartEndlessScrollRecyclerAdapter
  .items(items)
  .setAutoLoadMoreEnabled(true)
  .setLoadMoreLayoutResource(R.layout.custom_loadmore_view)
  .setOnLoadMoreListener { adapter,  loadMoreViewHolder ->
    // Handle load more items
  }
  .map(MovieModel::class, MovieViewHolder::class)
  .into(recyclerView)

More SmartEndlessScrollRecyclerAdapter features

Enable/Disable endless scrolling and thus removing the loading view. endlessScrollAdapter.isEndlessScrollEnabled = false

Extension libraries

smart-recycler-adapter-viewevent

As of smart-recycler-adapter:v5.0.0 all ViewEvent listeners have been removed from SmartRecyclerAdapter and added in this extension library smart-recycler-adapter-viewevent.

Essentially the SmartRecyclerAdapter will now hold a list of SmartViewHolderBinder that can implement any of these interfaces to listen to the adapter view holder stages:

  • OnSmartRecycleAdapterCreatedListener Invoked from SmartRecyclerAdapter init
  • OnCreateViewHolderListener Invoked from SmartRecyclerAdapter.onCreateViewHolder
  • OnBindViewHolderListener Invoked from SmartRecyclerAdapter.onBindViewHolder
  • OnViewAttachedToWindowListener Invoked from SmartRecyclerAdapter.onViewAttachedToWindow
  • OnViewDetachedFromWindowListener Invoked from SmartRecyclerAdapter.onViewDetachedFromWindow

This way all extension libraries has full control over the view holder lifecycle stages and can be hooked with various listeners and state holders.
You can create any type of SmartViewHolderBinder extension and implement any number of the listed adapter listeners.

View Events

In io.github.manneohlund:smart-recycler-adapter-viewevent comes with a range of ViewEvent listeners.
Default viewId is R.id.undefined that targets root view of the ViewHolder (ViewHolder.itemView).

SmartRecyclerAdapter
  .items(items)
  .map(MovieModel::class, MovieViewHolder::class)
  // Your ViewHolder must implement CustomViewEventListenerHolder & SmartAdapterHolder
  .add(OnCustomViewEventListener { event: ViewEvent -> })
  // Adds click event listener to all SmartViewHolder root itemView
  .add(OnClickEventListener { event: ViewEvent.OnClick -> })
  // Adds long click event listener to all SmartViewHolder root itemView
  .add(OnLongClickEventListener { event: ViewEvent.OnLongClick -> })
  // Adds click event listener to PosterViewHolder root itemView
  .add(OnClickEventListener(PosterViewHolder::class) { event: ViewEvent.OnClick -> })
  // Adds click event listener to PosterViewHolder on view with id R.id.playButton
  .add(OnClickEventListener(PosterViewHolder::class, R.id.playButton){ event: ViewEvent.OnClick -> })
  // Adds touch event listener to PosterViewHolder
  .add(OnTouchEventListener(PosterViewHolder::class) { event: ViewEvent.OnTouchEvent ->
    when(it.event.action) {
      MotionEvent.ACTION_UP -> // Handle touch event
    }
  })
  .into(recyclerView)

SmartStateHolder & ViewEventViewModel

With OnMultiItemSelectListener, OnMultiItemCheckListener, OnSingleItemSelectListener & OnSingleItemCheckListener you can easily keep track on selection states.

In combination with ViewEventViewModel you can keep selection states during screen rotation within the Activity lifecycle.
ViewEventViewModel provides a live data for the selection events.

OnMultiItemSelectListener

OnMultiItemSelectListener holds multi select states for recycler adapter positions and takes 4 arguments:

  • If enableOnLongClick is true multi select will be enabled after a long click, otherwise a regular ViewEvent.OnClick will be emitted when tapping.
  • viewId is by default R.id.undefined to target all SmartViewHolder.itemView.
  • viewHolderType is by default SmartViewHolder::class to target all view holders.
  • eventListener is by default noop in case of OnMultiItemSelectListener will be used with ViewEventViewModel along with live data observer.
// Define your ViewEventViewModel for OnMultiItemSelectListener to preserve state.
class MultiItemSelectViewModel :
  ViewEventViewModel<ViewEvent, OnMultiItemSelectListener>(
    OnMultiItemSelectListener(
      enableOnLongClick = true,
    )
)

// Get MultiItemSelectViewModel by androidx default viewModels provider.
private val multiItemSelectViewModel: MultiItemSelectViewModel by viewModels()

// Observe ViewEvent live data.
SmartRecyclerAdapter
  .items(items)
  .map(Integer::class, SimpleSelectableItemViewHolder::class)
  .add(multiItemSelectViewModel.observe(this) { event: ViewEvent ->
    // Either ViewEvent.OnClick or ViewEvent.OnItemSelected when enableOnLongClick = true
  })
  .into(recyclerView)

See sample app section: #SmartStateHolder

Drag & Drop

AutoDragAndDropBinder will be activated on long press if longPressDragEnabled = true
and on release the AutoDragAndDropBinder will automatically notify the SmartRecyclerAdapter about the item move.
You can extend the BasicDragAndDropBinder or DragAndDropEventBinder and create your custom implementation.

SmartRecyclerAdapter
  .items(items)
  .map(Integer::class, SimpleItemViewHolder::class)
  .add(AutoDragAndDropBinder(longPressDragEnabled = true) { event: ViewEvent.OnItemMoved ->
    // Handle drag event
  })
  .into(recyclerView)

See sample app section: #SmartStateHolder

Swipe dismiss

AutoRemoveItemSwipeEventBinder will automatically remove the item from the adapter on swipe.
You can extend the BasicSwipeEventBinder or SwipeEventBinder.kt and create your custom implementation.

SmartRecyclerAdapter
  .items(items)
  .map(Integer::class, SimpleItemViewHolder::class)
  .add(AutoRemoveItemSwipeEventBinder(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { event: ViewEvent.OnItemSwiped ->
    // Handle swipe event
  })
  .into(recyclerView)

See sample app section: #SmartStateHolder

smart-recycler-adapter-stickyheader

With io.github.manneohlund:smart-recycler-adapter-stickyheader it's super easy to add a sticky header recycler view item decoration.
Just set the target headerItemType and the StickyHeaderItemDecorationExtension will do the rest.
You can even add a sticky header item touch event listener.

SmartRecyclerAdapter
  .items(items)
  .map(String::class, SimpleHeaderViewHolder::class)
  .map(Integer::class, SimpleItemViewHolder::class)
  .add(StickyHeaderItemDecorationExtension(
    headerItemType = HeaderViewHolder::class
  ) { motionEvent, itemPosition ->
    if (motionEvent.action == MotionEvent.ACTION_UP) {
      showToast("Header $itemPosition clicked")
    }
  })
  .into(recyclerView)

See sample app section: #Sticky header

smart-recycler-adapter-diffutil

As of smart-recycler-adapter:v5.0.0 diff util have been removed from SmartRecyclerAdapter and is added in this extension library smart-recycler-adapter-diffutil.

Essentially the SmartRecyclerAdapter will now hold a map of SmartRecyclerAdapterBinder that is the basic interface for SmartRecyclerAdapter binding extensions.

// If adapter items contains unspecified super type DiffPredicate bust be of type Any, DiffPredicate<Any>
private val predicate = object : DiffUtilExtension.DiffPredicate<Int> {
  override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean {
    return oldItem == newItem
  }
    
  override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean {
    return oldItem == newItem
  }
}

// Add SimpleDiffUtilExtension to the adapter
SmartRecyclerAdapter
  .items((0..100).toMutableList())
  .map(Integer::class, SimpleItemViewHolder::class)
  .add(SimpleDiffUtilExtension(predicate))
  .into(recyclerView)

// Add some new random items
smartRecyclerAdapter.diffSwapList((0..100).shuffled().toMutableList())

See sample app section: #DiffUtil

smart-recycler-adapter-nestedadapter

As of smart-recycler-adapter:v5.0.0 static nested adapter mapping have been removed from SmartRecyclerAdapter and is added in this extension library smart-recycler-adapter-nestedadapter.
Default binder in nestedadapter is SmartNestedAdapterBinder implements SmartViewHolderBinder for basic view holder mapping functionality.
SmartRecyclerAdapter will hold the SmartNestedAdapterBinder references and call the default implemented interfaces OnCreateViewHolderListener, OnBindViewHolderListener, OnViewRecycledListener on ViewHolder lifecycle stages.
SmartViewHolder subclasses must implement SmartNestedRecyclerViewHolder in order for SmartNestedAdapterBinder to get the target recyclerView.

How does it work? 👇

SmartViewHolder

Sample uses kotlin synthetic view property import!

class NestedRecyclerViewHolder(parentView: ViewGroup) :
  SmartViewHolder<MovieCategory>(parentView, R.layout.nested_recycler_view),
  SmartNestedRecyclerViewHolder {

  override val recyclerView: RecyclerView = itemView.nestedRecyclerView

  init {
        // Set RecyclerView properties here or with RecyclerViewBinder
    itemView.nestedRecyclerView.apply {
      layoutManager = LinearLayoutManager(context, HORIZONTAL, false)
      isNestedScrollingEnabled = false
      setHasFixedSize(true)
    }
  }

  override fun bind(item: MovieCategory) {
    itemView.title.text = item.title
  }
}

SmartRecyclerAdapter

SmartNestedAdapterBinder will only target NestedRecyclerViewHolder.
Supply a SmartAdapterBuilder or SmartEndlessScrollAdapterBuilder that will be build a new nested adapter for each NestedRecyclerViewHolder. With reuseParentAdapterRecycledViewPool set to true will reuse the parent SmartRecyclerAdapters RecyclerView.RecycledViewPool in all nested adapters.

SmartRecyclerAdapter
  .items(items)
  .add(
    SmartNestedAdapterBinder(
      viewHolderType = NestedRecyclerViewHolder::class,
      reuseParentAdapterRecycledViewPool = true,
      smartRecyclerAdapterBuilder = SmartRecyclerAdapter.empty()
        .map(MovieModel::class, ThumbViewHolder::class)
        .add(OnClickEventListener { event: ViewEvent.OnClick ->
          // Handle nested adapter item click event
        })
    )
  )
  .add(OnClickEventListener(NestedRecyclerViewHolder::class, R.id.more) {
    // Handle parent adapter click event
  })
  .into(recyclerView)

See sample app section: #Nested SmartRecyclerAdapters

smart-recycler-adapter-filter

With the FilterExtension extension you can synchronously or asynchronously filter your items.

Create SmartRecyclerAdapter

SmartRecyclerAdapter.items(items)
  .map(String::class, HeaderViewHolder::class)
  .map(Int::class, FilterItemViewHolder::class)
  .add(OnClickEventListener {
    // Handle click event
  })
  .add(
    FilterExtension(
      filterPredicate = { item, constraint ->
        when (item) {
          is Int -> item.toString().contains(constraint)
          else -> true
        }
      },
      loadingStateListener = { isLoading ->
        // Set loading progress visibility
      }
    )
  )
  .into(recyclerView)

Set search view filter

searchView.setOnQueryTextListener(object : android.widget.SearchView.OnQueryTextListener {
    // Call some filter function ex: filter(newText)
})

Filter

fun filter(query: String?) {
  val filterExtension: FilterExtension = smartAdapter.get()

  filterExtension.filter(lifecycleScope, query, autoSetNewItems = true)
}

See sample app section: #Filter

Proguard

Only known rule is to keep constructor for all ViewHolders.
This rule is auto included in the consumer-rules.pro for smart-recycler-adapter library so no manual config is needed.

-keepclassmembers class **ViewHolder {
    public <init>(**);
}

More

For more samples test out the sample app and see the source code.

RecyclableViewHolder

Sometimes a ViewHolder created by the Adapter cannot be recycled due to its transient state.
In order to fix this is to implement RecyclableViewHolder in your SmartViewHolder extension so that upon receiving this callback, Adapter can clear the animation(s) that effect the View's transient state and return true so that the View can be recycled.

class MovieViewHolder : SmartViewHolder, RecyclableViewHolder {
  override fun onFailedToRecycleView(): Boolean = true
}

OnViewAttachedToWindowListener and OnViewDetachedFromWindowListener

If you want to catch when the view is attached and detached from the window in your ViewHolder you can implement OnViewAttachedToWindowListener and OnViewDetachedFromWindowListener in your SmartViewHolder extension.

Becoming detached from the window is not necessarily a permanent condition the consumer of an Adapter's views may choose to cache views offscreen while they are not visible, attaching and detaching them as appropriate.

class MovieViewHolder : SmartViewHolder, 
    OnViewAttachedToWindowListener, 
    OnViewDetachedFromWindowListener { 

  override fun onViewAttachedToWindow(viewHolder: RecyclerView.ViewHolder) {
    // Restore
  }

  override fun onViewDetachedFromWindow(viewHolder: RecyclerView.ViewHolder) {
    // Cache
  }
}

More SmartRecyclerAdapter features

val adapter: SmartRecyclerAdapter = SmartRecyclerAdapter
    .items(items)
    .map(MovieModel::class, MovieViewHolder::class)
    .into(recyclerView)

// We can add more data
adapter.addItems(items)

// Add data at index with animation
adapter.addItem(0, item)

// Add data at index without animation
adapter.addItem(0, item, false)

// Remove item at index with animation
adapter.removeItem(0)

// Remove item at index without animation
adapter.removeItem(0, false)

// Replace item at index with animation
adapter.replaceItem(0, item)

// Replace item at index without animation
adapter.replaceItem(0, item, false)

// Get items by type
adapter.getItems(MovieModel::class)

// Delete all items in the list
adapter.clear()
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].