Hola, soy Luis y esta vez les traigo este nuevo post.
Hay varias ocasiones en las que necesitamos implementar un encabezado fijo para alguna lista de datos que se muestran en formato RecyclerView
. Y, por supuesto, Android no tiene un componente de interfaz de usuario nativo para implementar esto fácilmente. Hay varias bibliotecas de terceros que podemos utilizar para lograr esta funcionalidad. Pero el uso de bibliotecas de terceros siempre tiene un costo. Siempre existe la duda de si la biblioteca que estamos usando se actualizará para versiones futuras o no, además de la sobrecarga adicional de LOC y el tamaño de la aplicación que agregan estas bibliotecas. Una forma más sencilla de lograr este mismo comportamiento sin tener que utilizar ninguna biblioteca de terceros es escribir una función personalizada RecyclerView ItemDecoration
y de anulación onDrawOver(canvas: Canvas, parent: RecyclerView, state: State)
. A continuación se muestra la descripción de cómo hacerlo. No dude en personalizar esta implementación para satisfacer sus necesidades:
Índice
ItemDecoration
personalizado
Esta es una envoltura alrededor de ItemDecoration
la clase abstracta. Como se indica en la documentación, estos ItemDecorations
se dibujan en el orden en que se agregaron, antes de las vistas y después de los elementos.
En esta implementación de la decoración personalizada, vamos a proporcionar tres cosas como parámetro:
- Adaptador RecyclerView
- Vista raíz, por ejemplo, la raíz del fragmento donde existe RecyclerView
- ID de recurso de diseño para el encabezado que se utilizará
``` kotlin class StickyHeaderDecoration<B : ViewDataBinding>(val adapter: StickyHeaderAdapter<*>, root: View, @LayoutRes headerLayout: Int) : ItemDecoration() { //lazily initialize the binding instance for the header view private val headerBinding:B by lazy { DataBindingUtil.inflate<B>(LayoutInflater.from(root.context), headerLayout, null, false) } override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: State) { super.onDrawOver(canvas, parent, state) // this will be further customized below } } ```
Arriba está la estructura básica para la ItemDecoration personalizada. Ahora tenemos que entender qué personalización incluye la onDrawOver
función.
Sigamos personalizando la onDrawOver()
función:
```kotlin override fun onDrawOver(canvas: Canvas, parent: RecyclerView, sate: State) { super.onDrawOver(canvas, parent, state) val topChild = parent.getChildAt(0) val secondChild = parent.getChildAt(1) parent.getChildAdapterPosition(topChild).let { topPosition -> val header = adapter.getHeaderForCurrentPosition(topPosition) headerView.tvStickyHeader.text = header layoutHeaderView(topChild) canvas.drawHeaderView(topChild, secondChild) } } ```
- layoutHeaderView (topChild: Ver)
``` kotlin private fun layoutHeaderView(topView: View) { headerView.measure( MeasureSpec.makeMeasureSpec(topView.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED) ) headerView.layout(topView.left, 0, topView.right, headerView.measuredHeight) } ```
- Canvas.drawHeaderView (topView: View, secondChild: View?)
``` kotlin private fun Canvas.drawHeaderView(topView: View, secondChild: View?) { save() translate(0f, calculateHeaderTop(topView, secondChild) headerView.draw(this) restore() } ```
- CalculateHeaderTop (topView: View, secondChild: View?): Float
```kotlin private fun calculateHeaderTop(topView: View, secondChild: View?) : Float = secondChild?.let { second -> // If there is any custom height to be added, calculate here if (secondView.findViewById(headerView.id)?.visibility != View.GONE) { secondView.top.toFloat() } else { maxOf(topView.top, 0).toFloat() } } ?: maxOf(topView.top, 0).toFloat() ```
En onDrawOver()
función,
- obtenemos la referencia para el elemento
first
ysecond
del RecyclerView - recuperamos el
header
texto del niño superior - medir el encabezado
- calcular la parte superior del encabezado y dibujar el encabezado
Función individual de la clase de decoración personalizada
fun getHeaderForCurrentPosition(topPosition) : Int{}
devuelve el header
texto para la posición dada.
``` kotlin // items is the list of objects displayed in the RecyclerView fun getHeaderForCurrentPosition(position: Int) = if (position in items.indices) { items[position] } else { "" } ```
fun layoutHeaderView(topView: View){}
mide el EXACT
ancho de la vista del encabezado que se va a dibujar. Tenga en cuenta que height
es unspecified
como estamos usando la vista de encabezado measuredHeight
.
fun calculateHeaderTop(topView: View, secondView: View?):Float{}
calcula el top
del encabezado. Si la segunda vista es visible, tomamos referencia a la parte superior de secondView, de lo contrario, la parte superior del encabezado es la parte superior de topView.
Uso básico
La costumbre ItemDecoration
se puede asignar a la recyclerView
clase Fragmento.
```kotlin // This custom decoration can be used in a fragment as follows class SomeFragment() { // initialization part... fragmentBinding.itemList.addItemDecoration( StickyHeaderItemDecoration<ViewStickyHeaderBinding>( someAdapter, fragmentBinding.root, R.layout.view_sticky_header ) ) ```
Comportamiento básico de desplazamiento
Cuando inicializamos la lista con los elementos, el texto del encabezado del primer elemento se mostrará tan pronto como se inflen todos los datos. Luego, a medida que nos desplazamos a través de la lista, cuando la top
del second
artículo (además de algunos umbral es opcional) toca el bottom
de la sticky header
continuación de la header
para el segundo artículo se dibuja. Y esto continúa mientras nos desplazamos hacia arriba por la lista. A medida que nos desplazamos hacia abajo por la lista, se produce el comportamiento inverso y se dibuja el texto del encabezado. La clase de adaptador es responsable de proporcionar el texto del encabezado para cualquier posición dada si la posición está dentro de los límites del tamaño de los elementos.
La implementación de muestra se puede encontrar aquí con fines de demostración:
Gracias por leer.
Añadir comentario