Margen / relleno en el último hijo en RecyclerView

126

Estoy tratando de agregar Padding / Margin Bottom en la última fila y Padding / Margin Top en la primera fila. No puedo hacerlo en el elemento xml, ya que afectaría a todos mis hijos.

Tengo encabezados y elementos secundarios en mi adaptador RecyclerView, por lo que no puedo usar el

   android:padding="4dp"
   android:clipToPadding="false"

Necesito usarlo individualmente en la última primera fila de cada encabezado

Aguila Roja
fuente
Úselo así: <android.support.v7.widget.RecyclerView android: id = "@ + id / list" android: paddingBottom = "5dp" android: layout_width = "match_parent" android: layout_height = "wrap_content" />
Pankaj Arora
Puede cambiar el relleno y el margen en onBindViewHolder y luego llamar a setIsRecyclable (false) en el soporte de la vista
usuario 3425867

Respuestas:

9

Yo uso esto en kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder(view), position: Int) {
    if (position == itemsList.lastIndex){
        val params = holder.itemView.layoutParams as FrameLayout.LayoutParams
        params.bottomMargin = 100
        holder.itemView.layoutParams = params
    }else{
        val params = holder.itemView.layoutParams as RecyclerView.LayoutParams
        params.bottomMargin = 0
        holder.itemView.layoutParams = params
    }
  //other codes ...
}
Radesh
fuente
232

Este problema es aún más fácil de resolver. Puede aplicar el relleno necesario en RecylerViewsí mismo y establecerlo clipToPaddingen falso, de lo contrario, el relleno cortará su área de desplazamiento. Aquí hay un ejemplo

<android.support.v7.widget.RecyclerView
    android:padding="4dp"
    android:clipToPadding="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Ver el relleno agregará 4dp en todos los lados, incluida la parte superior e inferior. Luego, el parámetro clipToPadding asegura que sus elementos secundarios no se corten. Ahora, agregue 4dprelleno a todos los lados para los artículos de su hijo, y listo. En total, obtienes un relleno de 8dp en los lados y entre los artículos.

Subin Sebastian
fuente
Sí, así es como lo hago habitualmente, pero mi RecyclerView consta de encabezados y cada encabezado tiene sus propios hijos, es por eso que necesito configurarlo programáticamente en la última y primera fila de cada encabezado solo
RedEagle el
No estoy seguro si entiendo. ¿Cómo se relaciona esto con los encabezados dentro de RecyclerView? ¿Quieres que los encabezados no tengan relleno? Incluso si ese es el caso, puede deshacerse del relleno izquierdo y derecho con la misma solución.
Subin Sebastian
Tengo encabezados y cada encabezado tiene un recuento no específico de elementos secundarios, por lo que quiero agregar el relleno / margen superior y el primer elemento secundario y el margen / margen inferior en el último elemento secundario de cada encabezado.
RedEagle
La única idea que tuve fue establecer el margen superior e inferior en el diseño de mi encabezado, pero me preguntaba si hay una forma más inteligente de lograrlo
RedEagle
En ese caso, simplemente puede agregar un elemento falso en RecyclerView después de su último hijo. Esa es la forma más simple, si no la más inteligente, de lograr esto. :)
Subin Sebastian
82

En lugar de agregar relleno a los elementos superior e inferior, puede agregar el relleno a la parte superior e inferior de su RecyclerView y establecer el clipToPaddingatributo en false.

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:paddingTop="8dp"
    android:paddingBottom="8dp" />
Collins Abitekaniza
fuente
Esa es la solución fácil, pero tengo encabezados e hijos en mi RecyclerView, así que no puedo usarlo
RedEagle
@ RedEagle Intenta ver si obtienes el efecto deseado, pero estoy seguro de que funciona.
Collins Abitekaniza
1
Esta solución es mejor que la Decoración del artículo. Cuando estoy usando ListAdapter y envío una lista diferente, si el último elemento de la lista anterior no cambió su posición pero se agregaron nuevos elementos después, el primero mantendrá su relleno inferior aunque no sea el último elemento en la lista más.
Ana Koridze
38

uso ItemDecoration:

private class SpacesItemDecoration extends RecyclerView.ItemDecoration {
    private int space;

