0

I have Recycler View (inside SwipeRefreshLayout) with vertical scrollbar with custom thumb color:

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@+id/messages_swipe_refresh"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@id/type_message_edit_text"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@id/rosters_end_guideline"
    app:layout_constraintTop_toTopOf="parent">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/messages_recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scrollbars="vertical"
                android:scrollbarThumbVertical="@color/orange"
                android:fadeScrollbars="false"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:stackFromEnd="true" />

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

In Recycler View I display text messages. I need to display how much time ago message was sent/received, so I store timestamps in recycler's dto:

data class MessageRecyclerDto(
    val sendTs: Long, 
    ...
)

I subtract timestamp from current time in onBindViewHolder method to calculate how much time has passed and refresh recycler data each minute, to display updated text:


override fun onBindViewHolder(holder: ViewHolder, position: Int) { 
    holder.bind(getItem(position)) 
}

class ViewHolder(
    private val binding: RecyclerItemChatMessageBinding
) : RecyclerView.ViewHolder(binding.root) { 
    fun bind(dto: MessageRecyclerDto) { 
        val minutesAgoText = (System.currentTimeMillis() - sendTs).millisToMinutes().toString()
        binding.timeAgoTv.text = minutesAgoText binding.executePendingBindings() 
    }
}

The problem is each time I call notifyItemRangeChanged or notifyDataSetChanged method of the adapter, scrollbars in recycler view appear for a moment and then fade after a while. That's the default behaviour on scroll action. What's more, I have another recycler view in the same Fragment, and scrollbars also appear in it. Recycler Views are not related, they have different adapters.

I don't want to hide scrollbars completely or show them all the time. Is there any way to disable this behaviour? Or maybe someone has some other solution to display elapsed time in Recycler View and refresh it periodically.

BochenChleba
  • 238
  • 1
  • 7
  • 1
    If it's making the scrollbars appear in another RecyclerView too, maybe there's a layout pass happening when you update? So everything has to recalculate its size, and because your RV's height is `match_parent` it needs to *"resize"* because its parent has also updated. Are they inside a `wrap_content` container at any level, that would have to resize if the contents of the RV changed? I'd look at those relationships - if a container's height depends on the size of the RV, and the RV's height depends on the size of items (e.g. the item layout is `wrap_content`) you could get a chain reaction – cactustictacs Mar 03 '23 at 00:58
  • @cactustictacs One RV is in SwipeRefreshLayout and has a match parent height, while SwipeRefreshLayout is vertically constrained with 0 dp height. Other RV is also vertically constrained with 0 dp height, directly in root layout. I think RVs don't change their height during runtime, they are constrained and take all available space all the time. – BochenChleba Mar 03 '23 at 12:30

1 Answers1

1

if you want scrollbars show only when user drag the list you should do it manually (disable/enable scrollbars).

you need to add addOnScrollListener and detect scroll to enable scrollbars also before notifyDataSetChanged disable scrollbars. something like this:

messagesRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
    // Check if the user has scrolled the RecyclerView
    if (dy != 0) {
        // Show the vertical scrollbar
        recyclerView.isVerticalScrollBarEnabled = true
    }
  }
})

and disable it before call changes:

recyclerView.isVerticalScrollBarEnabled = false
adapter.notifyDataSetChanged()
MHP
  • 2,613
  • 1
  • 16
  • 26
  • Seems to be working fine. I think it's more like a workaround than solving problem itself, but I didn't find better solution so I'm gonna use it. Thank you. – BochenChleba Mar 09 '23 at 10:23