very almost Every part You Have to Know About RecyclerView will lid the most recent and most present steering one thing just like the world. entrance slowly fittingly you comprehend competently and accurately. will addition your data skillfully and reliably

Earlier than you begin, sureYou possibly can discuss with the Youtube demo video to grasp what we’ll do on this article. We’ll implement RecyclerView with,

  • State administration with a number of view sorts (loading, error, pagination, and so forth.)
  • see binding
  • Pull to refresh
  • troublesome
  • Pagination
  • glitter loading animation
  • Scroll to the highest FAB
  • favourite button
  • error dealing with
  • popup menu
  • Delete/Replace/Insert merchandise

I wished to cowl all the things you would possibly want in your mission. We hope this text helps you perceive extra about RecyclerView. It’s open to enhancements and feedback. Please let me know when you have any.

Desk of Contents

We can’t be utilizing something fancy on this article, however I am going to assume you understand the fundamentals of RecyclerView, View Holder, Stay Information and the best way to implement it.

I’m going to leap some elements of the codeso if you wish to see the supply codeyou could find the hyperlink on the finish of this text.

utility stage construct.gradle proceedings,

android 
//...
buildFeatures
viewBinding true

dependencies
//...
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation "com.fb.shimmer:shimmer:0.5.0"

response wrapper,

sealed class NetworkResponse<out T> 
knowledge class Loading(
val isPaginating: Boolean = false,
): NetworkResponse<Nothing>()

knowledge class Success<out T>(
val knowledge: T,
val isPaginationData: Boolean = false,
): NetworkResponse<T>()

knowledge class Failure(
val errorMessage: String,
val isPaginationError: Boolean = false,
): NetworkResponse<Nothing>()

You possibly can be taught extra about it at this hyperlink, Dealing with success knowledge and error callback responses from a community for Android tasks utilizing Sandwich | by Jae Woong Eum | ProAndroidDev

This enum class to assist us with view sorts, you may perceive higher once we truly use it. The use could be very easy.

enum class RecyclerViewEnum(val worth: Int) 
Empty(0),
Loading(1),
Error(2),
View(3),
PaginationLoading(4),
PaginationExhaust(5),

Operation it can assist us with Insert, Delete and Replace “operations”, it can make issues simpler for us to deal with upcoming modifications.

knowledge class Operation<out T>(
val knowledge: T,
val operationEnum: OperationEnum
)

enum class OperationEnum
Insert,
Delete,
Replace,

Lastly, RecyclerViewModel knowledge class,

knowledge class RecyclerViewModel(
var id: String,
var content material: String = "",
var isLiked: Boolean = false,
)
val textual content: String
get() = "ID: $id"

override enjoyable equals(different: Any?): Boolean
if (this === different)
return true
if (different !is RecyclerViewModel)
return false
return different.id == id

override enjoyable hashCode() = Objects.hash(id)

Prior to now, I used to make use of notifyDataSetChanged and it was the best option to replace the RecyclerView, however I observed that it created efficiency points and brought on a nasty consumer expertise.

This occasion doesn’t specify what has modified within the knowledge set, forcing any observer to imagine that each one current components and constructions are not legitimate. LayoutManagers will probably be pressured to completely rebind and relay all seen views.

If you happen to’re writing an adapter, it is at all times extra environment friendly to make use of the extra particular change occasions if you happen to can. Rely on notifyDataSetChanged() as a final resort.

First, it refreshes your complete RecyclerView and causes efficiency points, and ought to be a final resort, because the Android documentation factors out.

Second, it has no animation and your complete listing glints and causes a nasty consumer expertise.

DiffUtil is a utility class that calculates the distinction between two lists and generates an inventory replace operation that converts the primary listing to the second.

DiffUtil involves the rescue. Calculate the modifications within the listing and replace solely the mandatory components.

class RecyclerViewDiffUtilCallBack(
personal val oldList: Record<RecyclerViewModel>,
personal val newList: Record<RecyclerViewModel>,
): DiffUtil.Callback()
override enjoyable getOldListSize() = oldList.dimension

override enjoyable getNewListSize() = newList.dimension

override enjoyable areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return oldList[oldItemPosition].id == newList[newItemPosition].id

