Skip to content

Commit

Permalink
Add fill to end for grid layout.
Browse files Browse the repository at this point in the history
  • Loading branch information
TonicArtos committed Feb 23, 2015
1 parent 04c4f1a commit 579a591
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,66 @@ public FillResult fill(LayoutState state, SectionData section) {
@Override
public int fillToEnd(int leadingEdge, int markerLine, int anchorPosition, SectionData2 sd,
LayoutState state) {
return 0;
final int itemCount = state.recyclerState.getItemCount();
if (anchorPosition >= itemCount) {
return markerLine;
}

LayoutState.View anchor = state.getView(anchorPosition);
if (anchor.getLayoutParams().getTestedFirstPosition() != sd.firstPosition) {
state.cacheView(anchorPosition, anchor.view);
return markerLine;
}

final int firstContentPosition = sd.hasHeader ? sd.firstPosition + 1 : sd.firstPosition;

// Ensure the anchor is the first item in the row.
final int col = (anchorPosition - firstContentPosition) % mNumColumns;
for (int i = 1; i <= col; i++) {
// Detach and scrap attached items in this row, so we can re-lay them again. The last
// child view in the index can be the header so we just skip past it if it last.
for (int j = 1; j <= mLayoutManager.getChildCount(); j++) {
View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() - j);
if (mLayoutManager.getPosition(child) == anchorPosition - i) {
markerLine = mLayoutManager.getDecoratedTop(child);
mLayoutManager.detachAndScrapViewAt(j, state.recycler);
break;
}

LayoutManager.LayoutParams params = (LayoutManager.LayoutParams) child
.getLayoutParams();
if (params.getTestedFirstPosition() != sd.firstPosition) {
break;
}
}
}
anchorPosition = anchorPosition - col;

// Lay out rows to end.
for (int i = anchorPosition; i < itemCount; i += mNumColumns) {
if (markerLine >= leadingEdge) {
break;
}

LayoutState.View view = state.getView(i);
if (view.getLayoutParams().getTestedFirstPosition() != sd.firstPosition) {
state.cacheView(i, view.view);
break;
}

int rowHeight = fillRow(markerLine, i, sd, state);
markerLine += rowHeight;
}

return markerLine;
}

@Override
public int finishFillToEnd(int leadingEdge, View anchor, SectionData2 sd, LayoutState state) {
return 0;
final int anchorPosition = mLayoutManager.getPosition(anchor);
final int markerLine = getLowestEdge(sd.firstPosition, leadingEdge);

return fillToEnd(leadingEdge, markerLine, anchorPosition + 1, sd, state);
}

@Override
Expand All @@ -73,6 +127,14 @@ public int getAnchorPosition(LayoutState state, SectionData section, int positio
return position - ((position - firstPosition) % mNumColumns);
}

public GridSectionLayoutManager init(SectionData2 sd) {
super.init(sd);

calculateColumnWidthValues(sd);

return this;
}

