Recycler View Item Gone is not working properly - android

I'm trying to hide part of my view in recycler when user toggles switch.
But when i toggle switch, sometimes it behaves in a strange way and hides only part of my view. It happens every 4 switch clicks (show-hide-show-hide), and i can't manage to solve this bug. Any advices what am i doing wrong?
There are screenshots of properly displayed view and awkwardly hidden view. As you can see, at the second screenshot seekbar with 2 image views is not fully hidden.
UPDATED: The problem is solved. Problem was in include tag. It somehow works wrong, the same layout in item file works fine.
Here is code for hiding logic:
itemView.light_switch.setOnCheckedChangeListener { _, isChecked ->
itemView.brightness.visibility = if (isChecked) View.VISIBLE else View.GONE
lightCallback(lamp, isChecked)
}
And here is my layout (recycler item):
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:background="#1a1a1a">
<ImageView
android:id="#+id/light_icon_image_view"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/prod_foot" />
<TextView
android:id="#+id/light_name_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="8dp"
android:fontFamily="sans-serif-medium"
android:textColor="#ffffff"
android:textSize="16sp"
android:textStyle="normal"
app:layout_constraintBottom_toBottomOf="#+id/light_icon_image_view"
app:layout_constraintStart_toEndOf="#+id/light_icon_image_view"
app:layout_constraintTop_toTopOf="#+id/light_icon_image_view"
tools:text="Floodlight 24 368 (01)" />
<TextView
android:id="#+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:text="No group"
android:textColor="#7fffffff"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintStart_toStartOf="#+id/light_name_text_view"
app:layout_constraintTop_toBottomOf="#+id/light_name_text_view" />
<android.support.v7.widget.SwitchCompat
android:id="#+id/light_switch"
style="#style/SwitchCompatStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="#+id/light_icon_image_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/light_icon_image_view"
app:thumbTint="#android:color/white" />
<include
android:id="#+id/brightness"
layout="#layout/brightness_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/light_icon_image_view">
</include>
<ImageView
android:id="#+id/light_gradient_circle"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_marginTop="40dp"
app:layout_constraintStart_toStartOf="#+id/light_icon_image_view"
app:layout_constraintTop_toTopOf="#+id/light_icon_image_view"
app:srcCompat="#drawable/badge_color_wheel_active_block" />
</android.support.constraint.ConstraintLayout>
Include layout is provided below (seekbar with 2 images):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="#1a1a1a"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="#+id/seekbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/seekbar"
app:srcCompat="#drawable/ic_dim_min" />
<SeekBar
android:id="#+id/seekbar"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="11dp"
android:layout_marginStart="11dp"
android:layout_marginTop="8dp"
android:max="255"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/imageView2"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="#+id/seekbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/seekbar"
app:srcCompat="#drawable/ic_dim_max" />
</android.support.constraint.ConstraintLayout>
UPDATED: Adapter full code
class IndividualLightsAdapter(private val context: Context,
private val data: MutableList<Lamp>,
private val lightCallback: (Lamp, Boolean) -> Unit,
private val lightBrightnessCallback: (lamp: Lamp, brightness: Int) -> Unit)
: RecyclerView.Adapter<IndividualLightsAdapter.LightViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LightViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.light_item_new, parent, false)
return LightViewHolder(view)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: LightViewHolder, position: Int) {
holder.bind(position)
}
inner class LightViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(position: Int) {
val lamp = data[position]
itemView.light_icon_image_view.setImageResource(lamp.imageId)
itemView.light_name_text_view.text = lamp.customName
itemView.light_gradient_circle.visibility = if (lamp is SmartLamp) View.VISIBLE else View.INVISIBLE
itemView.light_switch.isChecked = lamp.isTurnedOn
itemView.light_switch.setOnCheckedChangeListener { _, isChecked ->
itemView.brightness.visibility = if (isChecked) View.VISIBLE else View.GONE
lightCallback(lamp, isChecked)
}
setSeekBar()
itemView.seekbar.progress = lamp.brightness
itemView.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
lightBrightnessCallback(lamp, progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
}
})
}
private fun setSeekBar() {
val gradient = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(ContextCompat.getColor(context, R.color.grad_start), (ContextCompat.getColor(context, R.color.grad_end))))
gradient.cornerRadius = context.resources.getDimension(R.dimen.corner_radius_outer)
val progressLayer = ClipDrawable(gradient, Gravity.START, ClipDrawable.HORIZONTAL)
val background = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(Color.BLACK, Color.BLACK))
background.cornerRadius = context.resources.getDimension(R.dimen.corner_radius_inner)
// TODO Calculate padding dynamically
val backgroundLayer = InsetDrawable(background,
context.resources.getDimension(R.dimen.padding_inset_left).toInt(),
context.resources.getDimension(R.dimen.padding_inset_top).toInt(),
context.resources.getDimension(R.dimen.padding_inset_right).toInt(),
context.resources.getDimension(R.dimen.padding_inset_bottom).toInt())
itemView.seekbar.thumb.setTint((ContextCompat.getColor(context, R.color.color_thumb)))
itemView.seekbar.progressDrawable = LayerDrawable(arrayOf<Drawable>(backgroundLayer, progressLayer))
}
}
}

