2015-06-16 33 views
44

我想在RecyclerViewGridLayoutManager最后一个元素行下面添加间距。我使用的自定义ItemDecoration为此与底部填充时,其最后一个元素如下:Android添加间距低于lastlay元素在recyclerview与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; 
    } 
} 
} 

但是,使用这种方法的问题是,它在最后一排格子搞砸元素的高度。我猜测GridLayoutManager会根据剩余间距改变元素的高度。什么是实现这一目标的正确方法?

这对于LinearLayoutManager将正常工作。以防万一GridLayoutManager其问题。

它在底部有FAB的情况下非常有用,并且需要最后一行中的项目滚动到FAB以上才能看到它们。

回答

6

此问题的解决方案在于重写GridLayoutManager的SpanSizeLookup。

您必须对Activity或Fragment中的GridlayoutManager进行更改,并在此处对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

你可以做的是给你的回收站添加一个空的页脚。你的填充将是你的页脚的大小。

@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

这是一个不错的选择。然而,我的问题是我使用的是一个自定义的ItemDecoration,它基于recyclerview中的项目位置来工作,并决定在哪里放置什么间距和分隔符等。现在,如果我将此间距作为附加元素添加,我的ItemDecoration会将其计为一个元素,分隔符也添加到间距。所以我在想如果有人尝试使用自定义ItemDecoration来解决问题。 – pratsJ

+0

在LinearLayoutManager的情况下,或者在GridLayoutManager的情况下,如果网格的底部行已满,您的方法也可以正常工作。否则,空间将进入空元素空间的网格,而不是整个网格的底部。 – pratsJ

1

您可以使用下面的代码来检测网格视图中的第一行和最后一行,并相应地设置顶部和底部的偏移量。

@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

有了这样的事情,建议使用ItemDecoration解决它,因为它们是为了那个。

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

我复制了我原来的答复here这实际上是等间距的编辑,但它是同一个概念。

205

只需添加填充,并且设置android:clipToPadding="false"

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

感谢这个美好的answer

+1

这是为我做的。快速'简单的解决方案,当您需要在回收站中平等放置物品时。谢谢。 – Empty2k12

+1

这正是我所期待的!谢谢! –

+1

这正是我想要的。 thx –

相关问题