override enjoyable areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return when
oldList[oldItemPosition].id != newList[newItemPosition].id -> false
oldList[oldItemPosition].content material != newList[newItemPosition].content material -> false
oldList[oldItemPosition].isLiked != newList[newItemPosition].isLiked -> false
else -> true


areContentsTheSame known as to verify if two components have the identical knowledge.

areItemsTheSame known as to verify if two objects signify the identical factor.

Earlier than we start, we’ll create two adapters. first is generic BaseAdapter<T> that a lot of the implementation will probably be right here and the second is RecyclerViewAdapter. We may use a single adapter however having BaseAdapter makes issues a lot simpler for future makes use of. You probably have multiple RecyclerView with comparable wants, as an alternative of repeating the identical code, we are able to create a base adapter and prolong it.

@Suppress("UNCHECKED_CAST")
@SuppressLint("NotifyDataSetChanged")
summary class BaseAdapter<T>(open val interplay: Interplay<T>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
personal var errorMessage: String? = null
var isLoading = true
var isPaginating = false
var canPaginate = true

protected var arrayList: ArrayList<T> = arrayListOf()

protected summary enjoyable handleDiffUtil(newList: ArrayList<T>)

override enjoyable onBindViewHolder(holder: RecyclerView.ViewHolder, place: Int)
when(getItemViewType(place))
RecyclerViewEnum.View.worth ->
(holder as ItemViewHolderBind<T>).bind(arrayList[position], place, interplay)

RecyclerViewEnum.Error.worth ->
(holder as ErrorViewHolderBind<T>).bind(errorMessage, interplay)

RecyclerViewEnum.PaginationExhaust.worth ->
(holder as PaginationExhaustViewHolderBind<T>).bind(interplay)


override enjoyable getItemViewType(place: Int) : Int
return if (isLoading)
RecyclerViewEnum.Loading.worth
else if (errorMessage != null)
RecyclerViewEnum.Error.worth
else if (isPaginating && place == arrayList.dimension)
RecyclerViewEnum.PaginationLoading.worth
else if (!canPaginate && place == arrayList.dimension)
RecyclerViewEnum.PaginationExhaust.worth
else if (arrayList.isEmpty())
RecyclerViewEnum.Empty.worth
else
RecyclerViewEnum.View.worth

override enjoyable getItemCount(): Int

enjoyable setError(errorMessage: String, isPaginationError: Boolean)
//...

enjoyable setLoadingView(isPaginating: Boolean)
//...

enjoyable handleOperation(operation: Operation<T>)
//...

enjoyable setData(newList: ArrayList<T>, isPaginationData: Boolean = false)
//...

personal enjoyable setState(rvEnum: RecyclerViewEnum)
//...

We’ll implement the capabilities mentioned later. Let’s verify one after the other.

handleDiffUtil it will likely be applied in every adapter with the corresponding fashions. We’ll hold it as summary.

errorMessage, isLoading, isPaginating Y canPaginate the values ​​will probably be used for the view sorts.

  • When errorMessage is just not null, we’ll present Error view sort.
  • When isLoading it is true, let’s present Loading view sort.
  • When isPaginating is true and the place is the same as the scale of the listing, we’ll show PaginationLoading view sort.
  • When canPaginate is fake and the place is the same as the scale of the listing, we’ll show PaginationExhaust view sort.

getItemViewType returns the view sort of the factor in place for the needs of seeing recycling. Think about using identification sources to uniquely establish factor view sorts.

In getItemViewType we’re utilizing RecyclerViewEnum that we created earlier. We may simply cross numbers like 0, 1, 2, and so forth. however to make issues simpler to learn we’re utilizing the enum class.

Let’s begin implementing the capabilities mentioned.

enjoyable setErrorView(errorMessage: String, isPaginationError: Boolean) 
if (isPaginationError)
setState(RecyclerViewEnum.PaginationExhaust)
notifyItemInserted(itemCount)
else
setState(RecyclerViewEnum.Error)
this.errorMessage = errorMessage
notifyDataSetChanged()

enjoyable setLoadingView(isPaginating: Boolean)
if (isPaginating)
setState(RecyclerViewEnum.PaginationLoading)
notifyItemInserted(itemCount)
else
setState(RecyclerViewEnum.Loading)
notifyDataSetChanged()

Each of them setErrorView Y setLoadingView they’ve comparable implementations for various instances. Whether it is pagination, we name notifyItemInserted and add the corresponding view to the top of the listing. If it is not pagination, we set the state and use notifyDataSetChanged. We’re minimizing using notifyDataSetChanged however on this case it’s crucial.

enjoyable handleOperation(operation: Operation<T>) 
val newList = arrayList.toMutableList()

when(operation.operationEnum)
OperationEnum.Insert ->
newList.add(operation.knowledge)

OperationEnum.Delete ->
newList.take away(operation.knowledge)

OperationEnum.Replace ->
val index = newList.indexOfFirst
it == operation.knowledge

newList[index] = operation.knowledge

handleDiffUtil(newList as ArrayList<T>)

First, we copy the listing with toMutableList to keep away from the reference step and take the mandatory measures based on the operation. After that, we cross a brand new listing to handleDiffUtil and DiffUtil does its “magic”.

enjoyable setData(newList: ArrayList<T>, isPaginationData: Boolean = false) 
setState(RecyclerViewEnum.View)

if (!isPaginationData)
if (arrayList.isNotEmpty())
arrayList.clear()
arrayList.addAll(newList)
notifyDataSetChanged()
else
notifyItemRemoved(itemCount)

newList.addAll(0, arrayList)
handleDiffUtil(newList)

This operate will probably be used to insert new knowledge into the listing. If we aren’t paginating, we clear the listing and add all new gadgets and at last replace the listing. If we’re paging, notifyItemRemoved to take away the paging view on the backside of the listing and add new gadgets and notify DiffUtil.

personal enjoyable setState(rvEnum: RecyclerViewEnum) 
when(rvEnum)
RecyclerViewEnum.Empty ->
isLoading = false
isPaginating = false
errorMessage = null

RecyclerViewEnum.Loading ->
isLoading = true
isPaginating = false
errorMessage = null
canPaginate = true

RecyclerViewEnum.Error ->
isLoading = false
isPaginating = false

RecyclerViewEnum.View ->
isLoading = false
isPaginating = false
errorMessage = null

RecyclerViewEnum.PaginationLoading ->
isLoading = false
isPaginating = true
errorMessage = null

RecyclerViewEnum.PaginationExhaust ->
isLoading = false
isPaginating = false
canPaginate = false


Lastly, this operate merely units the values ​​based on the state. Once more, this operate is to make it simpler to grasp.

That is all for BaseAdapterwe’re going to implement RecyclerViewAdapter.

@Suppress("UNCHECKED_CAST")
class RecyclerViewAdapter(
override val interplay: Interplay<RecyclerViewModel>,
personal val extraInteraction: RecyclerViewInteraction,
): BaseAdapter<RecyclerViewModel>(interplay)

override enjoyable onCreateViewHolder(dad or mum: ViewGroup, viewType: Int): RecyclerView.ViewHolder
return when(viewType)
RecyclerViewEnum.View.worth -> ItemViewHolder(CellItemBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false), extraInteraction)
RecyclerViewEnum.Loading.worth -> LoadingViewHolder(CellLoadingBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false))
RecyclerViewEnum.PaginationLoading.worth -> PaginationLoadingViewHolder(CellPaginationLoadingBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false))
RecyclerViewEnum.PaginationExhaust.worth -> PaginationExhaustViewHolder(CellPaginationExhaustBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false))
RecyclerViewEnum.Error.worth -> ErrorItemViewHolder(CellErrorBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false))
else -> EmptyViewHolder(CellEmptyBinding.inflate(LayoutInflater.from(dad or mum.context), dad or mum, false))