You shouldn't hide the view that is an item of recyclerView.
It doesn't work properly because of its recycling mechanism.
You just need to remove/add the item to be able to do the same effect.
you should has the below methods on your RecyclerView adapter
private void deleteItem(int position) {
if (position != RecyclerView.NO_POSITION) {
myList.remove(position);
notifyItemRemoved(position);
}
}
private void addItem(int position, MyModel model) {
if (position != RecyclerView.NO_POSITION) {
myList.add(position, model);
notifyItemInserted(position);
}
}

Related

How to get currently selected item position in RecyclerView?

How can i get item position of item selected with checkBox in RecyclerView ? here you can see how it looks like
TaskRecyclerAdapter.kt
class TaskRecycleAdapter: RecyclerView.Adapter<TaskRecycleAdapter.MyViewHolder>() {
private var list = emptyList<Data>()
private lateinit var mListener : OnItemClickListener
class MyViewHolder(binding: TaskHolderBinding, listener: OnItemClickListener): RecyclerView.ViewHolder(binding.root) {
init {
itemView.setOnClickListener {
listener.onItemClick(adapterPosition)
}
}
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: OnItemClickListener) {
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(TaskHolderBinding.inflate(LayoutInflater.from(parent.context), parent, false), mListener)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = list[position]
holder.itemView.findViewById<TextView>(R.id.task).text = item.task
holder.itemView.findViewById<TextView>(R.id.taskDescription).text = item.task_Details
}
override fun getItemCount(): Int {
return list.size
}
fun setData(newList: List<Data>) {
val diffUtil = DiffUtil(list, newList )
val diffResults = calculateDiff(diffUtil)
list = newList
diffResults.dispatchUpdatesTo(this)
}
fun getTaskAt(position: Int): Data {
return list[position]
}
}
task_holder.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/taskHolder"
app:cardCornerRadius="15dp"
android:backgroundTint="#color/card"
app:cardElevation="3dp"
app:contentPadding="3dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
android:layout_marginEnd="3dp"
android:layout_marginStart="3dp" >
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="88dp">
<ImageView
android:id="#+id/imageView"
android:layout_width="80dp"
android:layout_height="80dp"
android:contentDescription="#string/task_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/task"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginStart="98dp"
android:layout_marginTop="4dp"
android:contentDescription="#string/task"
android:textAlignment="center"
android:textSize="30sp"
android:textStyle="bold|italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/taskDescription"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginEnd="100dp"
android:layout_marginBottom="4dp"
android:contentDescription="#string/task"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toBottomOf="#+id/task"
app:layout_constraintVertical_bias="1.0" />
<CheckBox
android:id="#+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.924"
app:layout_constraintStart_toEndOf="#+id/task"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.1" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I'm Java Developer so I cannot code with Kotlin but I'll show you how to get any view's position in RecyclerView
holder.itemView.setOnClickListener(view -> {
Toast.makeText(context, "This is position of your Every ItemViews", Toast.LENGTH_SHORT).show();
});
In your onBindViewHolder use this code holder.itemView.setOnClickListener and create new click listener. Here you can get the position of your items by coding and you can check by adding Toast.
your code lookalike below:-
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = list[position]
holder.itemView.findViewById<TextView>(R.id.task).text = item.task
holder.itemView.findViewById<TextView>(R.id.taskDescription).text = item.task_Details
holder.itemView.setOnClickListener(view -> {
Toast.makeText(context, "This is position of your Every ItemViews", Toast.LENGTH_SHORT).show();
});
}
Make sure to change JAVA to Kotlin of my code. If you any problem is causing, Let me know

