Android library project that intends to simplify the usage of Adapters for recyclerView using Data Binding.
- No more adapters to create
- Supports (RecyclerView adapter) and (RecyclerView ListAdapter + DiffUtil)
- Supports API levels 19+
- Uses ViewDataBinding
- Handles unlimited multiple types automatically
- Handles Load more automatically
- Handles all the common actions for the recyclerview-adapter and more...
- 100% kotlin ~ Compatible with Java
- Easy to use and implement
Step 1. Add the JitPack repository to your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the library dependency to your project build.gradle:
dependencies {
implementation 'com.github.haizo-code:recyclerview-general-adapter:v2.5.6'
}
Create an instance from BlenderListAdapter and bind it to your recyclerview
private val adapter: BlenderListAdapter by lazy {
BlenderListAdapter(context = this)
}
recyclerview.adapter = adapter
Just need to pass your models (ListItems) to the adapter and that's it :) You can mix all the types together and it will be handled automatically by the adapter
- for BlenderListAdapter (RecyclerView ListAdapter - diffUtil) use:
adapter.submitList(list)
- for BlenderAdapter (Legacy recyclerView adapter) use:
adapter.updateList(list)
Create a ViewHolder and let it extends the BaseBindingViewHolder, params as:
- ViewDataBinding: Your ViewDataBinding class
- BaseActionCallback: Note that you can add your custom action callback (must implements BaseActionCallback)
class UserViewHolder(
private val binding: RowUserBinding,
actionCallback: BaseActionCallback?
) : BaseBindingViewHolder<User>(binding, actionCallback) {
override fun onBind(listItem: User) {
// use the listItem here..
}
}
Create a new instance from ViewHolderContract, expected Params:
- viewHolderClass: The ViewHolder Class
- layoutResId: The layout resource-id
- itemName (Optional): A readable name used for debug only
- callbackClass (Optional):
- extrasClass (Optional):
val USER_VIEW_HOLDER_CONTRACT = ViewHolderContract(
viewHolderClass = UserViewHolder::class.java,
layoutResId = R.layout.row_user
)
let your model directly implements ListItem and override the ViewHolderContract variable
data class User(
val id: String,
val Name: String
) : ListItem {
override var viewHolderContract: ViewHolderContract = USER_VIEW_HOLDER_CONTRACT
}
if you do not want to let your model implements the ListItem directly, then you can use the ListItemWrapper:
Step 1. Create a wrapper class that will hold your model in it and let it implement ListItemWrapper
class UserWrapper constructor(val user: User) : ListItemWrapper {
override var viewHolderContract: ViewHolderContract = USER_VIEW_HOLDER_CONTRACT
}
Step 2. Update the ViewHolder to use the wrapper instead of the direct model as:
BaseBindingViewHolder<UserWrapper>(viewDataBinding, actionCallback)
Step 3. finally, update the adapter with the list of wrappers, you can use the below helper method inside ListItemWrapper.kt:
val usersList: list<User> = YOUR-USERS-LIST
val wrappedUsersList = usersList.map { UserWrapper(it) }
adapter.submitList(wrappedUsersList)
And that's it :)
if you have a inner RecyclerView in your main recyclerview, such as horizontal list section in main vertical list, then its recommended to follow these steps:
1- Setup the container model
let your model that holds the inner list implement ListItemContainer instead of using ListItem/ListItemWrapper
data class StoriesList constructor(
var list: MutableList<Story>
) : ListItemsContainer<Story> {
override fun getInnerList() = list
override fun itemUniqueIdentifier() = "my_container_id"
...
}
then use this model normally as you are using ListItem
2- Use helper methods
to update an item in your inner list, use this method updateInnerListItem(..):
val updatedItem = myItem.copy(..) // the updated item should have different reference, so you can do its using the copy(..) method
adapter.updateInnerListItem(my_container_id, updateListItem)
- Note that the targeted listItem will be updated using the its 'itemUniqueIdentifier' so make sure to set it.
- Make sure to set your Recyclerview with (supportsChangeAnimations = false) to avoid flicking the whole inner list
- if you are using the BlenderListAdapter, then you need to override these methods in your ListItem class to be used in the DiffUtil:
data class User(
val id: String
) : ListItem {
override fun itemUniqueIdentifier(): String {
return id
}
override fun areContentsTheSame(newItem: ListItem): Boolean {
return this == newItem
}
}
- if you are using the BlenderAdapter, then its not supported, its recommended to switch to BlenderListAdapter
- for default usage, you can use this method as:
adapter.setupLoadMore(
loadMoreListener = object : LoadMoreListener {
override fun onLoadMore(nextPageNumber: Int, nextPagePayload: String?) {
// request load next page
}
})
- for custom load-more, you can use this method as:
adapter.setupLoadMore(
autoShowLoadingItem = true,
pageSize = 10,
loadingThreshold = 3,
loadMoreListener = object : LoadMoreListener {
override fun onLoadMore(nextPageNumber: Int, nextPagePayload: String?) {
// request load next page
}
override fun isShouldTriggerLoadMore(nextPageNumber: Int, nextPagePayload: String?): Boolean {
// You can control here if the loadMore should be triggered or not
}
})
Use this method to submit your first page or to update the list with the new items:
// this method will reset the page number and will update the current list with the new list
adapter.submitListItems(list)
Use this method to submit more items to the current list
adapter.submitMoreListItems(list)
// or
adapter.submitMoreListItems(list = list, nextPagePayload = "your-next-page-url-here")
// you can also send the next page url by using this method
// adapter.setNextPagePayload("your-next-page-url-here")
// ### if you are using the BlenderAdapter (legacy adapter), then you should used this method
// adapter.addMoreItems(list)
You can use your own loading-item to be shown when load-More triggered
Step 1. create your Loading-model (the same as you create a normal ListItem) but let it implements "LoadingListItem" instead of "ListItem"
// ViewHolder
class MyLoadingViewHolder constructor(
private val binding: RowMyLoadingBinding
) : BaseBindingViewHolder<MyLoadListItem>(binding) {
...
}
// Contract
val MY_LOADING_VIEW_HOLDER_CONTRACT = ViewHolderContract(
viewHolderClass = MyLoadingViewHolder::class.java,
layoutResId = R.layout.row_my_loading
)
// ListItem
class MyCustomLoadListItem : LoadingListItem {
override var viewHolderContract: ViewHolderContract = MY_LOADING_VIEW_HOLDER_CONTRACT
override fun areContentsTheSame(newItem: ListItem): Boolean = true
}
Step 2. pass your created Loading-model to the adapter
adapter.setLoadingListItem(MyCustomLoadListItem())
Step 1. create a class and let it implements the ViewHolderExtras:
class FooExtras : ViewHolderExtras {.. }
Step 2. send instance from the ViewHolderExtras that you have created to the adapter using this method:
adapter.setExtraParams(fooExtras)
Step 3. define this extra class in the ViewHolderContract initialization:
val USER_VIEW_HOLDER_CONTRACT = ViewHolderContract(
viewHolderClass = UserViewHolder::class.java,
layoutResId = R.layout.row_user,
extrasClass = FooExtras::class.java < ---Here
)
Step 4. Add the extra param in the constructor of the ViewHolder as the third param:
class UserViewHolder(
private val viewDataBinding: RowUserBinding,
private val actionCallback: BaseActionCallback?,
private val fooExtras: FooExtras
) : BaseBindingViewHolder<UserWrapper>(viewDataBinding, actionCallback)
...
}
1. In the initialization of the adapter:
private val adapter: BlenderListAdapter by lazy {
BlenderListAdapter(context = this, actionCallbacks = this) // <--- Here, you can pass many different callbacks
}
2. by using this method:
adapter.addActionCallback(..)
// In your ViewHolder
init {
attachClickListener(itemView)
// or you can call it manually as
// actionCallback.onItemClicked(...)
}
// In your fragment/activity that implements this callback
override fun onItemClicked(view: View, listItem: ListItem, position: Int) {
if (listItem is UserWrapper) {
// do your work here
}
}
You can pass your custom callbacks to the ViewHolder by:
Step 1. Create your interface and let it implements BaseActionCallback**
interface UserActionCallback : BaseActionCallback {
fun foo(user: User)
fun bar(user: User)
}
Step 2. Replace the BaseActionCallback with your own interface**
class UserViewHolder constructor(
private val binding: RowUserCardBinding,
private val actionCallback: UserActionCallback?
) : BaseBindingViewHolder<User>(binding, actionCallback) {
...
}
Step 3. define this extra class in the ViewHolderContract initialization:
val USER_VIEW_HOLDER_CONTRACT = ViewHolderContract(
viewHolderClass = UserViewHolder::class.java,
layoutResId = R.layout.row_user,
extrasClass = FooExtras::class.java,
callbackClass = UserActionCallback::class.java < ----Here
)
Method | Description |
---|---|
setItemsToFitInScreen | Set the number of the items that will fit in the screen (Horizontally), for ex, 1.5f will show one item and the half of the second item |
setItemWidthPercentage | Set the item width percentage for the screen width |
removeItemFromList | Remove item from the currentList and resubmit the change |
addItemToList | Add item to the currentList and resubmit the change |
updateItemData | Used when the item has been updated by reference, so in this case the DiffUtil wont see the change |
updateInnerListItem | Used to update an inner item in sub recyclerView list |
notifyItemRangeChangedForSurroundingItems | This method will [notifyItemRangeChanged] for (previous x items) to (next x items) using the current item as a pivot |
You need to include the below line in your proguard-rules.pro
-keepclassmembers public class * extends com.haizo.generaladapter.viewholders.BaseBindingViewHolder{ public protected *; }
-keepclassmembers public class * extends com.haizo.generaladapter.model.**{ public protected *; }
Copyright 2020 Farouq Afghani
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.