Android додає інтервал нижче останнього елемента в reciclerview за допомогою gridlayoutmanager


84

Я намагаюся додати інтервал нижче останнього елемента рядка в RecyclerViewс GridLayoutManager. 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висоти елементів змінюються залежно від інтервалу ліворуч. Який правильний спосіб цього досягти?

Це буде працювати правильно для a LinearLayoutManager. На всякий випадок це GridLayoutManagerпроблематично.

Це дуже корисно, якщо у вас є FABвнизу і вам потрібні елементи в останньому рядку для прокрутки вище, FABщоб їх було видно.

Відповіді:


11

Вирішення цієї проблеми полягає у перекритті SpanSizeLookup GridLayoutManager.

Вам потрібно внести зміни до GridlayoutManager в Activity або Fragment, де ви нагнітаєте 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);
}

437

Просто додайте відступ і встановіть android:clipToPadding="false"

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

Завдяки цій чудовій відповіді !


2
Це зробило це для мене. Швидке і просте рішення, коли вам потрібно однаково розмістити предмети в утилізаторі. Дякую.
Empty2k12

3
Це було саме те, що я шукав! Дякую!
Маріано Зоррілла

1
Чудове і просте рішення. Дякую!
Михайло

1
Це чудово, але воно псує зникаючі краї, наприклад, android: requiresFadingEdge = "vertical" або recyclerView.setVerticalFadingEdgeEnabled (true);
Stephan Henningsen,

6
Це не розширює смугу прокрутки подання утилізатора до самого низу. Редагувати: щоб запобігти цьому доданнюandroid:scrollbarStyle="outsideOverlay"
Себастьян

8

Для нижнього поля слід використовувати прикрасу в поданні Recycler для останнього елемента

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

У мене була подібна проблема і я відповів на інший потік у переповненні стека. Щоб допомогти іншим, хто потрапив на цю сторінку, я розміщу її тут.
Прочитавши всі інші відповіді, і я виявив, що зміни в макеті xml for recyclerview спрацювали для мого подання переробника, як очікувалося:

        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

Що ви можете зробити, це додати порожній нижній колонтитул до вашого перегляду утилізаторів. Ваш відступ буде розміром з нижній колонтитул.

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

Це хороший варіант. Однак моя проблема з цим полягає в тому, що я вже використовую спеціальний 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++)

in drawVertical () та drawHorizontal ()

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.