How to cover up the loading time lag in android Studio?

I have made an app in Android Studio which imports data from a website...when I open the app the card views in which the data is to be imported are empty and get loaded after some time. I wanted to know if there is any way in which I can cover it up so that till the time it is being loaded my splash screen stays there. Here is a video showing the lag:
Card View loading time lag
Edit: Here is the movie Adapter:
class MoviesAdapter(
private var movies: MutableList<Movie>,
private val onMovieClick: (movie: Movie) -> Unit,
) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_movie, parent, false)
return MovieViewHolder(view)
}
override fun getItemCount(): Int = movies.size
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(movies[position])
}
fun appendMovies(movies: List<Movie>) {
this.movies.addAll(movies)
notifyItemRangeInserted(
this.movies.size,
movies.size - 1
)
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val poster: ImageView = itemView.findViewById(R.id.item_movie_poster)
fun bind(movie: Movie) {
itemView.setOnClickListener { onMovieClick.invoke(movie) }
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${movie.posterPath}")
.transform(CenterCrop())
.into(poster)
}
}
}
My main_activity_xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg_gradient"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="16dp"
android:text="#string/discover"
android:textColor="#android:color/white"
android:textSize="45sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="#string/popular"
android:textColor="#android:color/white"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="#string/most_popular_movies" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/popular_movies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="#string/top_rated"
android:textColor="#android:color/white"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="#string/highest_rated_movies" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/top_rated_movies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="#string/upcoming"
android:textColor="#android:color/white"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="#string/stay_updated" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/upcoming_movies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="#string/now_playing"
android:textColor="#android:color/white"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="#string/now_in_Cinemas" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/now_playing_movies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="10dp"
android:clipToPadding="false"
android:paddingStart="16dp"
android:paddingEnd="16dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Api Key:
interface Api {
#GET("movie/popular")
fun getPopularMovies(
#Query("api_key") apiKey: String =
"eff7d87eb14cdf1fb273414692d3e3a8",
#Query("page") page: Int,
): Call<GetMoviesResponse>
}
Api Loader:
api.getPopularMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>,
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t:
Throwable) {
onError.invoke()
}
})
Is there anything else that I should specify?
without code it's hard to answer the question, but you could always use a ViewSwitcher that displays a "Loading" state before the data for the cardviews are ready, but holds the space, so the UI won't jump, and then have the Card view displayed once the data is loaded.
Could be something like:
sealed class CardData {
object Loading: CardData()
class Info(val items: List<CardInfo>): CardData()
}
fun loadData() {
viewModel.cardData.observe { cardData ->
when(cardData) {
is Loading -> {
binding.switcher.displayedChild = 0
}
is Info -> {
binding.switcher.displayedChild = 1
binding.listOfCards.items = cardData.items
}
}
}
for your specific case:
// display the loading view before making the call
binding.switcher.displayedChild = 0
api.getPopularMovies(page = page)
.enqueue(object : Callback<GetMoviesResponse> {
override fun onResponse(
call: Call<GetMoviesResponse>,
response: Response<GetMoviesResponse>,
) {
if (response.isSuccessful) {
val responseBody = response.body()
if (responseBody != null) {
onSuccess.invoke(responseBody.movies)
} else {
onError.invoke()
}
} else {
onError.invoke()
}
}
override fun onFailure(call: Call<GetMoviesResponse>, t:
Throwable) {
onError.invoke()
}
})
// in your onSuccess()
private void onSuccess(List<Movies> movies) {
binding.switcher.displayedChild = 1
listAdapter.items = movies
listAdapter.notifyDatasetChanged()
}
// in your onError()
private void onError() {
binding.switcher.displayedChild = 2 // you can add a third child for errors
}
then the XML would be something like (missing attributes):
<ViewSwitcher
android:id="#+id/switcher">
<!-- child 0 - loading state -->
<ProgressBar
android:id="#+id/loader"
/>
<!-- child 1 - movies state -->
<RecyclerView
android:id="#+id/movies"
/>
<!-- child 2 - error state -->
<TextView
android:id="#id/error_text"
/>
</ViewSwitcher>
If you want to wait in the splash screen, you will need to make that request in the splash screen.
When you have a successful response, you then navigate and pass the data through args/intent extras, assuming this is a different fragment/activity.
Then, when you are setting creating the list adapter you just need to pass this list through the constructor and set wire everything up, like you probably already have.
Hope it helps, but let me know if this wasn't clear enough!
You can create a Splash screen view exactly like your splash screen in your main activity and show it first while loading all the data from the network in the background, once the data arrives, hide this splash screen view and show app's main screen.
You can instead use shimmer effect to show data loading in background.

How to show and hide a button inside recycleview with some debouncing using kotlin coroutines

The above is what I am trying to achieve .
My recycle view item has two buttons . Both are hidden by default . On click of the item view should show the buttons for 1 sec. After it must hide . if I tap on either of these buttons , the timer should be reset for another 1 sec . Something similar to a debounce in rx java.
Need to do it with Kotlin coroutines .
I was able to achieve the above using kotlin coroutines jobs .
My layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="150dp"
android:layout_height="250dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginHorizontal="5dp"
android:elevation="5dp"
android:layout_gravity="bottom"
>
<TextView
android:id="#+id/product_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="#id/guideline_horizontal_top"
android:text="x2"
android:fontFamily="#font/gotham"
android:textColor="#color/dark_blue"
android:textSize="20sp"/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline_horizontal_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".20"/>
<androidx.cardview.widget.CardView
android:id="#+id/part_card"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/guideline_horizontal_top"
app:layout_constraintBottom_toBottomOf="parent"
app:cardElevation="5dp"
android:clickable="true"
android:foreground="?attr/selectableItemBackgroundBorderless"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.google.android.material.button.MaterialButton
android:id="#+id/plus_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#color/transparent_dark_blue"
app:layout_constraintTop_toTopOf="parent"
android:insetTop="0dp"
app:cornerRadius="0dp"
android:text="ADD"
android:visibility="invisible"/>
<com.google.android.material.button.MaterialButton
android:id="#+id/minus_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#color/transparent_dark_blue"
app:layout_constraintBottom_toBottomOf="parent"
android:insetBottom="0dp"
app:cornerRadius="0dp"
android:text="REDUCE"
android:visibility="invisible"/>
<TextView
android:id="#+id/partition_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="PART 1"
android:layout_margin="5dp"
android:textColor="#color/dark_blue"
android:fontFamily="#font/gotham_thin_font"
android:textStyle="bold"
/>
<TextView
android:id="#+id/items_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Items left : "
android:layout_margin="5dp"
android:textColor="#color/dark_blue"
android:fontFamily="#font/gotham_thin_font"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center"/>
<TextView
android:id="#+id/product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/partition_text"
app:layout_constraintBottom_toTopOf="#id/items_left"
android:layout_margin="5dp"
android:textColor="#color/dark_blue"
android:fontFamily="#font/gotham_thin_font"
android:textStyle="bold"
android:gravity="start"
android:text="Product name here"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
My addapter
class PartitionRecycleAdapter : RecyclerView.Adapter<PartitionRecycleAdapter.ViewHolder>() {
private var partitions: List<Selection<Partition>> = listOf()
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
private var buttonManage: Job? = null
fun update(partitions: List<Selection<Partition>>) {
this.partitions = partitions
notifyDataSetChanged()
}
inner class ViewHolder(val binding: PartitionRecycleItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(partition: Selection<Partition>, position: Int) {
binding.apply {
partitionText.text = "Part ${partition.item.number}"
productName.text = partition.item.productName
val itemsLeftVar = partition.item.currentQty - partition.qty
itemsLeft.text = "Items left : $itemsLeftVar"
productCount.text = "X${partition.qty}"
if (partition.qty > 0) {
productCount.visibility = View.VISIBLE
} else {
productCount.visibility = View.INVISIBLE
}
Log.d(TAG, "bind: OUTER")
val listener = View.OnClickListener { view ->
buttonManage?.cancel()
buttonManage = coroutineScope.launch {
Log.d(TAG, "bind: Coroutine started")
delay(1000)
withContext(Dispatchers.Main) {
binding.plusButton.visibility = View.INVISIBLE
binding.minusButton.visibility = View.INVISIBLE
Log.d(TAG, "bind: Coroutine Ended")
}
}
if (view == partCard) {
binding.plusButton.visibility = View.VISIBLE
binding.minusButton.visibility = View.VISIBLE
}
if (view == plusButton) {
Log.d(TAG, "bind: PLUS")
if (itemsLeftVar > 0) {
partition.qty++
// notifyItemChanged(position)
}
}
if (view == minusButton) {
Log.d(TAG, "bind: MINUS")
if (partition.qty > 0) {
partition.qty--
// notifyItemChanged(position)
}
}
}
partCard.setOnClickListener(listener)
plusButton.setOnClickListener(listener)
minusButton.setOnClickListener(listener)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = PartitionRecycleItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val partition = partitions[position]
holder.bind(partition, position)
}
override fun getItemCount(): Int {
return partitions.size
}
}
The problem is , when I add notifyItemChanged on my button click . Everything breaks. the button closes sometimes . sometimes not .
please help..

How to use Gridlayoutmanager for expandable cards inside a recyclerView

I am trying to make a grid layout with expandable cards, but the problem is that when a card is expanded, its height gets bigger and so does the height of the other cards in the row (to match the height of the first card), but when the card is collapsed back, the height of all the cards does not change as if they were expanded. Anyone knows what could be the problem?
EDIT :
recyclerview_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/misty_rose"
android:layout_margin="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Media -->
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_gravity="center_horizontal"
android:padding="8dp"
android:contentDescription="Photo"
android:src="#drawable/unsplash"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#color/isabelline"
/>
<!-- Title, secondary and supporting text -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/misty_rose"
app:layout_constraintTop_toBottomOf="#+id/imageView"
android:padding="8dp">
<TextView
android:id="#+id/textViewCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Code"
android:textAppearance="?attr/textAppearanceHeadline6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/iconExpandCard"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="8dp"
android:src="#drawable/ic_baseline_expand_more_36"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/textViewDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Description"
android:textAppearance="?attr/textAppearanceBody1"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewCode" />
<TextView
android:id="#+id/textViewPrice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Price"
android:textAppearance="?attr/textAppearanceBody1"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewDescription"/>
<TextView
android:id="#+id/textViewComment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Comment"
android:textAppearance="?attr/textAppearanceBody1"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewPrice"
android:visibility="gone"/>
<!-- Buttons -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textViewComment">
<com.google.android.material.button.MaterialButton
android:id="#+id/buttonMinusArticle"
style="?attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:width="88dp"
android:minWidth="40dp"
android:backgroundTint="#color/purple_200"
android:text="#string/minus"
android:textColor="#color/white" />
<EditText
android:id="#+id/editNumberOfProducts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:inputType="numberDecimal|number"
android:text="#string/zero"
android:textAppearance="?attr/textAppearanceBody1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="18sp" />
<com.google.android.material.button.MaterialButton
android:id="#+id/buttonPlusArticle"
style="?attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="88dp"
android:minWidth="40dp"
android:backgroundTint="#color/purple_200"
android:text="#string/plus"
android:textColor="#color/white" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
And in ProductListAdapter.kt (the important part is in expandButton.setOnClickListener() ):
class ProductListAdapter() : ListAdapter<Product, ProductListAdapter.ProductViewHolder>(ProductsComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
return ProductViewHolder.create(parent)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current!!)
}
class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val productItemView: TextView = itemView.findViewById(R.id.textViewCode)
private val productDescription : TextView = itemView.findViewById(R.id.textViewDescription)
private val productPrice : TextView = itemView.findViewById(R.id.textViewPrice)
fun bind(product: Product) {
productItemView.text = product.code
//productDescription.text = product.description
//productPrice.text = "Price " + product.client_price.toString()
}
companion object {
val mapOfProducts :HashMap<String, Int> = hashMapOf<String, Int>()
fun create(parent: ViewGroup): ProductViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item, parent, false)
val minusButton : Button = view.findViewById(R.id.buttonMinusArticle)
val plusButton : Button = view.findViewById(R.id.buttonPlusArticle)
val productItemViewCode: TextView = view.findViewById(R.id.textViewCode)
val expandButton : androidx.appcompat.widget.AppCompatImageButton = view.findViewById(R.id.iconExpandCard)
val commentView : TextView = view.findViewById(R.id.textViewComment)
val cardView : CardView = view.findViewById(R.id.cardView)
expandButton.setOnClickListener{
if (commentView.visibility == View.GONE){
TransitionManager.beginDelayedTransition(cardView, AutoTransition())
commentView.visibility = View.VISIBLE
expandButton.setImageResource(R.drawable.ic_baseline_expand_less_36)
} else {
TransitionManager.beginDelayedTransition(cardView, AutoTransition())
commentView.visibility = View.GONE
expandButton.setImageResource(R.drawable.ic_baseline_expand_more_36)
}
}
val editNumberOfProducts: EditText = view.findViewById(R.id.editNumberOfProducts)
editNumberOfProducts.doAfterTextChanged {
val code = productItemViewCode.text.toString()
if (it.isNullOrBlank()) {
modifyText("0", view)
mapOfProducts.remove(code)
return#doAfterTextChanged
}
val originalText = it.toString()
try {
val number = originalText.toInt()
val numberText = originalText.toInt().toString()
if (originalText != numberText) {
modifyText(numberText, view)
}
if (number > 0) {
mapOfProducts[code] = number
d("CodeOfView", "$code $number")
}else {
mapOfProducts.remove(code)
}
} catch (e: Exception) {
modifyText("0", view)
mapOfProducts.remove(code)
}
}
minusButton.setOnClickListener {
var number = editNumberOfProducts.text.toString().toInt()
if (number>0) {
number -= 1
modifyText(number.toString(), view)
}
}
plusButton.setOnClickListener {
//val code = productItemViewCode.text.toString()
var number = editNumberOfProducts.text.toString().toInt()
number += 1
modifyText(number.toString(), view)
}
return ProductViewHolder(view)
}
private fun modifyText(numberText: String, view: View) {
val editNumberOfProducts = view.findViewById<EditText>(R.id.editNumberOfProducts)
editNumberOfProducts.setText(numberText)
editNumberOfProducts.setSelection(numberText.length)
}
}
}
class ProductsComparator : DiffUtil.ItemCallback<Product>() {
override fun areItemsTheSame(oldItem: Product, newItem: Product): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Product, newItem: Product): Boolean {
return oldItem.code == newItem.code
}
}
}
Example with images of the problem
I was facing a same issue, in my case it was vertical expandable cards and I managed to solve it by using
Adapter.notifyDataSetChanged()
in the right place.