    public SpacesItemDecoration(int space) {
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        boolean isLast = position == state.getItemCount()-1;
        if(isLast){
            outRect.bottom = space;
            outRect.top = 0; //don't forget about recycling...
        }
        if(position == 0){
            outRect.top = space;
            // don't recycle bottom if first item is also last
            // should keep bottom padding set above
            if(!isLast)
                outRect.bottom = 0;
        }
    }
}

y

//8dp as px
int space = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
            getResources().getDisplayMetrics()); // calculated
//int space = getResources().getDimensionPixelSize(
//    R.dimen.list_item_padding_vertical); // from resources
recyclerView.addItemDecoration(new SpacesItemDecoration(space));
snachmsm
fuente
1
Oye, ya no parece que RecyclerView.ItemDecoration obtenga la clase de recurso, simplemente la agregué como argumento en el constructor.
Krtko
2
¡Buena solución! Con soporte de diseño inverso: gist.github.com/Miha-x64/4e8754e72a1e65e3ca742744ea501f16
Miha_x64
2
¡Esta respuesta debe ser votada! Aunque la respuesta de Subin Sebastian está funcionando bien, sin embargo, con ItemDecoration los espacios no son iguales.
iroiroys
1
pero después de insertar la eliminación de elementos, no volverá a dibujar compensaciones para todos los elementos
usuario924
puede ser cierto, ya que notifyItemInserted/ Removeddibujará solo un elemento / eliminará un elemento y lo limpiará, Viewdejando intactos a todos los demás niños. por ejemplo, cuando algún elemento está primero en la lista (dibujado con el relleno superior) y estamos agregando uno nuevo al principio, entonces el elemento anterior-primero-actualmente-segundo todavía se rellenará en la parte superior ... la solución fácil es llamar notifyDataSetChanged(forzar redibujado todos ...), más complejos necesitan algo de lógica para reconocer ese elemento, que estaba en la primera y / o última posición, se movió y luego llamónotifyItemChanged(newPositionOfOldFirstAndOrLastItem)
snachmsm
28
<android.support.v7.widget.RecyclerView
    android:id="@+id/rv_tpf"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipToPadding="false"
    android:paddingBottom="100dp" />

Agregue android:clipToPadding="false"y android:paddingBottom="100dp"en su vista de reciclaje.

darshan patel
fuente
2
¡Increíble! Eso es perfecto
KevinB
16

Agregue android: clipToPadding = "false" y android: paddingBottom = "65dp" en su vista de reciclaje. Si está utilizando un botón de menú fabuloso y acciones en la celda de vista del reciclador.

<androidx.recyclerview.widget.RecyclerView
      android:id="@+id/dinner_recycler_view"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:clipToPadding="false"
      android:paddingBottom="65dp"/>
Yogesh Shinde
fuente
3

Por alguna razón, la clipToPadding=falsesolución anterior no me funciona. Entonces agregué un ItemDecoration

https://gist.github.com/kassim/582888fa5960791264fc92bc41fb6bcf

public class BottomPaddingDecoration extends RecyclerView.ItemDecoration {
    private final int bottomPadding;

    public BottomPaddingDecoration(int bottomPadding) {
        this.bottomPadding = bottomPadding;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
        if (position == parent.getAdapter().getItemCount() - 1) {
            outRect.set(0, 0, 0, bottomPadding);
        }
    }
}
kassim
fuente
1

He modificado la respuesta increíble @snachmsm para mejor y te doy una idea de cómo usarla correctamente

public class SpacesItemDecoration extends DividerItemDecoration {
    private int space;

    public SpacesItemDecoration(Context clContext,int oriantation,int space) {
        super(clContext,oriantation);
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect,view,parent,state);
        int position = parent.getChildAdapterPosition(view);
        boolean isLast = position == state.getItemCount()-1;
        if(isLast){
            outRect.bottom = space;
            outRect.top = 0; //don't forget about recycling...
        }
       /* if(position == 0){
            outRect.top = space;
            // don't recycle bottom if first item is also last
            // should keep bottom padding set above
            if(!isLast)
                outRect.bottom = 0;
        }*/
    }
}
sourav pandit
fuente