override enjoyable handleDiffUtil(newList: ArrayList<RecyclerViewModel>)
val diffUtil = RecyclerViewDiffUtilCallBack(
arrayList,
newList,
)
val diffResults = DiffUtil.calculateDiff(diffUtil, true)

arrayList = newList.toList() as ArrayList<RecyclerViewModel>

diffResults.dispatchUpdatesTo(this)

Since we’ve got completed all of the implementations in BaseAdapter, it is vitally straightforward to create an adapter. We simply must cross within the view holders and implement handleDiffUtil.

little notes, interplay Y extraInteraction They’re interfaces for dealing with actions. You possibly can verify them from the supply code.

See typographic designs

It is already a protracted article, so I am going to skip the implementations of view holders, aside from ItemViewHolder. You possibly can question different view holders from the supply code. You probably have any questions, be happy to ask.

class ItemViewHolder(
personal val binding: CellItemBinding,
personal val extraInteraction: RecyclerViewInteraction,
): RecyclerView.ViewHolder(binding.root), ItemViewHolderBind<RecyclerViewModel> {
override enjoyable bind(merchandise: RecyclerViewModel, place: Int, interplay: Interplay<RecyclerViewModel>) {

val textual content = "Place: $place $merchandise.textual content"
binding.contentTV.textual content = merchandise.content material.ifBlank textual content
binding.idTV.textual content = merchandise.id
binding.favButton.setImageDrawable(ContextCompat.getDrawable(binding.root.context, if (merchandise.isLiked) R.drawable.ic_heart else R.drawable.ic_empty_heart))

binding.moreButton.setOnClickListener {
val popupMenu = PopupMenu(binding.root.context, binding.moreButton)
popupMenu.inflate(R.menu.popup_menu)

popupMenu.setOnMenuItemClickListener
when(it.itemId)
R.id.delete ->
strive
extraInteraction.onDeletePressed(merchandise)
catch (e: Exception)
Toast.makeText(
binding.root.context,
"Please wait earlier than doing any operation.",
Toast.LENGTH_SHORT
).present()

[email protected] true

R.id.replace ->
strive
extraInteraction.onUpdatePressed(merchandise)
catch (e: Exception)
Toast.makeText(
binding.root.context,
"Please wait earlier than doing any operation.",
Toast.LENGTH_SHORT
).present()

[email protected] true

else ->
[email protected] false


popupMenu.present()
}

binding.favButton.setOnClickListener
extraInteraction.onLikePressed(merchandise)

binding.root.setOnClickListener
strive
interplay.onItemSelected(merchandise)
catch (e: Exception)
Toast.makeText(
binding.root.context,
"Please wait earlier than doing any operation.",
Toast.LENGTH_SHORT
).present()


}
}