How to show progress bar when my list reaches the bottom recyclerview?

I have created stmth like buggy paging library and when I reach the bottom of list I would like to show progress bar before my data will be downloaded. I think that notifyItemInserted() will help me bu I don't understand where I have to insert this item. I think that I have to use notifyItemChanged also for hiding all data fields and show progress bar at my list item. So, here is my own loading additional data when I reach the end of list:
jobLst.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) {
val visibleItemCount = mLayoutManager.childCount
val totalItemCount = mLayoutManager.itemCount
val pastVisibleItems = mLayoutManager.findFirstVisibleItemPosition()
if (loading) {
if (visibleItemCount + pastVisibleItems >= totalItemCount && !recyclerView.canScrollVertically(1)) {
// here I would like to show loader
getJobList(Integer.parseInt(Uri.parse(nextUrl).getQueryParameter("offset")), type, sp.getString("access_token", ""), filterData, true)
}
}
}
}
})
and I think that I will have to change my item layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="15dp"
android:background="#drawable/message_list_item_bg"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/tv_job_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3.5"
android:ellipsize="end"
android:fontFamily="#font/opensans_semibold"
android:singleLine="true"
android:textColor="#333333"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="8sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<TextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:fontFamily="#font/opensans_semibold"
android:gravity="end"
android:textSize="12sp" />
</LinearLayout>
<TextView
android:id="#+id/tv_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:fontFamily="#font/opensans_regular"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="#999999"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="8sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<TextView
android:id="#+id/tv_company"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="30dp"
android:layout_weight="3.5"
android:ellipsize="end"
android:fontFamily="#font/opensans_regular"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="#666666"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="8sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<ImageView
android:id="#+id/add_to_notepad"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/add_to_notepad"
android:gravity="end" />
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="24dp"
android:layout_height="24dp"
android:gravity="end"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
current progressbar is already used and can't be used for this purpose. So, I will need to add and then remove one item with another view type before and after loading some new data.My main layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
tools:context=".jobAGENT.JobsList">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="75dp"
android:gravity="center_horizontal"
android:text="#string/no_jobs"
android:textSize="32sp"
android:textStyle="italic"
android:visibility="gone" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/refresh_t"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="5dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:background="#color/white">
<android.support.v7.widget.RecyclerView
android:id="#+id/job_list_t"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
So, what you can advice me?
You can add a special loading ViewHolder type for your RecyclerView Adapter and keep the special item at the last one.
The loading ViewHolder item layout just include a ProgressBar.
This special item resolution has an advantage is that you can easily use the RecyclerView Animation when show or hide loading item.
|=== normal item ====|
|=== normal item ====|
|=== normal item ====|
|=== normal item ====|
|=== normal item ====|
--- insert data here ----
|=== loading item ===|
Here is the Adapter demo code, i didn't run the demo but it is enough to show the idea.
class ListAdapter(list: List<ItemModel>) : RecyclerView.Adapter<ListAdapter.BaseItemHolder>() {
companion object {
private const val VIEW_TYPE_LOADING = 1
private const val VIEW_TYPE_NORMAL = 2
}
private var mList = list
private var mShowLoading = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseItemHolder {
when(viewType) {
VIEW_TYPE_NORMAL -> return NormalViewHolder.create(parent)
VIEW_TYPE_LOADING -> return LoadingViewHolder.create(parent)
}
return BaseItemHolder(parent)
}
override fun getItemCount(): Int {
// return data list size + loading item size
return mList.size + if (mShowLoading) { 1 } else { 0 }
}
override fun onBindViewHolder(holder: BaseItemHolder, position: Int) {
if (holder is NormalViewHolder) {
holder.bind(mList[position])
} else if (holder is LoadingViewHolder) {
//do nothing
//LoadingViewHolder does not need bind data
}
}
override fun getItemViewType(position: Int): Int {
if (position < mList.size) {
return VIEW_TYPE_NORMAL
}
return VIEW_TYPE_LOADING
}
fun hideLoading() {
mShowLoading = false
//be careful with the index
notifyItemRemoved(itemCount)
}
fun showLoading() {
mShowLoading = true
//be careful with the index
notifyItemInserted(itemCount - 1)
}
open class BaseItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
class LoadingViewHolder(itemView: View) : BaseItemHolder(itemView) {
private var mText: TextView = itemView.findViewById(R.id.item_title)
companion object {
fun create(parent: ViewGroup): LoadingViewHolder {
//R.layout.layout_loading_item just contain a progress bar or something like that
return LoadingViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_loading_item, parent, false))
}
}
// LoadingViewHolder does not need bind data
//fun bind() {
//
//}
}
class NormalViewHolder(itemView: View) : BaseItemHolder(itemView) {
private var mText: TextView = itemView.findViewById(R.id.item_title)
companion object {
fun create(parent: ViewGroup): NormalViewHolder {
return NormalViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_list_item, parent, false))
}
}
fun bind(itemModel: ItemModel) {
//bind your data with ItemModel data
}
}
}
Use the below code snippet
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rcylv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/dimen_5dp"/>
<ProgressBar
android:id="#+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleX="3"
android:scaleY="3"
android:visibility="gone"/>
And inside activity
jobLst.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) {
val visibleItemCount = mLayoutManager.childCount
val totalItemCount = mLayoutManager.itemCount
val pastVisibleItems = mLayoutManager.findFirstVisibleItemPosition()
if (loading) {
if (visibleItemCount + pastVisibleItems >= totalItemCount && !recyclerView.canScrollVertically(1)) {
progressBar.visibility = View.VISIBLE -> Kotlin
or
progressBar.setVisibility(View.VISIBLE); -> Java
}
}
}
}
})
In your Adapter add this..
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(i==list.size()){
//show your progress bar
}
}
It seems like I have managed to solve my problem by the easiest way. At main layout where RecyclerView is placed you have to add your ProgressBar which will be below RV and for this purpose you have to use Relative layout. Then you can use scrollListener for recyclerview like this:
recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (!recyclerView.canScrollVertically(1)) {
loader.visibility = View.VISIBLE
}else{
loader.visibility = View.GONE
}
}
}
})
and when your response will be successful you will hide your progress bar. Maybe it will help someone else except me :)

Categories

Resources