Issue
Is it possible to set the background of a recyclerview to a gradient so that the gradient spans the whole recyclerview rather than spanning the screen? In other words I want the background drawable height to be equal to the total height of all items, so that the background color changes when user scrolls down and that the end color of the gradient can only be reached when the user scrolls down to the last item.
<shape>
<gradient
android:startColor="@color/colorPrimary500"
android:endColor="@color/colorSecondary50"
android:angle="90"/>
</shape>
When I use this shape as the background of a recyclerview with height="wrap_content"
, I can scroll down but the gradient spans the screen height and it is static, the end color (yellow) is always at the bottom of the screen and the start color (blue) is always at the top:
screenshot
One solution that I thought was setting the height of the recyclerview to the total height of its children but I don't know how it can be done.
Solution
There are a few ways to achieve that and to make that background scrollable.
- Move the background to a new view with the same height as the
RecyclerView
total height, and scroll it based on theRecyclerView
currenty
scroll position.
There are many downsides to this approach and it will not be easy to maintain as your app and codebase grows
- We could use a custom
RecyclerView.ItemDecoration
to manage that background and scroll it with theRecyclerView
seamlessly.
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class GradientItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
private var backgroundDrawable: Drawable? = null
init {
backgroundDrawable = context.getDrawable(R.drawable.background_gradient)
}
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
if (parent.layoutManager == null || backgroundDrawable == null) {
return
}
val left = parent.paddingLeft
val top = -parent.computeVerticalScrollOffset()
val right = parent.width - parent.paddingRight
val bottom = parent.computeVerticalScrollRange() + top
backgroundDrawable?.setBounds(left, top, right, bottom)
backgroundDrawable?.draw(canvas)
}
override fun getItemOffsets(
outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
) = outRect.set(0, 0, 0, 0)
}
And then add it to RecyclerView
.
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply {
....
addItemDecoration(GradientItemDecoration(context))
}
In the above code, we loaded the gradient image in GradientItemDecoration
and we update its bounds in onDraw
as the user interacts with the RecyclerView
.
You can also extend from DividerItemDecoration
instead if you want to still support dividers.
Edit
I created a Github repo showcasing this solution in action.
Answered By - Mostafa Gazar
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.