In bind operate, we arrange the UI components and click on occasions. As soon as once more, there may be nothing “magic” about this.

const val PAGE_SIZE = 50

class MainRepository {

personal val tempList = arrayListOf<RecyclerViewModel>().apply
for (i in 0..PAGE_SIZE)
add(RecyclerViewModel(UUID.randomUUID().toString(), "Content material $i"),)

enjoyable fetchData(web page: Int): Movement<NetworkResponse<ArrayList<RecyclerViewModel>>> = circulate {
emit(NetworkResponse.Loading(web page != 1))

kotlinx.coroutines.delay(2000L)

strive {
if (web page == 1)
emit(NetworkResponse.Success(tempList.toList() as ArrayList<RecyclerViewModel>))
else
val tempPaginationList = arrayListOf<RecyclerViewModel>().apply
for (i in 0..PAGE_SIZE)
add(RecyclerViewModel(UUID.randomUUID().toString(), "Content material $i * 2"),)

if (web page < 4)
emit(NetworkResponse.Success(
tempPaginationList,
isPaginationData = true,
))
else
emit(NetworkResponse.Failure(
"Pagination failed.",
isPaginationError = true
))


} catch (e: Exception)
emit(NetworkResponse.Failure(
e.message ?: e.toString(),
isPaginationError = web page != 1
))

}.flowOn(Dispatchers.IO)

enjoyable deleteData(merchandise: RecyclerViewModel): Movement<NetworkResponse<Operation<RecyclerViewModel>>> = circulate
kotlinx.coroutines.delay(1000L)

strive
emit(NetworkResponse.Success(Operation(merchandise, OperationEnum.Delete)))
catch (e: Exception)
emit(NetworkResponse.Failure(e.message ?: e.toString()))

.flowOn(Dispatchers.IO)

enjoyable updateData(merchandise: RecyclerViewModel): Movement<NetworkResponse<Operation<RecyclerViewModel>>> = circulate
kotlinx.coroutines.delay(1000L)

strive
merchandise.content material = "Up to date Content material $(0..10).random()"
emit(NetworkResponse.Success(Operation(merchandise, OperationEnum.Replace)))
catch (e: Exception)
emit(NetworkResponse.Failure(e.message ?: e.toString()))

.flowOn(Dispatchers.IO)

enjoyable toggleLikeData(merchandise: RecyclerViewModel): Movement<NetworkResponse<Operation<RecyclerViewModel>>> = circulate
kotlinx.coroutines.delay(1000L)

strive
merchandise.isLiked = !merchandise.isLiked
emit(NetworkResponse.Success(Operation(merchandise, OperationEnum.Replace)))
catch (e: Exception)
emit(NetworkResponse.Failure(e.message ?: e.toString()))

.flowOn(Dispatchers.IO)

enjoyable insertData(merchandise: RecyclerViewModel): Movement<NetworkResponse<Operation<RecyclerViewModel>>> = circulate
emit(NetworkResponse.Loading())

kotlinx.coroutines.delay(1000L)

strive
emit(NetworkResponse.Success(Operation(merchandise, operationEnum = OperationEnum.Insert)))
catch (e: Exception)
emit(NetworkResponse.Failure(e.message ?: e.toString()))

.flowOn(Dispatchers.IO)
}

