Margines / dopełnienie w ostatnim podrzędnym w RecyclerView

126

Próbuję dodać Padding / Margin Bottom w ostatnim wierszu i Padding / Margin Top w pierwszym wierszu. Nie mogę tego zrobić w pozycji xml, ponieważ wpłynęłoby to na wszystkie moje Childs.

Mam nagłówki i elementy podrzędne w moim adapterze RecyclerView, więc nie mogę używać

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

Muszę używać go indywidualnie w ostatnim pierwszym wierszu każdego nagłówka

Czerwony orzeł
źródło
Użyj w ten sposób: <android.support.v7.widget.RecyclerView android: id = "@ + id / list" android: paddingBottom = "5dp" android: layout_width = "match_parent" android: layout_height = "wrap_content" />
Pankaj Arora
Możesz zmienić dopełnienie i margines w onBindViewHolder, a następnie wywołać setIsRecyclable (false) na uchwycie widoku
user3425867

Odpowiedzi:

9

Używam tego w Kotlinie

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
źródło
234

Ten problem jest jeszcze łatwiejszy do rozwiązania. Możesz zastosować niezbędne dopełnienie do RecylerViewsamego siebie i ustawić clipToPaddingna fałsz, w przeciwnym razie dopełnienie odetnie obszar przewijania. Oto przykład

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

Zobacz, że wypełnienie doda 4 dp ze wszystkich stron, w tym na górze i na dole. Następnie parametr clipToPadding zapewnia, że ​​elementy podrzędne nie są odcinane. Teraz dodaj 4dpwyściółkę ze wszystkich stron dla przedmiotów dziecięcych i gotowe. W sumie otrzymujesz wyściółkę 8 dp po bokach i między elementami.

Subin Sebastian
źródło
Tak, tak to zwykle robię, ale mój RecyclerView składa się z nagłówków, a każdy nagłówek ma swoje własne elementy potomne, dlatego muszę ustawić go programowo tylko do ostatniego i pierwszego wiersza każdego nagłówka
RedEagle
Nie jestem pewien, czy rozumiem. Jak to się ma do nagłówków w RecyclerView? Czy chcesz, aby nagłówki nie miały wypełnienia? Nawet jeśli tak jest, możesz pozbyć się lewego i prawego dopełnienia za pomocą tego samego rozwiązania.
Subin Sebastian
Mam nagłówki, a każdy nagłówek ma niespecyficzną liczbę elementów podrzędnych, więc chcę dodać górne wypełnienie / margines i pierwsze dziecko i dolne wypełnienie / margines na ostatnim podrzędnym poziomie każdego nagłówka.
RedEagle
Jedynym pomysłem, jaki miałem, było ustawienie górnego i dolnego marginesu w układzie mojego nagłówka, ale zastanawiałem się, czy istnieje mądrzejszy sposób, aby to osiągnąć
RedEagle
W takim przypadku możesz po prostu dodać fałszywy element do RecyclerView po ostatnim dziecku. To najprostszy, jeśli nie najmądrzejszy sposób na osiągnięcie tego. :)
Subin Sebastian
82

Zamiast dodawać dopełnienie zarówno do górnych, jak i dolnych elementów, możesz po prostu dodać dopełnienie do górnej i dolnej części RecyclerView i ustawić clipToPaddingatrybut na 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
źródło
To proste rozwiązanie, ale mam nagłówki i elementy podrzędne w moim RecyclerView, więc nie mogę ich używać
RedEagle
@RedEagle Spróbuj i zobacz, czy uzyskasz pożądany efekt, ale jestem pewien, że działa.
Collins Abitekaniza
1
To rozwiązanie jest lepsze niż dekoracja przedmiotów. Kiedy używam ListAdapter i przesyłam inną listę, jeśli ostatnia pozycja poprzedniej listy nie zmieniła swojej pozycji, ale po niej zostały dodane nowe elementy, ta pierwsza zachowa dolne wypełnienie, mimo że nie jest to ostatnia pozycja na liście. więcej.
Ana Koridze,
38

zastosowanie 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;
        }
    }
}

i

//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
źródło
1
Hej, wygląda na to, że RecyclerView.ItemDecoration nie pobiera już klasy zasobów, po prostu dodałem ją jako argument w konstruktorze.
Krtko
2
Goog rozwiązanie! Z obsługą odwróconego układu: gist.github.com/Miha-x64/4e8754e72a1e65e3ca742744ea501f16
Miha_x64
2
ta odpowiedź musi zostać przyjęta za! Chociaż odpowiedź Subina Sebastiana działa dobrze, jednak z przestrzeniami PrzedmiotDecoration nie są równe.
iroiroys
1
ale po wstawieniu usuwania elementów nie będzie przerysowywać przesunięć dla wszystkich elementów
użytkownik924
może być prawdą, ponieważ notifyItemInserted/ Removednarysuje tylko jedną pozycję / usunie przedmiot i wyczyści, Viewpozostawiając wszystkie inne dzieci nietknięte. np. gdy jakaś pozycja jest pierwsza na liście (rysowana z górnym wypełnieniem) a my dodajemy nowy na górze, to poprzednio-pierwsza-obecnie-druga pozycja nadal będzie dopełniona na górze ... łatwą naprawą jest wywołanie notifyDataSetChanged(wymuszone przerysowanie all ...), bardziej złożone wymagają logiki rozpoznawania tego przedmiotu, który był na pierwszej i / lub ostatniej pozycji, przesunął się, a następnie zadzwonił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" />

Dodaj android:clipToPadding="false"i android:paddingBottom="100dp"w widoku recyklingu.

darszan patel
źródło
2
Niesamowite! To idealne
KevinB
16

Dodaj android: clipToPadding = "false" i android: paddingBottom = "65dp" w widoku recyklingu. Jeśli używasz przycisku menu fab i akcji w komórce widoku recyklera.

<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
źródło
3

Z jakiegoś powodu stary clipToPadding=false rozwiązanie nie działa dla mnie. Więc dodałem 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
źródło
1

Zmodyfikowałem niesamowitą odpowiedź @snachmsm odpowiedź na lepsze i dam ci pomysł, jak prawidłowo używać

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
źródło