@Override
public int getHighestEdge(int sectionFirstPosition, int startEdge) {
// Look from start to find children that are the highest.
Expand Down Expand Up @@ -122,6 +184,46 @@ public int getLowestEdge(int sectionFirstPosition, int endEdge) {
return bottomMostEdge;
}

/**
* Fill a row.
*
* @param markerLine Line indicating the top edge of the row.
* @param anchorPosition Position of the first view in the row.
* @param sd Section data.
* @param state Layout state.
* @return The height of the new row.
*/
public int fillRow(int markerLine, int anchorPosition, SectionData2 sd, LayoutState state) {
int rowHeight = 0;
LayoutState.View[] views = new LayoutState.View[mNumColumns];
for (int i = 0; i < mNumColumns; i++) {
final int position = anchorPosition + i;
if (position >= state.recyclerState.getItemCount()) {
break;
}

LayoutState.View view = state.getView(position);
if (view.getLayoutParams().getTestedFirstPosition() != sd.firstPosition) {
state.cacheView(position, view.view);
break;
}

measureChild(view, sd);
rowHeight = Math.max(rowHeight, mLayoutManager.getDecoratedMeasuredHeight(view.view));
views[i] = view;
}

for (int i = 0; i < mNumColumns; i++) {
if (views[i] == null) {
break;
}
layoutChild(views[i], markerLine, i, rowHeight, sd, state);
addView(views[i], i + anchorPosition, LayoutManager.Direction.END, state);
}

return rowHeight;
}

public void setColumnMinimumWidth(int minimumWidth) {
mMinimumWidth = minimumWidth;
mColumnsSpecified = false;
Expand All @@ -148,6 +250,26 @@ private int addView(LayoutState state, LayoutState.View child, int position,
return addIndex;
}

private void calculateColumnWidthValues(SectionData2 section) {
int availableWidth = mLayoutManager.getWidth() - section.marginStart - section.marginEnd;
if (!mColumnsSpecified) {
if (mMinimumWidth <= 0) {
mMinimumWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
mContext.getResources().getDisplayMetrics());
}
mNumColumns = availableWidth / Math.abs(mMinimumWidth);
}
if (mNumColumns < 1) {
mNumColumns = 1;
}
mColumnWidth = availableWidth / mNumColumns;
if (mColumnWidth == 0) {
Log.e("GridSection",
"Too many columns (" + mNumColumns + ") for available width" + availableWidth
+ ".");
}
}

private void calculateColumnWidthValues(SectionData section) {
int availableWidth = mLayoutManager.getWidth()
- section.getContentMarginStart() - section.getContentMarginEnd();
Expand Down Expand Up @@ -263,7 +385,7 @@ private AddData fillRow(LayoutState state, SectionData section, LayoutState.View
rowHeight = height > rowHeight ? height : rowHeight;
} else {
state.recycleView(next);
for (;i < mNumColumns; i++) {
for (; i < mNumColumns; i++) {
rowViews[i] = null;
}
break;
Expand Down Expand Up @@ -459,6 +581,34 @@ private int getViewSpan(LayoutState state, SectionData section, FillResult fillR
return viewSpan;
}

/**
* Layout out a view for the given column in a row. Views that have a height param of
* MATCH_PARENT are fixed to the height of the row.
*
* @param child View to lay out.
* @param markerLine Line indicating the top edge of the row.
* @param col Column view is being placed into.
* @param rowHeight Height of the row.
* @param sd Section data.
* @param state Layout state.
*/
private void layoutChild(LayoutState.View child, int markerLine, int col, int rowHeight,
SectionData2 sd, LayoutState state) {
final int height;
if (child.getLayoutParams().height == LayoutManager.LayoutParams.MATCH_PARENT) {
height = rowHeight;
} else {
height = mLayoutManager.getDecoratedMeasuredHeight(child.view);
}
final int width = mLayoutManager.getDecoratedMeasuredWidth(child.view);

final int bottom = markerLine + height;
final int left = (state.isLTR ? sd.marginStart : sd.marginEnd) + col * mColumnWidth;
final int right = left + width;

mLayoutManager.layoutDecorated(child.view, left, markerLine, right, bottom);
}

private void layoutChild(LayoutState.View child, SectionData section, LayoutState state,
int col, int top) {
if (child.wasCached) {
Expand All @@ -475,6 +625,19 @@ private void layoutChild(LayoutState.View child, SectionData section, LayoutStat
mLayoutManager.layoutDecorated(child.view, left, top, right, bottom);
}

/**
* Measure view. A view is given an area as wide as a single column with an undefined height.
*
* @param child View to measure.
* @param sd Section data.
*/
private void measureChild(LayoutState.View child, SectionData2 sd) {
int widthOtherColumns = (mNumColumns - 1) * mColumnWidth;
mLayoutManager.measureChildWithMargins(child.view,
sd.marginStart + sd.marginEnd + widthOtherColumns,
0);
}

private void measureChild(SectionData section, LayoutState.View child) {
if (child.wasCached) {
return;
Expand Down
42 changes: 17 additions & 25 deletions library/src/main/java/com/tonicartos/superslim/LayoutManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ private int fillToEnd(int leadingEdge, LayoutState state) {
final SectionData2 sd = new SectionData2(this, first);

final SectionLayoutManager slm = getSectionLayoutManager(sd.sectionManager);
int markerLine = slm.finishFillToEnd(leadingEdge, anchor, sd, state);
int markerLine = slm.init(sd).finishFillToEnd(leadingEdge, anchor, sd, state);
Log.d("finishFillToEnd", "markerLine " + markerLine);

markerLine = updateHeaderForEnd(markerLine, sd);
Expand Down Expand Up @@ -318,9 +318,7 @@ private int fillNextSectionToEnd(int leadingEdge, int markerLine, LayoutState st
SectionData2 sd = new SectionData2(this, header.view);
if (sd.hasHeader) {
Log.d("fillNextSectionToEnd", "found header");
if (!header.wasCached) {
measureHeader(header);
}
measureHeader(header);
sd = new SectionData2(this, header.view);
markerLine = layoutHeaderTowardsEnd(header.view, markerLine, sd, state);
Log.d("fillNextSectionToEnd", "markerLine " + markerLine);
Expand All @@ -332,13 +330,16 @@ private int fillNextSectionToEnd(int leadingEdge, int markerLine, LayoutState st
if (anchorPosition < state.recyclerState.getItemCount()) {
Log.d("fillNextSectionToEnd", "fill out section");
SectionLayoutManager slm = getSectionLayoutManager(sd.sectionManager);
markerLine = slm.fillToEnd(leadingEdge, markerLine, anchorPosition, sd, state);
markerLine = slm.init(sd).fillToEnd(leadingEdge, markerLine, anchorPosition, sd, state);
Log.d("fillNextSectionToEnd", "markerLine " + markerLine);
}

if (sd.hasHeader) {
Log.d("fillNextSectionToEnd", "attaching header");
addView(header.view);
if (header.wasCached) {
state.decacheView(sd.firstPosition);
}
markerLine = Math.max(getDecoratedBottom(header.view), markerLine);
Log.d("fillNextSectionToEnd", "markerLine " + markerLine);
}
Expand Down Expand Up @@ -626,10 +627,6 @@ public void registerSectionLayoutManager(int id, SectionLayoutManager manager) {
}

void measureHeader(LayoutState.View header) {
if (header.wasCached) {
return;
}

// Width to leave for the mSection to which this header belongs. Only applies if the
// header is being laid out adjacent to the mSection.
int unavailableWidth = 0;
Expand Down Expand Up @@ -732,38 +729,33 @@ private FillResult fillSections(LayoutState layoutState, FillResult fillState,
/**
* Find header or, if it cannot be found, the first view for a section.
*
* @param sectionFirstPosition Section to look for header inside of. Search is expected to start
* @param sfp Section to look for header inside of. Search is expected to start
* inside the section so it must be at the matching end specified by
* the direction.
* @param itemCount Current number of items in adapter.
* @param direction Direction to look in. Direction.END means to look from the start
* to the end.
* @return Null if no header or first item found, otherwise the found view.
*/
private View findAttachedHeaderOrFirstViewForSection(final int sectionFirstPosition,
final int itemCount,
final Direction direction) {
int position = direction == Direction.END ? 0 : getChildCount() - 1;
int step = direction == Direction.END ? 1 : -1;
for (; 0 <= position && position < itemCount; position += step) {
View child = getChildAt(position);
if (child == null) {
continue;
private View findAttachedHeaderOrFirstViewForSection(final int sfp, final Direction direction) {
int childIndex = direction == Direction.START ? 0 : getChildCount() - 1;
int step = direction == Direction.START ? 1 : -1;
for (; 0 <= childIndex && childIndex < getChildCount(); childIndex += step) {
View child = getChildAt(childIndex);

if (getPosition(child) == sfp) {
return child;
}
LayoutParams params = (LayoutParams) child.getLayoutParams();
if (params.getTestedFirstPosition() != sectionFirstPosition) {
if (params.getTestedFirstPosition() != sfp) {
break;
} else {
return child;
}
}

return null;
}

private View getHeaderOrFirstViewForSection(int sfp, Direction direction, LayoutState state) {
View view = findAttachedHeaderOrFirstViewForSection(sfp,
state.recyclerState.getItemCount(), direction);
View view = findAttachedHeaderOrFirstViewForSection(sfp, direction);
if (view == null) {
LayoutState.View stateView = state.getView(sfp);
view = stateView.view;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tonicartos.superslim;

import android.util.Log;
import android.view.View;

public class LinearSectionLayoutManager extends SectionLayoutManager {
Expand Down Expand Up @@ -34,21 +33,20 @@ public int fillToEnd(int leadingEdge, int markerLine, int anchorPosition, Sectio
final int itemCount = state.recyclerState.getItemCount();

for (int i = anchorPosition; i < itemCount; i++) {
if (markerLine >= leadingEdge) {
break;
}

LayoutState.View next = state.getView(i);
LayoutManager.LayoutParams params = next.getLayoutParams();
if (params.getTestedFirstPosition() != sd.firstPosition) {
state.recycleView(next);
state.cacheView(i, next.view);
break;
}

measureChild(next, sd);
markerLine = layoutChild(next, markerLine, LayoutManager.Direction.END, sd, state);
addView(next, i, LayoutManager.Direction.END, state);
Log.d("DEBUG", "Add content " + i);

if (markerLine >= leadingEdge) {
break;
}
}

return markerLine;
Expand Down Expand Up @@ -100,24 +98,6 @@ public int getLowestEdge(int sectionFirstPosition, int endEdge) {
return endEdge;
}

private int addView(LayoutState.View child, int position, LayoutManager.Direction direction,
LayoutState state) {
int addIndex;
if (direction == LayoutManager.Direction.START) {
addIndex = 0;
} else {
addIndex = mLayoutManager.getChildCount();
}

if (child.wasCached) {
mLayoutManager.attachView(child.view, addIndex);
state.decacheView(position);
} else {
mLayoutManager.addView(child.view, addIndex);
}

return addIndex;
}

/**
* Work out by how much the header overlaps with the displayed content.
Expand Down
Loading

0 comments on commit 579a591

Please sign in to comment.