We’ll attempt to faux we’re making a community request, ready for it to complete and current the info. We’ll use flows to current NetworkResponse.

for instance in fetchData first we ship the state of cost with NetworkResponse.Loading and wait 2 seconds. After ready, if the web page quantity is 1, which suggests we’re updating or it is an preliminary fetch, we ship NetworkResponse.Success with knowledge If the web page quantity is completely different from 1, it signifies that we’re paging and sending NetworkResponse.Success with isPaginationData = true.

Since we mimic the community request, if the web page quantity is 4, we exhaust paging and ship NetworkResponse.Failure with isPaginationError = true to show the pagination escape view.

We even have comparable logic for different capabilities. The one distinction is that, in some instances, we use NetworkResponse with Operation. These capabilities are used to imitate insert, replace, and delete.

class MainViewModel : ViewModel() {
personal val repository = MainRepository()

personal val _rvList = MutableLiveData<NetworkResponse<ArrayList<RecyclerViewModel>>>()
val rvList: LiveData<NetworkResponse<ArrayList<RecyclerViewModel>>> = _rvList

personal val _rvOperation = MutableLiveData<NetworkResponse<Operation<RecyclerViewModel>>>()
val rvOperation: LiveData<NetworkResponse<Operation<RecyclerViewModel>>> = _rvOperation

personal var web page: Int = 1

init
fetchData()

enjoyable refreshData()
web page = 1
fetchData()

enjoyable fetchData() = viewModelScope.launch(Dispatchers.IO)
repository.fetchData(web page).accumulate state ->
withContext(Dispatchers.Essential)
_rvList.worth = state

if (state is NetworkResponse.Success)
web page += 1



enjoyable deleteData(merchandise: RecyclerViewModel) = viewModelScope.launch(Dispatchers.IO)
repository.deleteData(merchandise).accumulate state ->
withContext(Dispatchers.Essential)
_rvOperation.worth = state


enjoyable updateData(merchandise: RecyclerViewModel) = viewModelScope.launch(Dispatchers.IO)
repository.updateData(merchandise).accumulate state ->
withContext(Dispatchers.Essential)
_rvOperation.worth = state


enjoyable toggleLikeData(merchandise: RecyclerViewModel) = viewModelScope.launch(Dispatchers.IO)
repository.toggleLikeData(merchandise).accumulate state ->
withContext(Dispatchers.Essential)
_rvOperation.worth = state


enjoyable insertData(merchandise: RecyclerViewModel) = viewModelScope.launch(Dispatchers.IO)
repository.insertData(merchandise).accumulate state ->
withContext(Dispatchers.Essential)
_rvOperation.worth = state


enjoyable throwError() = viewModelScope.launch(Dispatchers.Essential)
_rvList.worth = NetworkResponse.Failure("Error occured!")

enjoyable exhaustPagination() = viewModelScope.launch(Dispatchers.Essential)
_rvList.worth = NetworkResponse.Failure(
"Pagination Exhaust",
true
)

}

View mannequin is simply right here to current the info to the consumer interface. We could have two LiveData, rvList Y rvOperation. rvList will probably be used to pay attention for modifications to our listing and rvOperation will probably be used to pay attention for operations, eg a brand new factor is inserted and we’ll pay attention for that operation and deal with it within the UI.

