2015-06-16 7 views
44

Sto cercando di aggiungere spaziatura sotto l'ultima riga dell'elemento in RecyclerView con GridLayoutManager. Ho usato personalizzata ItemDecoration a questo scopo con imbottitura fondo quando l'ultimo elemento come segue:Android aggiungi la spaziatura sotto l'ultimo elemento in recyclerview con 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; 
    } 
} 
} 

Ma il problema di questo metodo è che incasinato le altezze degli elementi nella griglia in ultima fila. Sto indovinando che GridLayoutManager cambia le altezze per gli elementi basati sulla spaziatura a sinistra. Qual è il modo corretto per raggiungere questo obiettivo?

Questo funzionerà correttamente per un LinearLayoutManager. Solo in caso di un GridLayoutManager è problematico.

È molto utile nel caso in cui si abbia un FAB in basso e occorrono elementi nell'ultima riga per scorrere sopra FAB in modo che possano essere visibili.

risposta

6

La soluzione a questo problema consiste nel sovrascrivere SpanSizeLookup di GridLayoutManager.

È necessario apportare modifiche a GridlayoutManager nell'attività o frammento in cui si sta gonfiando il RecylerView.

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

Quello che puoi fare è aggiungere un footer vuoto al tuo recyclerview. Il tuo padding avrà le dimensioni del tuo footer.

@Override 
public Holder onCreateViewHolder(ViewGroup parent, int viewType) { 
    if (viewType == FOOTER) { 
     return new FooterHolder(); 
    } 
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false); 
    return new Holder(view); 
} 

@Override 
public void onBindViewHolder(final Holder holder, final int position) { 
    //if footer 
    if (position == items.getSize() - 1) { 
    //do nothing 
     return; 
    } 
    //do regular object bindding 

} 

@Override 
public int getItemViewType(int position) { 
    return (position == items.getSize() - 1) ? FOOTER : ITEM_VIEW_TYPE_ITEM; 
} 

@Override 
public int getItemCount() { 
    //add one for the footer 
    return items.size() + 1; 
} 
+0

Questa è una buona opzione. Comunque il mio problema è che sto usando già una ItemDecoration personalizzata che funziona in base alle posizioni degli oggetti in recyclerview e decide dove mettere la spaziatura e il divisore ecc. Ora se aggiungo questa spaziatura come elemento aggiuntivo, il mio ItemDecoration lo conta come un elemento e aggiunge anche il divisore alla spaziatura. Quindi stavo pensando se qualcuno ha provato a utilizzare un oggetto ItemDecoration personalizzato per risolvere il problema. – pratsJ

+0

Anche il tuo metodo funzionerà bene nel caso di LinearLayoutManager o in caso di GridLayoutManager se la riga inferiore della griglia è piena. Altrimenti lo spazio andrà nella griglia nello spazio vuoto dell'elemento e non nella parte inferiore dell'intera griglia. – pratsJ

1

È possibile utilizzare il codice sottostante per rilevare prime e ultime righe in una vista griglia e impostare offset superiore e inferiore corrispondentemente.

@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

Con cose come questo, si consiglia di risolvere utilizzando l'ItemDecoration così come sono pensati per quello.

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

ho copiato un modificati dalla mia risposta originale here che è in realtà per la parità di distanza, ma è lo stesso concetto.

205

Basta aggiungere un padding e impostare android:clipToPadding="false"

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

Grazie a questo meraviglioso answer!

+1

Questo lo ha fatto per me. Soluzione rapida e semplice quando è necessario distribuire gli articoli nello riciclatore. Grazie. – Empty2k12

+1

Questo era esattamente quello che stavo cercando! Grazie! –

+1

Questo è esattamente quello che voglio. grazie –

Problemi correlati