Android는 gridlayoutmanager를 사용하여 recyclerview의 마지막 요소 아래에 간격을 추가합니다.


84

나는 마지막 요소 행 아래에 간격을 추가하려고하고 RecyclerViewGridLayoutManager. 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왼쪽 간격을 기준으로 요소의 높이를 변경 한다고 생각합니다 . 이를 달성하는 올바른 방법은 무엇입니까?

이것은 LinearLayoutManager. GridLayoutManager문제의 경우에만 .

FAB아래쪽에있는 항목이 있고 위쪽으로 스크롤 FAB하여 볼 수 있도록 마지막 행에 항목이 필요한 경우 매우 유용합니다 .

답변:


11

이 문제에 대한 해결책은 GridLayoutManager의 SpanSizeLookup을 덮어 쓰는 것입니다.

RecylerView를 팽창시키는 Activity 또는 Fragment에서 GridlayoutManager를 변경해야합니다.

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

436

패딩을 추가하고 android:clipToPadding="false"

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

이 멋진 답변 덕분에 !


2
이것은 나를 위해 해냈습니다. 재활용 기에서 항목을 균등하게 배치해야 할 때 빠르고 쉬운 솔루션입니다. 감사.
Empty2k12

3
이것이 바로 제가 찾던 것입니다! 감사!
마리아노 Zorrilla

1
위대하고 간단한 솔루션. 감사!
Mikhail

1
이것은 훌륭하지만 페이딩 가장자리를 엉망으로 만듭니다. 예 : android : requiresFadingEdge = "vertical"또는 recyclerView.setVerticalFadingEdgeEnabled (true);
Stephan Henningsen 2016 년

6
이것은 리사이클 러 뷰의 스크롤바를 맨 아래로 확장하지 않습니다. 편집 :이 추가를 방지하기 위해android:scrollbarStyle="outsideOverlay"
Sebastian

8

마지막 항목의 경우에만 하단 여백에 Recycler View의 장식을 사용해야합니다.

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 */;
        }
    }
}

6

비슷한 문제가 발생하여 스택 오버플로의 다른 스레드에 응답했습니다. 이 페이지를 방문하는 다른 사람들을 돕기 위해 여기에 다시 게시하겠습니다.
다른 모든 회신을 읽은 후 recyclerview에 대한 레이아웃 xml의 변경 사항이 예상대로 내 recycler보기에서 작동 함을 발견했습니다.

        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" />  

전후 효과에 대해서는 androidblog.us : Android Recylerview 끝에 공간 추가 링크를 참조하십시오.
어떻게 작동하는지 알려주십시오.

데이비드


3

아래 코드를 사용하여 그리드보기에서 첫 번째 및 마지막 행을 감지하고 이에 따라 상단 및 하단 오프셋을 설정할 수 있습니다.

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

2

할 수있는 일은 recyclerview에 빈 바닥 글을 추가하는 것입니다. 패딩은 바닥 글의 크기입니다.

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

이것은 좋은 선택입니다. 그러나 내 문제는 이미 recyclerview의 항목 위치를 기반으로 작동하는 사용자 정의 ItemDecoration을 사용하고 있으며 간격 및 구분선 등을 어디에 둘 것인지 결정합니다. 이제이 간격을 추가 요소로 추가하면 ItemDecoration이 요소로 계산하고 간격에 구분선도 추가합니다. 그래서 누군가가 문제를 해결하기 위해 사용자 정의 ItemDecoration을 사용하려고 시도했다면 생각했습니다.
pratsJ

또한 귀하의 방법은 LinearLayoutManager의 경우 또는 GridLayoutManager의 경우 그리드의 맨 아래 행이 가득 찬 경우 잘 작동합니다. 그렇지 않으면 공간은 전체 그리드의 맨 아래가 아닌 빈 요소 공간의 그리드로 이동합니다.
pratsJ

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

나는 내 원래의 대답에서 편집 복사 여기에 동일한 간격에 대한 사실이지만이 같은 개념입니다.


0

소스 코드에서 DividerItemDecoration.java를 예로 들어

for (int i = 0; i < childCount; i++)

for (int i = 0; i < childCount - 1; i++)

drawVertical () 및 drawHorizontal ()

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.