2015-06-16 2 views
44

Ich versuche, Abstand unter der letzten Element Zeile in RecyclerView mit GridLayoutManager hinzuzufügen. Ich verwenden individuelle ItemDecoration zu diesem Zweck mit der Bodenpolsterung, wenn das letzte Element, wie folgt:Android hinzufügen Abstand unter dem letzten Element in recyclerview mit gridlayoutmanager

public class SpaceItemDecoration extends RecyclerView.ItemDecoration { 
private int space; 
private int bottomSpace = 0; 

public SpaceItemDecoration(int space, int bottomSpace) { 
    this.space = space; 
    this.bottomSpace = bottomSpace; 
} 

public SpaceItemDecoration(int space) { 
    this.space = space; 
    this.bottomSpace = 0; 
} 

@Override 
public void getItemOffsets(Rect outRect, View view, 
          RecyclerView parent, RecyclerView.State state) { 

    int childCount = parent.getChildCount(); 
    final int itemPosition = parent.getChildAdapterPosition(view); 
    final int itemCount = state.getItemCount(); 

    outRect.left = space; 
    outRect.right = space; 
    outRect.bottom = space; 
    outRect.top = space; 

    if (itemCount > 0 && itemPosition == itemCount - 1) { 
     outRect.bottom = bottomSpace; 
    } 
} 
} 

Aber das Problem mit dieser Methode ist, dass sie die Elementhöhen im Raster in letzter Zeile durcheinander. Ich vermute, dass GridLayoutManager die Höhen für Elemente basierend auf dem Abstand ändert. Was ist der richtige Weg, um dies zu erreichen?

Dies funktioniert ordnungsgemäß für eine LinearLayoutManager. Gerade im Fall von GridLayoutManager ist es problematisch.

Es ist sehr nützlich, wenn Sie eine FAB im unteren Bereich haben und Elemente in der letzten Zeile über FAB scrollen müssen, damit sie sichtbar sein können.

Antwort

6

Die Lösung für dieses Problem liegt in der Überschreibung der SpanSizeLookup von GridLayoutManager.

Sie müssen Änderungen am GridlayoutManager in der Aktivität oder dem Fragment vornehmen, wo Sie das RecylerView aufblasen.

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    //your code 
    recyclerView.addItemDecoration(new PhotoGridMarginDecoration(context)); 

    // SPAN_COUNT is the number of columns in the Grid View 
    GridLayoutManager gridLayoutManager = new GridLayoutManager(context, SPAN_COUNT); 

    // With the help of this method you can set span for every type of view 
    gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 
     @Override 
     public int getSpanSize(int position) { 
      if (list.get(position).getType() == TYPE_HEADER) { 
       // Will consume the whole width 
       return gridLayoutManager.getSpanCount(); 
      } else if (list.get(position).getType() == TYPE_CONTENT) { 
       // will consume only one part of the SPAN_COUNT 
       return 1; 
      } else if(list.get(position).getType() == TYPE_FOOTER) { 
       // Will consume the whole width 
       // Will take care of spaces to be left, 
       // if the number of views in a row is not equal to 4 
       return gridLayoutManager.getSpanCount(); 
      } 
      return gridLayoutManager.getSpanCount(); 
     } 
    }); 
    recyclerView.setLayoutManager(gridLayoutManager); 
} 
2

Sie können Ihrer Recycleransicht eine leere Fußzeile hinzufügen. Ihre Polsterung entspricht der Größe Ihrer Fußzeile.

+0

Dies ist eine gute Option. Aber mein Problem damit ist, dass ich bereits eine benutzerdefinierte ItemDecoration benutze, die basierend auf Artikelpositionen in recyclerview funktioniert und entscheidet, wo und in welchen Abstand und Trenner etc. gesetzt wird. Wenn ich diesen Abstand als zusätzliches Element hinzufüge, zählt ItemDeco ration es als Element und fügt dem Abstand auch Teiler hinzu. Also dachte ich, wenn jemand versucht hat, eine benutzerdefinierte ItemDecoration zu verwenden, um das Problem zu lösen. – pratsJ

+0

Auch Ihre Methode funktioniert gut im Fall von LinearLayoutManager oder im Fall von GridLayoutManager, wenn die untere Reihe des Rasters voll ist. Andernfalls wird der Speicherplatz im leeren Elementbereich und nicht im unteren Bereich des gesamten Gitters in das Raster übertragen. – pratsJ

1

Sie können den folgenden Code verwenden, um die erste und die letzte Zeile in einer Rasteransicht zu erkennen und den oberen und unteren Offset entsprechend einzustellen.

@Override 
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { 
    LayoutParams params = (LayoutParams) view.getLayoutParams(); 
    int pos = params.getViewLayoutPosition(); 
    int spanCount = mGridLayoutManager.getSpanCount(); 

    boolean isFirstRow = pos < spanCount; 
    boolean isLastRow = state.getItemCount() - 1 - pos < spanCount; 

    if (isFirstRow) { 
     outRect.top = top offset value here 
    } 

    if (isLastRow) { 
     outRect.bottom = bottom offset value here 
    } 
} 

// you also need to keep reference to GridLayoutManager to know the span count 
private final GridLayoutManager mGridLayoutManager; 
0

Mit solchen Dingen wird empfohlen, die ItemDecoration so zu lösen, wie sie dafür gedacht sind.

