Every little thing it’s essential to learn about RecyclerView | Summary Tech

about Every little thing it’s essential to learn about RecyclerView

will cowl the most recent and most present help virtually the world. learn slowly thus you comprehend skillfully and accurately. will deposit your information dexterously and reliably

Earlier than you begin, sureYou possibly can seek advice from 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 varieties (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 needed 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 cannot be utilizing something fancy on this article, however I will assume you already know the fundamentals of RecyclerView, View Holder, Stay Knowledge and learn how to implement it.

I’m going to leap some elements of the codeso if you wish to see the supply codeyou will discover 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> 
information class Loading(
val isPaginating: Boolean = false,
): NetworkResponse<Nothing>()

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

information 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 information 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 varieties, you will 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 is going to assist us with Insert, Delete and Replace “operations”, it is going to make issues simpler for us to deal with upcoming modifications.

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

enum class OperationEnum
Insert,
Delete,
Replace,

Lastly, RecyclerViewModel information class,

information 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)

Previously, I used to make use of notifyDataSetChanged and it was the best solution to replace the RecyclerView, however I seen that it created efficiency points and prompted a foul consumer expertise.

This occasion doesn’t specify what has modified within the information set, forcing any observer to imagine that each one present components and buildings are now not legitimate. LayoutManagers shall be pressured to totally rebind and relay all seen views.

Should you’re writing an adapter, it is at all times extra environment friendly to make use of the extra particular change occasions for those who can. Rely upon notifyDataSetChanged() as a final resort.

First, it refreshes the 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 the complete record sparkles and causes a foul consumer expertise.

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

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

class RecyclerViewDiffUtilCallBack(
non-public val oldList: Listing<RecyclerViewModel>,
non-public val newList: Listing<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 is named to verify if two components have the identical information.

areItemsTheSame is named to verify if two objects characterize the identical component.

Earlier than we start, we’ll create two adapters. first is generic BaseAdapter<T> that a lot of the implementation shall be right here and the second is RecyclerViewAdapter. We might use a single adapter however having BaseAdapter makes issues a lot simpler for future makes use of. When you’ve got multiple RecyclerView with comparable wants, as a substitute of repeating the identical code, we are able to create a base adapter and lengthen it.

@Suppress("UNCHECKED_CAST")
@SuppressLint("NotifyDataSetChanged")
summary class BaseAdapter<T>(open val interplay: Interplay<T>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
non-public 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)
//...

non-public enjoyable setState(rvEnum: RecyclerViewEnum)
//...

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

handleDiffUtil it will likely be carried out in every adapter with the corresponding fashions. We’ll maintain it as summary.

errorMessage, isLoading, isPaginating Y canPaginate the values ​​shall be used for the view varieties.

  • 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 record, we’ll show PaginationLoading view sort.
  • When canPaginate is fake and the place is the same as the scale of the record, we’ll show PaginationExhaust view sort.

getItemViewType returns the view sort of the component in place for the needs of seeing recycling. Think about using identification sources to uniquely determine component view varieties.

In getItemViewType we’re utilizing RecyclerViewEnum that we created earlier. We might simply go 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 features 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 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 record. If it isn’t pagination, we set the state and use notifyDataSetChanged. We’re minimizing using notifyDataSetChanged however on this case it’s essential.

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

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

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

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

newList[index] = operation.information

handleDiffUtil(newList as ArrayList<T>)

First, we copy the record with toMutableList to keep away from the reference step and take the mandatory measures in keeping with the operation. After that, we go a brand new record 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 perform shall be used to insert new information into the record. If we’re not paginating, we clear the record and add all new objects and eventually replace the record. If we’re paging, notifyItemRemoved to take away the paging view on the backside of the record and add new objects and notify DiffUtil.

non-public 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 perform merely units the values ​​in keeping with the state. Once more, this perform 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>,
non-public val extraInteraction: RecyclerViewInteraction,
): BaseAdapter<RecyclerViewModel>(interplay)

