/// <summary> /// Locate the visible view which has the latest adapter position. /// </summary> /// <param name="sectionFirstPosition"> Section id. </param> /// <returns> View. </returns> public View FindLastVisibleView(int sectionFirstPosition) { int lookAt = mLayoutManager.ChildCount - 1; View candidate = null; while (true) { if (lookAt < 0) { return(candidate); } View view = mLayoutManager.GetChildAt(lookAt); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)view.LayoutParameters; if (sectionFirstPosition == lp.FirstPosition) { if (!lp.isHeader) { return(view); } else { candidate = view; } } else { // Skipped past section. return(candidate); } lookAt -= 1; } }
public override int FillToEnd(int leadingEdge, int markerLine, int anchorPosition, SectionData sd, LayoutState state) { int itemCount = state.recyclerState.ItemCount; for (int i = anchorPosition; i < itemCount; i++) { if (markerLine >= leadingEdge) { break; } LayoutState.View next = state.GetView(i); LayoutManager.LayoutParams lp = next.LayoutParams; if (lp.FirstPosition != sd.firstPosition) { 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); } return(markerLine); }
/// <summary> /// Tell decorators which edges are and external. The default implementation assumes a /// linear list. /// </summary> /// <param name="outRect"> Rect to load with ege states. </param> /// <param name="child"> Child to look at. </param> /// <param name="sectionData"> Section data. </param> /// <param name="state"> State. </param> public void GetEdgeStates(Rect outRect, View child, SectionData sectionData, RecyclerView.State state) { outRect.Left = ItemDecorator.EXTERNAL; outRect.Right = ItemDecorator.EXTERNAL; LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; int position = lp.ViewPosition; outRect.Top = position == sectionData.FirstContentPosition ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; outRect.Bottom = position == sectionData.lastContentPosition ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; }
/// <summary> /// Locate the last view in this section that is completely visible. Will skip headers unless /// they are the only one visible. /// </summary> /// <param name="sectionFirstPosition"> First position of section being queried. </param> /// <returns> Last completely visible item or null. </returns> public View FindLastCompletelyVisibleView(int sectionFirstPosition) { int topEdge = mLayoutManager.ClipToPadding ? mLayoutManager.PaddingTop : 0; int bottomEdge = mLayoutManager.ClipToPadding ? mLayoutManager.Height - mLayoutManager.PaddingBottom : mLayoutManager.Height; int lookAt = mLayoutManager.ChildCount - 1; View candidate = null; while (true) { if (lookAt < 0) { return(candidate); } View view = mLayoutManager.GetChildAt(lookAt); bool topInside = mLayoutManager.GetDecoratedTop(view) >= topEdge; bool bottomInside = mLayoutManager.GetDecoratedBottom(view) <= bottomEdge; LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)view.LayoutParameters; if (sectionFirstPosition == lp.FirstPosition) { if (topInside && bottomInside) { if (!lp.isHeader) { return(view); } else { candidate = view; } } } else if (candidate == null) { sectionFirstPosition = lp.FirstPosition; continue; } else { return(candidate); } lookAt -= 1; } }
private bool assignedTo(SectionData sectionData, LayoutManager.LayoutParams lp) { if (mCheckers.Count == 0) { return(true); } foreach (AssignmentChecker check in mCheckers) { if (check.IsAssigned(sectionData, lp)) { return(true); } } return(false); }
public override void GetItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { resolveLayoutDirection(parent); // Check decorator is assigned to section by sfp or slm. LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)view.LayoutParameters; LayoutManager lm = (LayoutManager)parent.GetLayoutManager(); if (!assignedTo(lm.GetSectionData(lp.FirstPosition, view), lp)) { outRect.Left = 0; outRect.Top = 0; outRect.Right = 0; outRect.Bottom = 0; return; } mSpacing.GetOffsets(outRect, view, lm, state); }
/// <summary> /// Locate the first view in this section that is completely visible. Will skip headers unless /// they are the only one visible. /// </summary> /// <param name="sectionFirstPosition"> First position of section being queried. </param> /// <param name="skipHeader"> Do not include the section header if it has one. </param> /// <returns> First completely visible item or null. </returns> public View FindFirstCompletelyVisibleView(int sectionFirstPosition, bool skipHeader) { int topEdge = mLayoutManager.ClipToPadding ? mLayoutManager.PaddingTop : 0; int bottomEdge = mLayoutManager.ClipToPadding ? mLayoutManager.Height - mLayoutManager.PaddingBottom : mLayoutManager.Height; int lookAt = 0; int childCount = mLayoutManager.ChildCount; View candidate = null; while (true) { if (lookAt >= childCount) { return(candidate); } View view = mLayoutManager.GetChildAt(lookAt); bool topInside = mLayoutManager.GetDecoratedTop(view) >= topEdge; bool bottomInside = mLayoutManager.GetDecoratedBottom(view) <= bottomEdge; LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)view.LayoutParameters; if (sectionFirstPosition == lp.FirstPosition && topInside && bottomInside) { if (!lp.isHeader || !skipHeader) { return(view); } else { candidate = view; } } else { // Skipped past section. return(candidate); } lookAt += 1; } }
/// <summary> /// Find the highest displayed edge of the section. If there is no member found then return the /// default edge instead. /// </summary> /// <param name="sectionFirstPosition"> Section id, position of first item in the section. </param> /// <param name="firstIndex"> Child index to start looking from. </param> /// <param name="defaultEdge"> Default value. </param> /// <returns> Top (attached) edge of the section. </returns> public int GetHighestEdge(int sectionFirstPosition, int firstIndex, int defaultEdge) { // Look from start to find children that are the highest. for (int i = firstIndex; i < mLayoutManager.ChildCount; i++) { View child = mLayoutManager.GetChildAt(i); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; if (lp.FirstPosition != sectionFirstPosition) { break; } if (lp.isHeader) { continue; } // A more interesting layout would have to do something more here. return(mLayoutManager.GetDecoratedTop(child)); } return(defaultEdge); }
/// <summary> /// Find the lowest displayed edge of the section. If there is no member found then return the /// default edge instead. /// </summary> /// <param name="sectionFirstPosition"> Section id, position of first item in the section. </param> /// <param name="lastIndex"> Index to start looking from. Usually the index of the last /// attached view in this section. </param> /// <param name="defaultEdge"> Default value. </param> /// <returns> Lowest (attached) edge of the section. </returns> public int GetLowestEdge(int sectionFirstPosition, int lastIndex, int defaultEdge) { // Look from end to find children that are the lowest. for (int i = lastIndex; i >= 0; i--) { View child = mLayoutManager.GetChildAt(i); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; if (lp.FirstPosition != sectionFirstPosition) { break; } if (lp.isHeader) { continue; } // A more interesting layout would have to do something more here. return(mLayoutManager.GetDecoratedBottom(child)); } return(defaultEdge); }
public void GetEdgeStates(Rect outRect, View child, SectionData sd, RecyclerView.State state) { LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; int position = lp.ViewPosition; int column = (position - sd.FirstContentPosition) % mNumColumns; bool isLtr = mLayoutManager.LayoutDirection == ViewCompat.LayoutDirectionLtr; int ltrColumn = AdjustColumnForLayoutDirection(column, isLtr); outRect.Left = ltrColumn == 0 ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; outRect.Right = ltrColumn == mNumColumns - 1 ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; outRect.Top = position - column == sd.FirstContentPosition ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; // Reset position to left column and add num columns, if < itemcount then not last row. if (sd.LastContentItemFound) { outRect.Bottom = position + (mNumColumns - column) > sd.lastContentPosition ? ItemDecorator.EXTERNAL : ItemDecorator.INTERNAL; } else { outRect.Bottom = ItemDecorator.INTERNAL; } }
public int GetLowestEdge(int sectionFirstPosition, int lastIndex, int defaultEdge) { int bottomMostEdge = 0; int leftPosition = mLayoutManager.Width; bool foundItems = false; // Look from end to find children that are the lowest. for (int i = lastIndex; i >= 0; i--) { View look = mLayoutManager.GetChildAt(i); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)look.LayoutParameters; if (lp.FirstPosition != sectionFirstPosition) { break; } if (lp.isHeader) { continue; } if (look.Left < leftPosition) { leftPosition = look.Left; } else { break; } foundItems = true; bottomMostEdge = Math.Max(bottomMostEdge, mLayoutManager.GetDecoratedBottom(look)); } return(foundItems ? bottomMostEdge : defaultEdge); }
public bool IsAssigned(SectionData sd, LayoutManager.LayoutParams lp) { int kind = lp.isHeader ? HEADER : CONTENT; return(sd.sectionManagerKind == mSlmId && (kind & mTargetKind) != 0); }
public bool IsAssigned(SectionData sd, LayoutManager.LayoutParams lp) { return(lp.ViewPosition == mPosition); }
public override void OnDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { resolveLayoutDirection(parent); LayoutManager lm = (LayoutManager)parent.GetLayoutManager(); for (int i = 0; i < lm.ChildCount; i++) { View child = lm.GetChildAt(i); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; if (!assignedTo(lm.GetSectionData(lp.FirstPosition, child), lp)) { continue; } lm.GetEdgeStates(mEdgeState, child, state); int decorLeft = lm.GetDecoratedLeft(child); int decorTop = lm.GetDecoratedTop(child); int decorRight = lm.GetDecoratedRight(child); int decorBottom = lm.GetDecoratedBottom(child); int childLeft = child.Left; int childTop = child.Top; int childRight = child.Right; int childBottom = child.Bottom; int offsetLeft = childLeft - decorLeft - lp.LeftMargin; int offsetTop = childTop - decorTop - lp.TopMargin; int offsetRight = decorRight - childRight - lp.RightMargin; int offsetBottom = decorBottom - childBottom - lp.BottomMargin; if (mLeft.hasValidDrawableFor(mEdgeState.Left)) { int right = decorLeft + offsetLeft; int left = right - mLeft.drawable.IntrinsicWidth; bool excludeOffsets = (mLeft.drawableFlags & INSET) == INSET; int top = excludeOffsets ? decorTop + offsetTop : decorTop; int bottom = excludeOffsets ? decorBottom - offsetBottom : decorBottom; mLeft.drawable.SetBounds(left, top, right, bottom); mLeft.drawable.Draw(c); } if (mTop.hasValidDrawableFor(mEdgeState.Top)) { int bottom = decorTop + offsetTop; int top = bottom - mTop.drawable.IntrinsicHeight; bool excludeOffsets = (mTop.drawableFlags & INSET) == INSET; int left = excludeOffsets ? decorLeft + offsetLeft : decorLeft; int right = excludeOffsets ? decorRight - offsetRight : decorRight; mTop.drawable.SetBounds(left, top, right, bottom); mTop.drawable.Draw(c); } if (mRight.hasValidDrawableFor(mEdgeState.Right)) { int left = decorRight - offsetRight; int right = left + mRight.drawable.IntrinsicWidth; bool excludeOffsets = (mRight.drawableFlags & INSET) == INSET; int top = excludeOffsets ? decorTop + offsetTop : decorTop; int bottom = excludeOffsets ? decorBottom - offsetBottom : decorBottom; mRight.drawable.SetBounds(left, top, right, bottom); mRight.drawable.Draw(c); } if (mBottom.hasValidDrawableFor(mEdgeState.Bottom)) { int top = decorBottom - offsetBottom; int bottom = top + mBottom.drawable.IntrinsicHeight; bool excludeOffsets = (mBottom.drawableFlags & INSET) == INSET; int left = excludeOffsets ? decorLeft + offsetLeft : decorLeft; int right = excludeOffsets ? decorRight - offsetRight : decorRight; mBottom.drawable.SetBounds(left, top, right, bottom); mBottom.drawable.Draw(c); } } base.OnDrawOver(c, parent, state); }
public override int FillToStart(int leadingEdge, int markerLine, int anchorPosition, SectionData sd, LayoutState state) { int firstContentPosition = sd.hasHeader ? sd.firstPosition + 1 : sd.firstPosition; // Check to see if we have to adjust for minimum section height. We don't if there is an // attached non-header view in this section. bool applyMinHeight = false; for (int i = 0; i < mLayoutManager.ChildCount; i++) { View check = mLayoutManager.GetChildAt(0); LayoutManager.LayoutParams checkParams = (LayoutManager.LayoutParams)check.LayoutParameters; if (checkParams.FirstPosition != sd.firstPosition) { applyMinHeight = true; break; } if (!checkParams.isHeader) { applyMinHeight = false; break; } } // _ _ ^ a b int col = (anchorPosition - firstContentPosition) % mNumColumns; for (int i = 1; i < mNumColumns - 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 = 0; j < mLayoutManager.ChildCount; j++) { View child = mLayoutManager.GetChildAt(j); LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; if (lp.FirstPosition != sd.firstPosition) { break; } if (mLayoutManager.GetPosition(child) == anchorPosition + i) { mLayoutManager.DetachAndScrapViewAt(j, state.recycler); break; } } } // Ensure the anchor is the first item in the row. int columnAnchorPosition = anchorPosition - col; // Work out offset to marker line by measuring rows from the end. If section height is less // than min height, then adjust marker line and then lay out items. int measuredPositionsMarker = -1; int sectionHeight = 0; int minHeightOffset = 0; if (applyMinHeight) { for (int i = columnAnchorPosition; i >= 0; i -= mNumColumns) { LayoutState.View check = state.GetView(i); state.CacheView(i, check.view); LayoutManager.LayoutParams checkParams = check.LayoutParams; if (checkParams.FirstPosition != sd.firstPosition) { break; } int rowHeight = 0; for (int j = 0; j < mNumColumns && i + j <= anchorPosition; j++) { LayoutState.View measure = state.GetView(i + j); state.CacheView(i + j, measure.view); LayoutManager.LayoutParams measureParams = measure.LayoutParams; if (measureParams.FirstPosition != sd.firstPosition) { break; } if (measureParams.isHeader) { continue; } MeasureChild(measure, sd); rowHeight = Math.Max(rowHeight, mLayoutManager.GetDecoratedMeasuredHeight(measure.view)); } sectionHeight += rowHeight; measuredPositionsMarker = i; if (sectionHeight >= sd.minimumHeight) { break; } } if (sectionHeight < sd.minimumHeight) { minHeightOffset = sectionHeight - sd.minimumHeight; markerLine += minHeightOffset; } } // Lay out rows to end. for (int i = columnAnchorPosition; i >= 0; i -= mNumColumns) { if (markerLine - minHeightOffset < leadingEdge) { break; } LayoutState.View rowAnchor = state.GetView(i); state.CacheView(i, rowAnchor.view); LayoutManager.LayoutParams lp = rowAnchor.LayoutParams; if (lp.isHeader || lp.FirstPosition != sd.firstPosition) { break; } bool measureRowItems = !applyMinHeight || i < measuredPositionsMarker; int rowHeight = fillRow(markerLine, i, LayoutManager.Direction.START, measureRowItems, sd, state); markerLine -= rowHeight; } return(markerLine); }
public override int FillToStart(int leadingEdge, int markerLine, int anchorPosition, SectionData sd, LayoutState state) { // Check to see if we have to adjust for minimum section height. We don't if there is an // attached non-header view in this section. bool applyMinHeight = false; for (int i = 0; i < state.recyclerState.ItemCount; i++) { View check = mLayoutManager.GetChildAt(0); if (check == null) { applyMinHeight = false; break; } LayoutManager.LayoutParams checkParams = (LayoutManager.LayoutParams)check.LayoutParameters; if (checkParams.FirstPosition != sd.firstPosition) { applyMinHeight = true; break; } if (!checkParams.isHeader) { applyMinHeight = false; break; } } // Work out offset to marker line by measuring items from the end. If section height is less // than min height, then adjust marker line and then lay out items. int measuredPositionsMarker = -1; int sectionHeight = 0; int minHeightOffset = 0; if (applyMinHeight) { for (int i = anchorPosition; i >= 0; i--) { LayoutState.View measure = state.GetView(i); state.CacheView(i, measure.view); LayoutManager.LayoutParams lp = measure.LayoutParams; if (lp.FirstPosition != sd.firstPosition) { break; } if (lp.isHeader) { continue; } measureChild(measure, sd); sectionHeight += mLayoutManager.GetDecoratedMeasuredHeight(measure.view); measuredPositionsMarker = i; if (sectionHeight >= sd.minimumHeight) { break; } } if (sectionHeight < sd.minimumHeight) { minHeightOffset = sectionHeight - sd.minimumHeight; markerLine += minHeightOffset; } } for (int i = anchorPosition; i >= 0; i--) { if (markerLine - minHeightOffset < leadingEdge) { break; } LayoutState.View next = state.GetView(i); LayoutManager.LayoutParams lp = next.LayoutParams; if (lp.isHeader) { state.CacheView(i, next.view); break; } if (lp.FirstPosition != sd.firstPosition) { state.CacheView(i, next.view); break; } if (!applyMinHeight || i < measuredPositionsMarker) { measureChild(next, sd); } else { state.DecacheView(i); } markerLine = layoutChild(next, markerLine, LayoutManager.Direction.START, sd, state); AddView(next, i, LayoutManager.Direction.START, state); } return(markerLine); }
public override int FillToEnd(int leadingEdge, int markerLine, int anchorPosition, SectionData sd, LayoutState state) { if (markerLine >= leadingEdge) { return(markerLine); } int itemCount = state.recyclerState.ItemCount; if (anchorPosition >= itemCount) { return(markerLine); } LayoutState.View anchor = state.GetView(anchorPosition); state.CacheView(anchorPosition, anchor.view); if (anchor.LayoutParams.FirstPosition != sd.firstPosition) { return(markerLine); } int firstContentPosition = sd.hasHeader ? sd.firstPosition + 1 : sd.firstPosition; // Ensure the anchor is the first item in the row. 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.ChildCount; j++) { View child = mLayoutManager.GetChildAt(mLayoutManager.ChildCount - j); if (mLayoutManager.GetPosition(child) == anchorPosition - i) { markerLine = mLayoutManager.GetDecoratedTop(child); mLayoutManager.DetachAndScrapViewAt(j, state.recycler); break; } LayoutManager.LayoutParams lp = (LayoutManager.LayoutParams)child.LayoutParameters; if (lp.FirstPosition != 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.LayoutParams.FirstPosition != sd.firstPosition) { state.CacheView(i, view.view); break; } int rowHeight = fillRow(markerLine, i, LayoutManager.Direction.END, true, sd, state); markerLine += rowHeight; } return(markerLine); }
public SectionData(LayoutManager lm, View first) { int paddingStart = lm.PaddingStart; int paddingEnd = lm.PaddingEnd; headerParams = (LayoutManager.LayoutParams)first.LayoutParameters; if (headerParams.isHeader) { headerWidth = lm.GetDecoratedMeasuredWidth(first); headerHeight = lm.GetDecoratedMeasuredHeight(first); if (!headerParams.HeaderInline || headerParams.HeaderOverlay) { minimumHeight = headerHeight; } else { minimumHeight = 0; } if (headerParams.headerStartMarginIsAuto) { if (headerParams.HeaderStartAligned && !headerParams.HeaderOverlay) { marginStart = headerWidth; } else { marginStart = 0; } } else { marginStart = headerParams.headerMarginStart; } if (headerParams.headerEndMarginIsAuto) { if (headerParams.HeaderEndAligned && !headerParams.HeaderOverlay) { marginEnd = headerWidth; } else { marginEnd = 0; } } else { marginEnd = headerParams.headerMarginEnd; } } else { minimumHeight = 0; headerHeight = 0; headerWidth = 0; marginStart = headerParams.headerMarginStart; marginEnd = headerParams.headerMarginEnd; } contentEnd = marginEnd + paddingEnd; contentStart = marginStart + paddingStart; hasHeader = headerParams.isHeader; firstPosition = headerParams.FirstPosition; sectionManager = headerParams.sectionManager; sectionManagerKind = headerParams.sectionManagerKind; }
public bool IsAssigned(SectionData sd, LayoutManager.LayoutParams lp) { int kind = lp.isHeader ? HEADER : CONTENT; return(sd.sectionManagerKind == LayoutManager.SECTION_MANAGER_CUSTOM && (kind & mTargetKind) != 0 && TextUtils.Equals(sd.sectionManager, mSlmKey)); }
public LayoutManager.LayoutParams GenerateLayoutParams(LayoutManager.LayoutParams lp) { return(lp); }
public bool IsAssigned(SectionData sd, LayoutManager.LayoutParams lp) { int kind = lp.isHeader ? HEADER : CONTENT; return(lp.FirstPosition == mSfp && (kind & mTargetKind) != 0); }
public bool SameSectionManager(LayoutManager.LayoutParams lp) { return(lp.sectionManagerKind == sectionManagerKind || TextUtils.Equals(lp.sectionManager, sectionManager)); }