Android 具有渐变背景的RecyclerView
Android RecyclerView with gradient background
是否可以将recyclerview的背景设置为渐变,让渐变覆盖整个recyclerview而不是横跨屏幕?换句话说,我希望背景可绘制高度等于所有项目的总高度,以便当用户向下滚动时背景颜色发生变化,并且只有当用户向下滚动到最后一个时才能达到渐变的结束颜色项目。
<shape>
<gradient
android:startColor="@color/colorPrimary500"
android:endColor="@color/colorSecondary50"
android:angle="90"/>
</shape>
当我使用此形状作为带有 height="wrap_content"
的 recyclerview 的背景时,我可以向下滚动,但渐变跨越屏幕高度并且是静态的,结束颜色(黄色)始终位于底部屏幕和开始颜色(蓝色)总是在顶部:
screenshot
我想到的一个解决方案是将 recyclerview 的高度设置为它的子项的总高度,但我不知道该怎么做。
有几种方法可以实现这一点并使背景可滚动。
- 将背景移动到与
RecyclerView
总高度相同的新视图,并根据RecyclerView
当前y
滚动位置滚动。
这种方法有很多缺点,随着您的应用程序和代码库的增长,维护起来也不容易
- 我们可以使用自定义
RecyclerView.ItemDecoration
来管理该背景并使用 RecyclerView
无缝滚动它。
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)
}
然后添加到RecyclerView
。
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply {
....
addItemDecoration(GradientItemDecoration(context))
}
在上面的代码中,我们在 GradientItemDecoration
中加载了渐变图像,并在用户与 RecyclerView
交互时在 onDraw
中更新了它的边界。
如果您仍想支持分隔符,也可以从 DividerItemDecoration
扩展。
编辑
我创建了一个 Github repo 来展示这个解决方案的实际应用。
是否可以将recyclerview的背景设置为渐变,让渐变覆盖整个recyclerview而不是横跨屏幕?换句话说,我希望背景可绘制高度等于所有项目的总高度,以便当用户向下滚动时背景颜色发生变化,并且只有当用户向下滚动到最后一个时才能达到渐变的结束颜色项目。
<shape>
<gradient
android:startColor="@color/colorPrimary500"
android:endColor="@color/colorSecondary50"
android:angle="90"/>
</shape>
当我使用此形状作为带有 height="wrap_content"
的 recyclerview 的背景时,我可以向下滚动,但渐变跨越屏幕高度并且是静态的,结束颜色(黄色)始终位于底部屏幕和开始颜色(蓝色)总是在顶部:
screenshot
我想到的一个解决方案是将 recyclerview 的高度设置为它的子项的总高度,但我不知道该怎么做。
有几种方法可以实现这一点并使背景可滚动。
- 将背景移动到与
RecyclerView
总高度相同的新视图,并根据RecyclerView
当前y
滚动位置滚动。
这种方法有很多缺点,随着您的应用程序和代码库的增长,维护起来也不容易
- 我们可以使用自定义
RecyclerView.ItemDecoration
来管理该背景并使用RecyclerView
无缝滚动它。
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)
}
然后添加到RecyclerView
。
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply {
....
addItemDecoration(GradientItemDecoration(context))
}
在上面的代码中,我们在 GradientItemDecoration
中加载了渐变图像,并在用户与 RecyclerView
交互时在 onDraw
中更新了它的边界。
如果您仍想支持分隔符,也可以从 DividerItemDecoration
扩展。
编辑
我创建了一个 Github repo 来展示这个解决方案的实际应用。