override enjoyable onCreateViewHolder(guardian: ViewGroup, viewType: Int): RecyclerView.ViewHolder
return when(viewType)
RecyclerViewEnum.View.worth -> ItemViewHolder(CellItemBinding.inflate(LayoutInflater.from(guardian.context), guardian, false), extraInteraction)
RecyclerViewEnum.Loading.worth -> LoadingViewHolder(CellLoadingBinding.inflate(LayoutInflater.from(guardian.context), guardian, false))
RecyclerViewEnum.PaginationLoading.worth -> PaginationLoadingViewHolder(CellPaginationLoadingBinding.inflate(LayoutInflater.from(guardian.context), guardian, false))
RecyclerViewEnum.PaginationExhaust.worth -> PaginationExhaustViewHolder(CellPaginationExhaustBinding.inflate(LayoutInflater.from(guardian.context), guardian, false))
RecyclerViewEnum.Error.worth -> ErrorItemViewHolder(CellErrorBinding.inflate(LayoutInflater.from(guardian.context), guardian, false))
else -> EmptyViewHolder(CellEmptyBinding.inflate(LayoutInflater.from(guardian.context), guardian, 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 rather straightforward to create an adapter. We simply must go 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 an extended article, so I will skip the implementations of view holders, apart from ItemViewHolder. You possibly can question different view holders from the supply code. When you’ve got any questions, be at liberty to ask.

class ItemViewHolder(
non-public val binding: CellItemBinding,
non-public 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 perform, 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 {

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

enjoyable fetchData(web page: Int): Circulate<NetworkResponse<ArrayList<RecyclerViewModel>>> = movement {
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): Circulate<NetworkResponse<Operation<RecyclerViewModel>>> = movement
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): Circulate<NetworkResponse<Operation<RecyclerViewModel>>> = movement
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): Circulate<NetworkResponse<Operation<RecyclerViewModel>>> = movement
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): Circulate<NetworkResponse<Operation<RecyclerViewModel>>> = movement
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 information. 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 information If the web page quantity is totally different from 1, it implies 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 features. The one distinction is that, in some instances, we use NetworkResponse with Operation. These features are used to imitate insert, replace, and delete.

class MainViewModel : ViewModel() {
non-public val repository = MainRepository()

non-public val _rvList = MutableLiveData<NetworkResponse<ArrayList<RecyclerViewModel>>>()
val rvList: LiveData<NetworkResponse<ArrayList<RecyclerViewModel>>> = _rvList

non-public val _rvOperation = MutableLiveData<NetworkResponse<Operation<RecyclerViewModel>>>()
val rvOperation: LiveData<NetworkResponse<Operation<RecyclerViewModel>>> = _rvOperation

non-public var web page: Int = 1

init
fetchData()

enjoyable refreshData()
web page = 1
fetchData()

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

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



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


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


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


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


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

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

}

View mannequin is barely right here to current the information to the consumer interface. We can have two LiveData, rvList Y rvOperation. rvList shall be used to hear for modifications to our record and rvOperation shall be used to hear for operations, eg a brand new component is inserted and we’ll hear for that operation and deal with it within the UI.

class MainFragment : BaseFragment<FragmentMainBinding>() {
non-public lateinit var viewModel: MainViewModel
non-public var recyclerViewAdapter: RecyclerViewAdapter? = null
non-public 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()

non-public enjoyable setDialog(context: Context)
loadingDialog = Dialog(context)
loadingDialog?.setCancelable(false)
loadingDialog?.setContentView(R.structure.dialog_loading)
loadingDialog?.window?.setBackgroundDrawable(ColorDrawable(Coloration.TRANSPARENT))

non-public 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()


non-public 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.information, 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.information)



non-public enjoyable setRecyclerView()
//... Later

}

we’ll implement setRecyclerView perform later,

setListeners The perform is to configure the press or replace listeners. A lot 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 perform 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 information.
  • In when(response)we confirm NetworkResponse write and name the wanted perform with recyclerViewAdapter.

identical logic in rvOperation.observe,

  • We verify the reply in when(response) and name the required perform.
    The one distinction is that we present both dismiss load dialog.
load dialog
non-public 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)
)
}
}

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

We additionally implement addScrollListener which shall be used to point out/conceal fab and activate pagination.

  • For fab.present, we verify if we’ve got scrolled sufficient and a sure variety of seen components. In that case, if we’ve got additionally shifted some quantity within the destructive route. fab.conceal 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’re not already paging.

We’ll separate the glow into two elements, the primary would be the regular design half and the second shall 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="@shade/black"
android:background="@shade/shimmer_color"
app:layout_constraintEnd_toStartOf="@+id/shimmerFavButton"
app:layout_constraintStart_toStartOf="guardian"
app:layout_constraintTop_toTopOf="guardian" />

<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="@shade/shimmer_color"
android:textColor="@shade/black"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="guardian"
app:layout_constraintEnd_toStartOf="@+id/shimmerFavButton"
app:layout_constraintStart_toStartOf="guardian"
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="@shade/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="@shade/shimmer_color"
app:layout_constraintBottom_toBottomOf="guardian"
app:layout_constraintEnd_toEndOf="guardian"
app:layout_constraintTop_toTopOf="guardian"/>
</androidx.constraintlayout.widget.ConstraintLayout>

simmerColor the code is #BFBDBD

We merely copy and paste the structure of the RecyclerView component and add background=”@shade/shimmer_color” every certainly 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 structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
<embrace structure="@structure/cell_shimmer"/>
</LinearLayout>
</com.fb.shimmer.ShimmerFrameLayout>

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

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

That is it! I hope you will have been useful. 👋👋

MrNtlu/RecyclerView-Information (github.com)

I want the article just about Every little thing it’s essential to learn about RecyclerView

provides keenness to you and is beneficial for totaling to your information

Everything you need to know about RecyclerView

Leave a Reply

x