public class ListSpacingDecoration extends RecyclerView.ItemDecoration { 

    private static final int VERTICAL = OrientationHelper.VERTICAL; 

    private int orientation = -1; 
    private int spanCount = -1; 
    private int spacing; 


    public ListSpacingDecoration(Context context, @DimenRes int spacingDimen) { 

    spacing = context.getResources().getDimensionPixelSize(spacingDimen); 
    } 

    public ListSpacingDecoration(int spacingPx) { 

    spacing = spacingPx; 
    } 

    @Override 
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 

    super.getItemOffsets(outRect, view, parent, state); 

    if (orientation == -1) { 
     orientation = getOrientation(parent); 
    } 

    if (spanCount == -1) { 
     spanCount = getTotalSpan(parent); 
    } 

    int childCount = parent.getLayoutManager().getItemCount(); 
    int childIndex = parent.getChildAdapterPosition(view); 

    int itemSpanSize = getItemSpanSize(parent, childIndex); 
    int spanIndex = getItemSpanIndex(parent, childIndex); 

    /* INVALID SPAN */ 
    if (spanCount < 1) return; 

    setSpacings(outRect, parent, childCount, childIndex, itemSpanSize, spanIndex); 
    } 

    protected void setSpacings(Rect outRect, RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { 

    if (isBottomEdge(parent, childCount, childIndex, itemSpanSize, spanIndex)) { 
     outRect.bottom = spacing; 
    } 
    } 

    @SuppressWarnings("all") 
    protected int getTotalSpan(RecyclerView parent) { 

    RecyclerView.LayoutManager mgr = parent.getLayoutManager(); 
    if (mgr instanceof GridLayoutManager) { 
     return ((GridLayoutManager) mgr).getSpanCount(); 
    } else if (mgr instanceof StaggeredGridLayoutManager) { 
     return ((StaggeredGridLayoutManager) mgr).getSpanCount(); 
    } else if (mgr instanceof LinearLayoutManager) { 
     return 1; 
    } 

    return -1; 
    } 

    @SuppressWarnings("all") 
    protected int getItemSpanSize(RecyclerView parent, int childIndex) { 

    RecyclerView.LayoutManager mgr = parent.getLayoutManager(); 
    if (mgr instanceof GridLayoutManager) { 
     return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanSize(childIndex); 
    } else if (mgr instanceof StaggeredGridLayoutManager) { 
     return 1; 
    } else if (mgr instanceof LinearLayoutManager) { 
     return 1; 
    } 

    return -1; 
    } 

    @SuppressWarnings("all") 
    protected int getItemSpanIndex(RecyclerView parent, int childIndex) { 

    RecyclerView.LayoutManager mgr = parent.getLayoutManager(); 
    if (mgr instanceof GridLayoutManager) { 
     return ((GridLayoutManager) mgr).getSpanSizeLookup().getSpanIndex(childIndex, spanCount); 
    } else if (mgr instanceof StaggeredGridLayoutManager) { 
     return childIndex % spanCount; 
    } else if (mgr instanceof LinearLayoutManager) { 
     return 0; 
    } 

    return -1; 
    } 

    @SuppressWarnings("all") 
    protected int getOrientation(RecyclerView parent) { 

    RecyclerView.LayoutManager mgr = parent.getLayoutManager(); 
    if (mgr instanceof LinearLayoutManager) { 
     return ((LinearLayoutManager) mgr).getOrientation(); 
    } else if (mgr instanceof GridLayoutManager) { 
     return ((GridLayoutManager) mgr).getOrientation(); 
    } else if (mgr instanceof StaggeredGridLayoutManager) { 
     return ((StaggeredGridLayoutManager) mgr).getOrientation(); 
    } 

    return VERTICAL; 
    } 

    protected boolean isBottomEdge(RecyclerView parent, int childCount, int childIndex, int itemSpanSize, int spanIndex) { 

    if (orientation == VERTICAL) { 

     return isLastItemEdgeValid((childIndex >= childCount - spanCount), parent, childCount, childIndex, spanIndex); 

    } else { 

     return (spanIndex + itemSpanSize) == spanCount; 
    } 
    } 

    protected boolean isLastItemEdgeValid(boolean isOneOfLastItems, RecyclerView parent, int childCount, int childIndex, int spanIndex) { 

    int totalSpanRemaining = 0; 
    if (isOneOfLastItems) { 
     for (int i = childIndex; i < childCount; i++) { 
      totalSpanRemaining = totalSpanRemaining + getItemSpanSize(parent, i); 
     } 
    } 

    return isOneOfLastItems && (totalSpanRemaining <= spanCount - spanIndex); 
    } 
} 

Ich kopierte eine von meiner ursprünglichen Antwort here bearbeitet, die eigentlich für eine gleichmäßige Verteilung ist, aber es ist das gleiche Konzept.

205

einfach eine Polsterung hinzufügen und android:clipToPadding="false"

<RecyclerView 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:paddingBottom="8dp" 
    android:clipToPadding="false" /> 

Dank dieser wunderbaren answer!

+1

Das hat es für mich getan. Schnelle und einfache Lösung, wenn Sie Gegenstände im Recycler gleichmäßig aufstellen müssen. Vielen Dank. – Empty2k12

+1

Das war genau das, was ich gesucht habe! Vielen Dank! –

+1

Das ist genau das, was ich will. thx –

Verwandte Themen