Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/233.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android使用gridlayoutmanager在recyclerview的最后一个元素下方添加间距_Android_Android Recyclerview_Gridlayoutmanager - Fatal编程技术网

Android使用gridlayoutmanager在recyclerview的最后一个元素下方添加间距

Android使用gridlayoutmanager在recyclerview的最后一个元素下方添加间距,android,android-recyclerview,gridlayoutmanager,Android,Android Recyclerview,Gridlayoutmanager,我正在尝试使用GridLayoutManager在RecyclerView中的最后一个元素行下方添加间距。为此,我使用了customItemDecoration作为底部填充,其最后一个元素如下所示: public class SpaceItemDecoration extends RecyclerView.ItemDecoration { private int space; private int bottomSpace = 0; public SpaceItemDecoration(int

我正在尝试使用
GridLayoutManager
RecyclerView
中的最后一个元素行下方添加间距。为此,我使用了custom
ItemDecoration
作为底部填充,其最后一个元素如下所示:

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
会根据左间距更改元素的高度。实现这一目标的正确方法是什么

对于
线性布局管理器
,这将正常工作。如果是
GridLayoutManager
的话,它就有问题了


如果底部有一个
FAB
,并且需要最后一行中的项目滚动到
FAB
上方,以便它们可见,那么它非常有用。

您可以做的是在回收视图中添加一个空页脚。您的填充将是页脚的大小

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

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

@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;
@覆盖
public void getItemOffset(Rect outRect、视图视图、RecyclerView父级、状态){
LayoutParams params=(LayoutParams)视图。getLayoutParams();
int pos=params.getViewLayoutPosition();
int spanCount=mGridLayoutManager.getSpanCount();
布尔值isFirstRow=pos
此问题的解决方案在于过度阅读GridLayoutManager的SpanSizeLookup

您必须在活动或片段中对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);
}

对于这样的事情,建议使用itemEdition来解决,因为它们就是为了解决这个问题

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);
  }
}
公共类ListSpacingDecoration扩展了RecyclerView.ItemDecoration{
private static final int VERTICAL=OrientationHelper.VERTICAL;
私有int方向=-1;
私有整数span计数=-1;
私有整数间隔;
公共ListSpacingDecoration(上下文上下文,@DimenRes int-spacingDimen){
间距=context.getResources().getDimensionPixelSize(spacingDimen);
}
公共列表空间配置(int spacingPx){
间距=间距gpx;
}
@凌驾
public void getItemOffsets(Rect outRect、View View、RecyclerView父级、RecyclerView.State){
super.getItemOffset(outRect、view、parent、state);
如果(方向==-1){
方向=获取方向(父级);
}
如果(spanCount==-1){
spanCount=getTotalSpan(父级);
}
int childCount=parent.getLayoutManager().getItemCount();
int childIndex=parent.getChildAdapterPosition(视图);
int itemSpanSize=getItemSpanSize(父、子索引);
int spanIndex=getItemSpanIndex(父、子索引);
/*无效跨度*/
如果(spanCount<1)返回;
设置空间(outRect、parent、childCount、childIndex、itemSpanSize、spanIndex);
}
受保护的void setpackings(Rect outRect、RecyclerView parent、int childCount、int childIndex、int itemSpanSize、int spanIndex){
if(isBottomEdge(父项、子项计数、子项索引、项span大小、span索引)){
outRect.bottom=间距;
}
}
@抑制警告(“全部”)
受保护的int getTotalSpan(RecyclerView父级){
RecycleView.LayoutManager mgr=parent.getLayoutManager();
if(GridLayoutManager的经理实例){
return((GridLayoutManager)mgr.getSpanCount();
}else if(StaggedGridLayoutManager的经理实例){
return((StaggedGridLayoutManager)mgr.getSpanCount();
}else if(LinearLayoutManager的经理实例){
返回1;
}
返回-1;
}
@抑制警告(“全部”)
受保护的int getItemSpanSize(RecyclerView父级,int childIndex){
RecycleView.LayoutManager mgr=parent.getLayoutManager();
if(GridLayoutManager的经理实例){
return((GridLayoutManager)mgr.getSpanSizeLookup().getSpanSize(childIndex);
}else if(StaggedGridLayoutManager的经理实例){
返回1;
}else if(LinearLayoutManager的经理实例){
返回1;
}
返回-1;
}
@抑制警告(“全部”)
受保护的int getItemSpanIndex(RecyclerView父级,int childIndex){
RecycleView.LayoutManager mgr=parent.getLayoutManager();
if(GridLayoutManager的经理实例){
return((GridLayoutManager)mgr.getSpanSizeLookup().getSpanIndex(childIndex,spanCount);
}else if(StaggedGridLayoutManager的经理实例){
返回子索引%spanCount;
}else if(LinearLayoutManager的经理实例){
返回0;
}
返回-1;
}
@抑制警告(“全部”)
受保护的int getOrientation(RecyclerView父级){
RecycleView.LayoutManager mgr=parent.getLayoutManager();
if(直线布局管理器的经理实例){
return((LinearLayoutManager)mgr.getOrientation();
}else if(GridLayoutManager的经理实例){
return((GridLayoutManager)mgr.getOrientation();
}else if(StaggedGridLayoutManager的经理实例){
return((StaggedGridLayoutManager)mgr.getOrientation();
}
垂直返回;
}
受保护的布尔值isBottomEdge(RecyclerView父项、int-childCount、int-childIndex、int-itemSpanSize、int-spanIndex){
如果(方向==垂直){
返回isLastItemEdgeValid((childIndex>=childCount-spanCount),parent,childCount,childIndex,spanIndex);
}否则{
返回(spanIndex+itemSpanSize)=spanCount;
}
}
受保护的布尔isLastItemEdgeValid(布尔isOneOfLastItems、RecyclerView父项、int childCount、int childIndex、int spanIndex){
整合式SpanRemain
<RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="8dp"
    android:clipToPadding="false" />
for (int i = 0; i < childCount; i++)
for (int i = 0; i < childCount - 1; i++)
recyclerView.addItemDecoration(MemberItemDecoration())

public class MemberItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        // only for the last one
        if (parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = 50/* set your margin here */;
        }
    }
}
android:paddingBottom="127px"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"  
<android.support.v7.widget.RecyclerView
        android:id="@+id/library_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="160px"
        android:layout_marginEnd="160px"
        tools:listitem="@layout/library_list_item" />  
/**
 * A {@link RecyclerView.ItemDecoration} that will add a bottom offset to the last item in the
 * RecyclerView it is added to.
 *
 * @hide
 */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class BottomOffsetDecoration extends RecyclerView.ItemDecoration {
    private int mBottomOffset;

    public BottomOffsetDecoration(int bottomOffset) {
        mBottomOffset = bottomOffset;
    }

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

        RecyclerView.Adapter adapter = parent.getAdapter();

        if (adapter == null || adapter.getItemCount() == 0) {
            return;
        }

        if (parent.getLayoutManager() instanceof GridLayoutManager) {
            if (GridLayoutManagerUtils.isOnLastRow(view, parent)) {
                outRect.bottom = mBottomOffset;
            }
        } else if (parent.getChildAdapterPosition(view) == adapter.getItemCount() - 1) {
            // Only set the offset for the last item.
            outRect.bottom = mBottomOffset;
        } else {
            outRect.bottom = 0;
        }
    }

    /** Sets the value to use for the bottom offset. */
    public void setBottomOffset(int bottomOffset) {
        mBottomOffset = bottomOffset;
    }

    /** Returns the set bottom offset. If none has been set, then 0 will be returned. */
    public int getBottomOffset() {
        return mBottomOffset;
    }
}
/**
 * Utility class that helps navigating in GridLayoutManager.
 *
 * <p>Assumes parameter {@code RecyclerView} uses {@link GridLayoutManager}.
 *
 * <p>Assumes the orientation of {@code GridLayoutManager} is vertical.
 *
 * @hide
 */
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class GridLayoutManagerUtils {
    private GridLayoutManagerUtils() {}

    /**
     * Returns the number of items in the first row of a RecyclerView that has a
     * {@link GridLayoutManager} as its {@code LayoutManager}.
     *
     * @param recyclerView RecyclerView that uses GridLayoutManager as LayoutManager.
     * @return number of items in the first row in {@code RecyclerView}.
     */
    public static int getFirstRowItemCount(RecyclerView recyclerView) {
        GridLayoutManager manager = (GridLayoutManager) recyclerView.getLayoutManager();
        int itemCount = recyclerView.getAdapter().getItemCount();
        int spanCount = manager.getSpanCount();

        int spanSum = 0;
        int numOfItems = 0;
        while (numOfItems < itemCount && spanSum < spanCount) {
            spanSum += manager.getSpanSizeLookup().getSpanSize(numOfItems);
            numOfItems++;
        }
        return numOfItems;
    }

    /**
     * Returns the span index of an item.
     */
    public static int getSpanIndex(View item) {
        GridLayoutManager.LayoutParams layoutParams =
                ((GridLayoutManager.LayoutParams) item.getLayoutParams());
        return layoutParams.getSpanIndex();
    }

    /**
     * Returns whether or not the given view is on the last row of a {@code RecyclerView} with a
     * {@link GridLayoutManager}.
     *
     * @param view The view to inspect.
     * @param parent {@link RecyclerView} that contains the given view.
     * @return {@code true} if the given view is on the last row of the {@code RecyclerView}.
     */
    public static boolean isOnLastRow(View view, RecyclerView parent) {
        return getLastItemPositionOnSameRow(view, parent) == parent.getAdapter().getItemCount() - 1;
    }

    /**
     * Returns the position of the last item that is on the same row as input {@code view}.
     *
     * @param view The view to inspect.
     * @param parent {@link RecyclerView} that contains the given view.
     */
    public static int getLastItemPositionOnSameRow(View view, RecyclerView parent) {
        GridLayoutManager layoutManager = ((GridLayoutManager) parent.getLayoutManager());

        GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
        int spanCount = layoutManager.getSpanCount();
        int lastItemPosition = parent.getAdapter().getItemCount() - 1;

        int currentChildPosition = parent.getChildAdapterPosition(view);
        int spanSum = getSpanIndex(view) + spanSizeLookup.getSpanSize(currentChildPosition);
        // Iterate to the end of the row starting from the current child position.
        while (currentChildPosition <= lastItemPosition && spanSum <= spanCount) {
            spanSum += spanSizeLookup.getSpanSize(currentChildPosition + 1);
            if (spanSum > spanCount) {
                return currentChildPosition;
            }
            currentChildPosition++;
        }
        return lastItemPosition;
    }
}
//https://androidx.de/androidx/car/widget/itemdecorators/BottomOffsetDecoration.html
class BottomOffsetDecoration(private val mBottomOffset: Int, private val layoutManagerType: LayoutManagerType) : ItemDecoration() {
    enum class LayoutManagerType {
        GRID_LAYOUT_MANAGER, LINEAR_LAYOUT_MANAGER
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)
        when (layoutManagerType) {
            LayoutManagerType.LINEAR_LAYOUT_MANAGER -> {
                val position = parent.getChildAdapterPosition(view)
                outRect.bottom =
                        if (state.itemCount <= 0 || position != state.itemCount - 1)
                            0
                        else
                            mBottomOffset
            }
            LayoutManagerType.GRID_LAYOUT_MANAGER -> {
                val adapter = parent.adapter
                outRect.bottom =
                        if (adapter == null || adapter.itemCount == 0 || GridLayoutManagerUtils.isOnLastRow(view, parent))
                            0
                        else
                            mBottomOffset
            }
        }
    }
}
//https://androidx.de/androidx/car/util/GridLayoutManagerUtils.html
/**
 * Utility class that helps navigating in GridLayoutManager.
 *
 *
 * Assumes parameter `RecyclerView` uses [GridLayoutManager].
 *
 *
 * Assumes the orientation of `GridLayoutManager` is vertical.
 */
object GridLayoutManagerUtils {
    /**
     * Returns whether or not the given view is on the last row of a `RecyclerView` with a
     * [GridLayoutManager].
     *
     * @param view   The view to inspect.
     * @param parent [RecyclerView] that contains the given view.
     * @return `true` if the given view is on the last row of the `RecyclerView`.
     */
    fun isOnLastRow(view: View, parent: RecyclerView): Boolean {
        return getLastItemPositionOnSameRow(view, parent) == parent.adapter!!.itemCount - 1
    }

    /**
     * Returns the position of the last item that is on the same row as input `view`.
     *
     * @param view   The view to inspect.
     * @param parent [RecyclerView] that contains the given view.
     */
    private fun getLastItemPositionOnSameRow(view: View, parent: RecyclerView): Int {
        val layoutManager = parent.layoutManager as GridLayoutManager
        val spanSizeLookup = layoutManager.spanSizeLookup
        val spanCount = layoutManager.spanCount
        val lastItemPosition = parent.adapter!!.itemCount - 1
        var currentChildPosition = parent.getChildAdapterPosition(view)
        val itemSpanIndex = (view.layoutParams as GridLayoutManager.LayoutParams).spanIndex
        var spanSum = itemSpanIndex + spanSizeLookup.getSpanSize(currentChildPosition)
        // Iterate to the end of the row starting from the current child position.
        while (currentChildPosition <= lastItemPosition && spanSum <= spanCount) {
            spanSum += spanSizeLookup.getSpanSize(currentChildPosition + 1)
            if (spanSum > spanCount)
                return currentChildPosition
            ++currentChildPosition
        }
        return lastItemPosition
    }
}