class MainFragment : BaseFragment<FragmentMainBinding>() {
personal lateinit var viewModel: MainViewModel
personal var recyclerViewAdapter: RecyclerViewAdapter? = null
personal var loadingDialog: Dialog? = null

//OnCreate and OnCreateView commented.

override enjoyable onViewCreated(view: View, savedInstanceState: Bundle?)
tremendous.onViewCreated(view, savedInstanceState)

setDialog(view.context)
setListeners()
setRecyclerView()
setObservers()

personal enjoyable setDialog(context: Context)
loadingDialog = Dialog(context)
loadingDialog?.setCancelable(false)
loadingDialog?.setContentView(R.format.dialog_loading)
loadingDialog?.window?.setBackgroundDrawable(ColorDrawable(Shade.TRANSPARENT))

personal enjoyable setListeners()
binding.swipeRefreshLayout.setOnRefreshListener
viewModel.refreshData()

binding.swipeRefreshLayout.isRefreshing = false

binding.errorButton.setOnClickListener
viewModel.throwError()

binding.appendButton.setOnClickListener
if (recyclerViewAdapter?.canPaginate == true && recyclerViewAdapter?.isPaginating == false)
viewModel.fetchData()

binding.mainRV.scrollToPosition(recyclerViewAdapter?.itemCount ?: 0)

binding.insertButton.setOnClickListener
viewModel.insertData(RecyclerViewModel(UUID.randomUUID().toString()))

binding.paginateErrorButton.setOnClickListener
viewModel.exhaustPagination()

binding.fab.setOnClickListener
viewLifecycleOwner.lifecycleScope.launch
binding.mainRV.quickScrollToTop()


personal enjoyable setObservers()
viewModel.rvList.observe(viewLifecycleOwner) response ->
binding.swipeRefreshLayout.isEnabled = when (response)
is NetworkResponse.Success ->
true

is NetworkResponse.Failure ->
response.isPaginationError

else -> false

when(response)
is NetworkResponse.Failure ->
recyclerViewAdapter?.setErrorView(response.errorMessage, response.isPaginationError)

is NetworkResponse.Loading ->
recyclerViewAdapter?.setLoadingView(response.isPaginating)

is NetworkResponse.Success ->
recyclerViewAdapter?.setData(response.knowledge, response.isPaginationData)


viewModel.rvOperation.observe(viewLifecycleOwner) response ->
when(response)
is NetworkResponse.Failure ->
if (loadingDialog?.isShowing == true)
loadingDialog?.dismiss()

is NetworkResponse.Loading ->
if (recyclerViewAdapter?.isLoading == false)
loadingDialog?.present()

is NetworkResponse.Success ->
if (loadingDialog?.isShowing == true)
loadingDialog?.dismiss()
recyclerViewAdapter?.handleOperation(response.knowledge)



personal enjoyable setRecyclerView()
//... Later

}

we’ll implement setRecyclerView operate later,

setListeners The operate is to configure the clicking or replace listeners. Many of the buttons are for testing functions and will not be actually wanted. binding.fab is to maneuver to the highest button. quickScrollToTop is customized operate by patrick elmquist. You possibly can verify his article at this hyperlink.

In rvList.observe,

  • We set the swipeRefreshLayout.isEnabled since we do not need the consumer to replace once more once we are already loading the info.
  • In when(response)we confirm NetworkResponse write and name the wanted operate with recyclerViewAdapter.

identical logic in rvOperation.observe,

  • We verify the reply in when(response) and name the required operate.
    The one distinction is that we present both dismiss load dialog.
load dialog
personal enjoyable setRecyclerView() {
binding.mainRV.apply {
val linearLayoutManager = LinearLayoutManager(context)
layoutManager = linearLayoutManager
addItemDecoration(DividerItemDecoration(context, linearLayoutManager.orientation))
recyclerViewAdapter = RecyclerViewAdapter(object: Interplay<RecyclerViewModel>
override enjoyable onItemSelected(merchandise: RecyclerViewModel)
Toast.makeText(context, "Merchandise $merchandise.content material", Toast.LENGTH_SHORT).present()

override enjoyable onErrorRefreshPressed()
viewModel.refreshData()

override enjoyable onExhaustButtonPressed()
viewLifecycleOwner.lifecycleScope.launch
quickScrollToTop()


, object: RecyclerViewInteraction
override enjoyable onUpdatePressed(merchandise: RecyclerViewModel)
viewModel.updateData(merchandise.copy())

override enjoyable onDeletePressed(merchandise: RecyclerViewModel)
viewModel.deleteData(merchandise)

override enjoyable onLikePressed(merchandise: RecyclerViewModel)
viewModel.toggleLikeData(merchandise.copy())

)
adapter = recyclerViewAdapter

var isScrolling = false
addOnScrollListener(object: RecyclerView.OnScrollListener()
override enjoyable onScrollStateChanged(recyclerView: RecyclerView, newState: Int)
tremendous.onScrollStateChanged(recyclerView, newState)
isScrolling = newState != AbsListView.OnScrollListener.SCROLL_STATE_IDLE

override enjoyable onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int)
tremendous.onScrolled(recyclerView, dx, dy)
val itemCount = linearLayoutManager.itemCount
val lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition()

if (lastVisibleItemPosition > PAGE_SIZE.plus(PAGE_SIZE.div(2)) && dy <= -75)
binding.fab.present()
else if (lastVisibleItemPosition <= PAGE_SIZE.plus(PAGE_SIZE.div(2))
)
}
}

