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 función onDrawOver
.
```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 la función onDrawOver()
,
- Obtenemos la referencia para el elemento
first
ysecond
delRecyclerView
. - Recuperamos el texto
header
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 texto header
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 ancho EXACT
de la vista del encabezado que se va a dibujar. Tenga en cuenta que height
es unspecified
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 clase recyclerView
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 artículo second
(además de algunos umbral es opcional) toca el bottom
de la continuación sticky header
del 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 este post.
Añadir comentario