/** * Computes the offsets based on the vertical and horizontal spacing values. The spacing * computation has to ensure that the lane sizes are the same after applying the offsets. This * means we have to shift the spacing unevenly across items depending on their position in the * layout. */ public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { final BaseLayoutManager lm = (BaseLayoutManager) parent.getLayoutManager(); lm.getLaneForPosition(mTempLaneInfo, itemPosition, TwoWayLayoutManager.Direction.END); final int lane = mTempLaneInfo.startLane; final int laneSpan = lm.getLaneSpanForPosition(itemPosition); final int laneCount = lm.getLanes().getCount(); final int itemCount = parent.getAdapter().getItemCount(); final boolean isVertical = lm.isVertical(); final boolean firstLane = (lane == 0); final boolean secondLane = isSecondLane(lm, itemPosition, lane); final boolean lastLane = (lane + laneSpan == laneCount); final boolean beforeLastLane = (lane + laneSpan == laneCount - 1); final int laneSpacing = (isVertical ? mHorizontalSpacing : mVerticalSpacing); final int laneOffsetStart; final int laneOffsetEnd; if (firstLane) { laneOffsetStart = 0; } else if (lastLane && !secondLane) { laneOffsetStart = (int) (laneSpacing * 0.75); } else if (secondLane && !lastLane) { laneOffsetStart = (int) (laneSpacing * 0.25); } else { laneOffsetStart = (int) (laneSpacing * 0.5); } if (lastLane) { laneOffsetEnd = 0; } else if (firstLane && !beforeLastLane) { laneOffsetEnd = (int) (laneSpacing * 0.75); } else if (beforeLastLane && !firstLane) { laneOffsetEnd = (int) (laneSpacing * 0.25); } else { laneOffsetEnd = (int) (laneSpacing * 0.5); } final boolean isFirstInLane = isFirstChildInLane(lm, itemPosition); final boolean isLastInLane = !mAddSpacingAtEnd && isLastChildInLane(lm, itemPosition, itemCount); if (isVertical) { outRect.left = laneOffsetStart; outRect.top = (isFirstInLane ? 0 : mVerticalSpacing / 2); outRect.right = laneOffsetEnd; outRect.bottom = (isLastInLane ? 0 : mVerticalSpacing / 2); } else { outRect.left = (isFirstInLane ? 0 : mHorizontalSpacing / 2); outRect.top = laneOffsetStart; outRect.right = (isLastInLane ? 0 : mHorizontalSpacing / 2); outRect.bottom = laneOffsetEnd; } }
/** Checks whether the given position is placed at the start of a layout lane. */ private static boolean isFirstChildInLane(BaseLayoutManager lm, int itemPosition) { final int laneCount = lm.getLanes().getCount(); if (itemPosition >= laneCount) { return false; } int count = 0; for (int i = 0; i < itemPosition; i++) { count += lm.getLaneSpanForPosition(i); if (count >= laneCount) { return false; } } return true; }
/** Checks whether the given position is placed at the end of a layout lane. */ private static boolean isLastChildInLane(BaseLayoutManager lm, int itemPosition, int itemCount) { final int laneCount = lm.getLanes().getCount(); if (itemPosition < itemCount - laneCount) { return false; } // TODO: Figure out a robust way to compute this for layouts // that are dynamically placed and might span multiple lanes. return !(lm instanceof SpannableGridLayoutManager || lm instanceof StaggeredGridLayoutManager); }
/** * Checks whether the given position is placed just after the item in the first lane of the layout * taking items spans into account. */ private boolean isSecondLane(BaseLayoutManager lm, int itemPosition, int lane) { if (lane == 0 || itemPosition == 0) { return false; } int previousLane = Lanes.NO_LANE; int previousPosition = itemPosition - 1; while (previousPosition >= 0) { lm.getLaneForPosition(mTempLaneInfo, previousPosition, TwoWayLayoutManager.Direction.END); previousLane = mTempLaneInfo.startLane; if (previousLane != lane) { break; } previousPosition--; } final int previousLaneSpan = lm.getLaneSpanForPosition(previousPosition); return previousLane == 0 && (lane == previousLane + previousLaneSpan); }