Lastly, setRecyclerView operate. We create and configure recyclerViewAdapter a binding.mainRV.

We additionally implement addScrollListener which will probably be used to indicate/disguise fab and activate pagination.

  • For fab.present, we verify if we’ve got scrolled sufficient and a sure variety of seen components. If that’s the case, if we’ve got additionally shifted some quantity within the unfavourable path. fab.disguise it is the reverse of that. You possibly can take a look at it your self and set numbers your self.
  • For pagination, we verify if we’re scrolling and if we’re at a sure quantity earlier than the final seen merchandise and if canPaginate & if we aren’t already paging.

We’ll separate the glow into two elements, the primary would be the regular design half and the second will probably be ShimmerFrameLayout,

<?xml model="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="6dp">

<TextView
android:id="@+id/shimmerIdTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
android:textColor="@coloration/black"
android:background="@coloration/shimmer_color"
app:layout_constraintEnd_toStartOf="@+id/shimmerFavButton"
app:layout_constraintStart_toStartOf="dad or mum"
app:layout_constraintTop_toTopOf="dad or mum" />

<TextView
android:id="@+id/shimmerContentTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="8dp"
android:background="@coloration/shimmer_color"
android:textColor="@coloration/black"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="dad or mum"
app:layout_constraintEnd_toStartOf="@+id/shimmerFavButton"
app:layout_constraintStart_toStartOf="dad or mum"
app:layout_constraintTop_toBottomOf="@+id/shimmerIdTV" />

<ImageView
android:id="@+id/shimmerFavButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="8dp"
android:background="@coloration/shimmer_color"
app:layout_constraintBottom_toBottomOf="@+id/shimmerMoreButton"
app:layout_constraintEnd_toStartOf="@+id/shimmerMoreButton"
app:layout_constraintTop_toTopOf="@+id/shimmerMoreButton"
app:layout_constraintVertical_bias="0.407" />

<ImageView
android:id="@+id/shimmerMoreButton"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="8dp"
android:background="@coloration/shimmer_color"
app:layout_constraintBottom_toBottomOf="dad or mum"
app:layout_constraintEnd_toEndOf="dad or mum"
app:layout_constraintTop_toTopOf="dad or mum"/>
</androidx.constraintlayout.widget.ConstraintLayout>

simmerColor the code is #BFBDBD

We merely copy and paste the format of the RecyclerView factor and add background=”@coloration/shimmer_color” every considered one of them.

<?xml model="1.0" encoding="utf-8"?>
<com.fb.shimmer.ShimmerFrameLayout
android:id="@+id/shimmerLoadingLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:shimmer_auto_start="true"
app:shimmer_duration="1300">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
<embrace format="@format/cell_shimmer"/>
</LinearLayout>
</com.fb.shimmer.ShimmerFrameLayout>

Inside ShimmerFrameLayoutwe set auto_start="true" to start out the animation routinely. length is the time it takes to complete the animation. You possibly can see extra about it right here.

You possibly can lower or improve the variety of designs included. I’ve tried so as to add as little as doable to cowl your complete display screen. Much less is best for efficiency I feel 🙂

That is it! I hope you’ve been useful. 👋👋

MrNtlu/RecyclerView-Information (github.com)

I hope the article roughly Every part You Have to Know About RecyclerView provides perception to you and is beneficial for including as much as your data

Everything You Need to Know About RecyclerView

By admin

x