package com.marshalchen.common.uimodule.twowayview.widget;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.marshalchen.common.uimodule.twowayview.TwoWayLayoutManager.Direction;
import com.marshalchen.common.uimodule.twowayview.widget.Lanes.LaneInfo;
/**
* Core logic for applying item vertical and horizontal spacings via item
* offsets. Account for the item lane positions to only apply spacings within
* the layout.
*/
class ItemSpacingOffsets {
private final int mVerticalSpacing;
private final int mHorizontalSpacing;
private boolean mAddSpacingAtEnd;
private final LaneInfo mTempLaneInfo = new LaneInfo();
public ItemSpacingOffsets(int verticalSpacing, int horizontalSpacing) {
if (verticalSpacing < 0 || horizontalSpacing < 0) {
throw new IllegalArgumentException("Spacings should be equal or greater than 0");
}
mVerticalSpacing = verticalSpacing;
mHorizontalSpacing = horizontalSpacing;
}
/**
* 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, Direction.END);
previousLane = mTempLaneInfo.startLane;
if (previousLane != lane) {
break;
}
previousPosition--;
}
final int previousLaneSpan = lm.getLaneSpanForPosition(previousPosition);
if (previousLane == 0) {
return (lane == previousLane + previousLaneSpan);
}
return false;
}
/**
* 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.
if (lm instanceof SpannableGridLayoutManager ||
lm instanceof StaggeredGridLayoutManager) {
return false;
}
return true;
}
public void setAddSpacingAtEnd(boolean spacingAtEnd) {
mAddSpacingAtEnd = spacingAtEnd;
}
/**
* 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, 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;
}
}
}